一些java知识

来源:互联网 发布:openwrt怎么导入软件 编辑:程序博客网 时间:2024/05/16 19:19
 一些java知识
应聘Java笔试时可能出现问题及其答案
Java基础方面:

1、作用域public,private,protected,以及不写时的区别
答:区别如下:
作用域 当前类 同一package 子孙类 其他package
public √ √ √ √
protected √ √ √ ×
friendly √ √ × ×
private √ × × ×
不写时默认为friendly

2、ArrayList和Vector的区别,HashMap和Hashtable的区别
答:就ArrayList与Vector主要从二方面来说.
一.同步性:Vector是线程安全的,也就是说是同步的,而ArrayList是线程序不安全的,不是同步的
二.数据增长:当需要增长时,Vector默认增长为原来一培,而ArrayList却是原来的一半
就HashMap与HashTable主要从三方面来说。
一.历史原因:Hashtable是基于陈旧的Dictionary类的,HashMap是Java 1.2引进的Map接口的一个实现
二.同步性:Hashtable是线程安全的,也就是说是同步的,而HashMap是线程序不安全的,不是同步的
三.值:只有HashMap可以让你将空值作为一个表的条目的key或value

3、char型变量中能不能存贮一个中文汉字?为什么?
答:是能够定义成为一个中文的,因为java中以unicode编码,一个char占16个字节,所以放一个中文是没问题的

4、多线程有几种实现方法,都是什么?同步有几种实现方法,都是什么?
答:多线程有两种实现方法,分别是继承Thread类与实现Runnable接口
同步的实现方面有两种,分别是synchronized,wait与notify

5、继承时候类的执行顺序问题,一般都是选择题,问你将会打印出什么?
答:父类:
package test;
public class FatherClass
{
public FatherClass()
{
System.out.println("FatherClass Create");
}
}
子类:
package test;
import test.FatherClass;
public class ChildClass extends FatherClass
{
public ChildClass()
{
System.out.println("ChildClass Create");
}
public static void main(String[] args)
{
FatherClass fc = new FatherClass();
ChildClass cc = new ChildClass();
}
}
输出结果:
C:/>java test.ChildClass
FatherClass Create
FatherClass Create
ChildClass Create

6、内部类的实现方式?
答:示例代码如下:
package test;
public class OuterClass
{
private class InterClass
{
public InterClass()
{
System.out.println("InterClass Create");
}
}
public OuterClass()
{
InterClass ic = new InterClass();
System.out.println("OuterClass Create");
}
public static void main(String[] args)
{
OuterClass oc = new OuterClass();
}
}
输出结果:
C:/>java test/OuterClass
InterClass Create
OuterClass Create
再一个例题:
public class OuterClass {
private double d1 = 1.0;
//insert code here
}
You need to insert an inner class declaration at line 3. Which two inner class declarations are

valid?(Choose two.)
A. class InnerOne{
public static double methoda() {return d1;}
}
B. public class InnerOne{
static double methoda() {return d1;}
}
C. private class InnerOne{
double methoda() {return d1;}
}
D. static class InnerOne{
protected double methoda() {return d1;}
}
E. abstract class InnerOne{
public abstract double methoda();
}
说明如下:
一.静态内部类可以有静态成员,而非静态内部类则不能有静态成员。 故 A、B 错
二.静态内部类的非静态成员可以访问外部类的静态变量,而不可访问外部类的非静态变量;return d1 出错。

故 D 错
三.非静态内部类的非静态成员可以访问外部类的非静态变量。 故 C 正确
四.答案为C、E

7、垃圾回收机制,如何优化程序?
希望大家补上,谢谢

8、float型float f=3.4是否正确?
答:不正确。精度不准确,应该用强制类型转换,如下所示:float f=(float)3.4

9、介绍JAVA中的Collection FrameWork(包括如何写自己的数据结构)?
答:Collection FrameWork如下:
Collection
├List
│├LinkedList
│├ArrayList
│└Vector
│ └Stack
└Set
Map
├Hashtable
├HashMap
└WeakHashMap
Collection是最基本的集合接口,一个Collection代表一组Object,即Collection的元素(Elements)
Map提供key到value的映射

10、Java中异常处理机制,事件机制?

11、JAVA中的多形与继承?
希望大家补上,谢谢

12、抽象类与接口?
答:抽象类与接口都用于抽象,但是抽象类(JAVA中)可以有自己的部分实现,而接口则完全是一个标识(同时有多重继承的功能)。

13、Java 的通信编程,编程题(或问答),用JAVA SOCKET编程,读服务器几个字符,再写入本地显示?
答:Server端程序:
package test;
import java.net.*;
import java.io.*;

public class Server
{
private ServerSocket ss;
private Socket socket;
private BufferedReader in;
private PrintWriter out;
public Server()
{
try
{
ss=new ServerSocket(10000);
while(true)
{
socket = ss.accept();
String RemoteIP = socket.getInetAddress().getHostAddress();
String RemotePort = ":"+socket.getLocalPort();
System.out.println("A client come in!IP:"+RemoteIP+RemotePort);
in = new BufferedReader(new

InputStreamReader(socket.getInputStream()));
String line = in.readLine();
System.out.println("Cleint send is :" + line);
out = new PrintWriter(socket.getOutputStream(),true);
out.println("Your Message Received!");
out.close();
in.close();
socket.close();
}
}catch (IOException e)
{
out.println("wrong");
}
}
public static void main(String[] args)
{
new Server();
}
};
Client端程序:
package test;
import java.io.*;
import java.net.*;

public class Client
{
Socket socket;
BufferedReader in;
PrintWriter out;
public Client()
{
try
{
System.out.println("Try to Connect to 127.0.0.1:10000");
socket = new Socket("127.0.0.1",10000);
System.out.println("The Server Connected!");
System.out.println("Please enter some Character:");
BufferedReader line = new BufferedReader(new

InputStreamReader(System.in));
out = new PrintWriter(socket.getOutputStream(),true);
out.println(line.readLine());
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
System.out.println(in.readLine());
out.close();
in.close();
socket.close();
}catch(IOException e)
{
out.println("Wrong");
}
}
public static void main(String[] args)
{
new Client();
}
};

14、用JAVA实现一种排序,JAVA类实现序列化的方法(二种)? 如在COLLECTION框架中,实现比较要实现什么样的接口?
答:用插入法进行排序代码如下
package test;
import java.util.*;
class InsertSort
{
ArrayList al;
public InsertSort(int num,int mod)
{
al = new ArrayList(num);
Random rand = new Random();
System.out.println("The ArrayList Sort Before:");
for (int i=0;i<num ;i++ )
{
al.add(new Integer(Math.abs(rand.nextInt()) % mod + 1));
System.out.println("al["+i+"]="+al.get(i));
}
}
public void SortIt()
{
Integer tempInt;
int MaxSize=1;
for(int i=1;i<al.size();i++)
{
tempInt = (Integer)al.remove(i);
if(tempInt.intValue()>=((Integer)al.get(MaxSize-1)).intValue())
{
al.add(MaxSize,tempInt);
MaxSize++;
System.out.println(al.toString());
} else {
for (int j=0;j<MaxSize ;j++ )
{
if

(((Integer)al.get(j)).intValue()>=tempInt.intValue())
{
al.add(j,tempInt);
MaxSize++;
System.out.println(al.toString());
break;
}
}
}
}
System.out.println("The ArrayList Sort After:");
for(int i=0;i<al.size();i++)
{
System.out.println("al["+i+"]="+al.get(i));
}
}
public static void main(String[] args)
{
InsertSort is = new InsertSort(10,100);
is.SortIt();
}
}
JAVA类实现序例化的方法是实现java.io.Serializable接口
Collection框架中实现比较要实现Comparable 接口和 Comparator 接口

15、编程:编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串。 但是要保证汉字不被截半个,如“我ABC”4,应该截为“我AB”,输入“我ABC汉DEF”,6,应该输出为“我ABC”而不是“我ABC+汉的半个”。
答:代码如下:
package test;

class SplitString
{
String SplitStr;
int SplitByte;
public SplitString(String str,int bytes)
{
SplitStr=str;
SplitByte=bytes;
System.out.println("The String is:´"+SplitStr+"´;SplitBytes="+SplitByte);
}
public void SplitIt()
{
int loopCount;


loopCount=(SplitStr.length()%SplitByte==0)?(SplitStr.length()/SplitByte):(SplitStr.length()/Split

Byte+1);
System.out.println("Will Split into "+loopCount);
for (int i=1;i<=loopCount ;i++ )
{
if (i==loopCount){


System.out.println(SplitStr.substring((i-1)*SplitByte,SplitStr.length()));
} else {


System.out.println(SplitStr.substring((i-1)*SplitByte,(i*SplitByte)));
}
}
}
public static void main(String[] args)
{
SplitString ss = new SplitString("test中dd文dsaf中男大3443n中国43中国人

0ewldfls=103",4);
ss.SplitIt();
}
}

16、JAVA多线程编程。 用JAVA写一个多线程程序,如写四个线程,二个加1,二个对一个变量减一,输出。
希望大家补上,谢谢

17、STRING与STRINGBUFFER的区别。
答:STRING的长度是不可变的,STRINGBUFFER的长度是可变的。如果你对字符串中的内容经常进行操作,特别是内容要修改时,那么使用StringBuffer,如果最后需要String,那么使用StringBuffer的toString()方法

Jsp方面

1、jsp有哪些内置对象?作用分别是什么?
答:JSP共有以下9种基本内置组件(可与ASP的6种内部组件相对应):
 request 用户端请求,此请求会包含来自GET/POST请求的参数
response 网页传回用户端的回应
pageContext 网页的属性是在这里管理
session 与请求有关的会话期
application servlet 正在执行的内容
out 用来传送回应的输出
config servlet的构架部件
page JSP网页本身
exception 针对错误网页,未捕捉的例外

2、jsp有哪些动作?作用分别是什么?
答:JSP共有以下6种基本动作
jsp:include:在页面被请求的时候引入一个文件。
jsp:useBean:寻找或者实例化一个JavaBean。
jsp:setProperty:设置JavaBean的属性。
jsp:getProperty:输出某个JavaBean的属性。
jsp:forward:把请求转到一个新的页面。
jsp:plugin:根据浏览器类型为Java插件生成OBJECT或EMBED标记

3、JSP中动态INCLUDE与静态INCLUDE的区别?
答:动态INCLUDE用jsp:include动作实现
<jsp:include page="included.jsp" flush="true" />它总是会检查所含文件中的变化,适合用于包含动态页面,并且可以带参数
静态INCLUDE用include伪码实现,定不会检查所含文件的变化,适用于包含静态页面
<%@ include file="included.htm" %>

4、两种跳转方式分别是什么?有什么区别?
答:有两种,分别为:
<jsp:include page="included.jsp" flush="true">
<jsp:forward page= "nextpage.jsp"/>
前者页面不会转向include所指的页面,只是显示该页的结果,主页面还是原来的页面。执行完后还会回来,相当于函数调用。并且可以带参数.后者完全转向新页面,不会再回来。相当于go to 语句。

Servlet方面

1、说一说Servlet的生命周期?
答:servlet有良好的生存期的定义,包括加载和实例化、初始化、处理请求以及服务结束。这个生存期由javax.servlet.Servlet接口的init,service和destroy方法表达。

2、Servlet版本间(忘了问的是哪两个版本了)的不同?
希望大家补上,谢谢

3、JAVA SERVLET API中forward() 与redirect()的区别?
答:前者仅是容器中控制权的转向,在客户端浏览器地址栏中不会显示出转向后的地址;后者则是完全的跳转,浏览器将会得到跳转的地址,并重新发送请求链接。这样,从浏览器的地址栏中可以看到跳转后的链接地址。所以,前者更加高效,在前者可以满足需要时,尽量使用forward()方法,并且,这样也有助于隐藏实际的链接。在有些情况下,比如,需要跳转到一个其它服务器上的资源,则必须使用sendRedirect()方法。

4、Servlet的基本架构
public class ServletName extends HttpServlet {
public void doPost(HttpServletRequest request, HttpServletResponse response) throws
ServletException, IOException {
}
public void doGet(HttpServletRequest request, HttpServletResponse response) throws
ServletException, IOException {
}
}

Jdbc、Jdo方面

1、可能会让你写一段Jdbc连Oracle的程序,并实现数据查询.
答:程序如下:
package hello.ant;
import java.sql.*;
public class jdbc
{
String dbUrl="jdbc:oracle:thin:@127.0.0.1:1521:orcl";
String theUser="admin";
String thePw="manager";
Connection c=null;
Statement conn;
ResultSet rs=null;
public jdbc()
{
try{
Class.forName("oracle.jdbc.driver.OracleDriver").newInstance();
c = DriverManager.getConnection(dbUrl,theUser,thePw);
conn=c.createStatement();
}catch(Exception e){
e.printStackTrace();
}
}
public boolean executeUpdate(String sql)
{
try
{
conn.executeUpdate(sql);
return true;
}
catch (SQLException e)
{
e.printStackTrace();
return false;
}
}
public ResultSet executeQuery(String sql)
{
rs=null;
try
{
rs=conn.executeQuery(sql);
}
catch (SQLException e)
{
e.printStackTrace();
}
return rs;
}
public void close()
{
try
{
conn.close();
c.close();
}
catch (Exception e)
{
e.printStackTrace();
}
}
public static void main(String[] args)
{
ResultSet rs;
jdbc conn = new jdbc();
rs=conn.executeQuery("select * from test");
try{
while (rs.next())
{
System.out.println(rs.getString("id"));
System.out.println(rs.getString("name"));
}
}catch(Exception e)
{
e.printStackTrace();
}
}
}

2、Class.forName的作用?为什么要用?
答:调用该访问返回一个以字符串指定类名的类的对象。

3、Jdo是什么?
答:JDO是Java对象持久化的新的规范,为java data object的简称,也是一个用于存取某种数据仓库中的对象的标准化API。JDO提供了透明的对象存储,因此对开发人员来说,存储数据对象完全不需要额外的代码(如JDBC API的使用)。这些繁琐的例行工作已经转移到JDO产品提供商身上,使开发人员解脱出来,从而集中时间和精力在业务逻辑上。另外,JDO很灵活,因为它可以在任何数据底层上运行。JDBC只是面向关系数据库(RDBMS)JDO更通用,提供到任何数据底层的存储功能,比如关系数据库、文件、XML以及对象数据库(ODBMS)等等,使得应用可移植性更强。

4、在ORACLE大数据量下的分页解决方法。一般用截取ID方法,还有是三层嵌套方法。
答:一种分页方法
<%
int i=1;
int numPages=14;
String pages = request.getParameter("page") ;
int currentPage = 1;
currentPage=(pages==null)?(1):{Integer.parseInt(pages)}
sql = "select count(*) from tables";
ResultSet rs = DBLink.executeQuery(sql) ;
while(rs.next()) i = rs.getInt(1) ;
int intPageCount=1;
intPageCount=(i%numPages==0)?(i/numPages):(i/numPages+1);
int nextPage ;
int upPage;
nextPage = currentPage+1;
if (nextPage>=intPageCount) nextPage=intPageCount;
upPage = currentPage-1;
if (upPage<=1) upPage=1;
rs.close();
sql="select * from tables";
rs=DBLink.executeQuery(sql);
i=0;
while((i<numPages*(currentPage-1))&&rs.next()){i++;}
%>
//输出内容
//输出翻页连接
合计:<%=currentPage%>/<%=intPageCount%><a href="List.jsp?page=1">第一页</a><a

href="List.jsp?page=<%=upPage%>">上一页</a>
<%
for(int j=1;j<=intPageCount;j++){
if(currentPage!=j){
%>
<a href="list.jsp?page=<%=j%>">[<%=j%>]</a>
<%
}else{
out.println(j);
}
}
%>
<a href="List.jsp?page=<%=nextPage%>">下一页</a><a href="List.jsp?page=<%=intPageCount%>">最后页

</a>


Xml方面

1、xml有哪些解析技术?区别是什么?
答:有DOM,SAX,STAX等
DOM:处理大型文件时其性能下降的非常厉害。这个问题是由DOM的树结构所造成的,这种结构占用的内存较多,而且DOM必须在解析文件之前把整个文档装入内存,适合对XML的随机访问SAX:不现于DOM,SAX是事件驱动型的XML解析方式。它顺序读取XML文件,不需要一次全部装载整个文件。当遇到像文件开头,文档结束,或者标签开头与标签结束时,它会触发一个事件,用户通过在其回调事件中写入处理代码来处理XML文件,适合对XML的顺序访问
STAX:Streaming API for XML (StAX)

2、你在项目中用到了xml技术的哪些方面?如何实现的?
答:用到了数据存贮,信息配置两方面。在做数据交换平台时,将不能数据源的数据组装成XML文件,然后将XML文件压缩打包加密后通过网络传送给接收者,接收解密与解压缩后再同XML文件中还原相关信息进行处理。在做软件配置时,利用XML可以很方便的进行,软件的各种配置参数都存贮在XML文件中。

3、用jdom解析xml文件时如何解决中文问题?如何解析?
答:看如下代码,用编码方式加以解决
package test;
import java.io.*;
public class DOMTest
{
private String inFile = "c://people.xml";
private String outFile = "c://people.xml";
public static void main(String args[])
{
new DOMTest();
}
public DOMTest()
{
try
{
javax.xml.parsers.DocumentBuilder builder =


javax.xml.parsers.DocumentBuilderFactory.newInstance().newDocumentBuilder();
org.w3c.dom.Document doc = builder.newDocument();
org.w3c.dom.Element root = doc.createElement("老师");
org.w3c.dom.Element wang = doc.createElement("王");
org.w3c.dom.Element liu = doc.createElement("刘");
wang.appendChild(doc.createTextNode("我是王老师"));
root.appendChild(wang);
doc.appendChild(root);
javax.xml.transform.Transformer transformer =
javax.xml.transform.TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(javax.xml.transform.OutputKeys.ENCODING, "gb2312");
transformer.setOutputProperty(javax.xml.transform.OutputKeys.INDENT, "yes");


transformer.transform(new javax.xml.transform.dom.DOMSource(doc),
new

javax.xml.transform.stream.StreamResult(outFile));
}
catch (Exception e)
{
System.out.println (e.getMessage());
}
}
}

4、编程用JAVA解析XML的方式.
答:用SAX方式解析XML,XML文件如下:
<?xml version="1.0" encoding="gb2312"?>
<person>
<name>王小明</name>
<college>信息学院</college>
<telephone>6258113</telephone>
<notes>男,1955年生,博士,95年调入海南大学</notes>
</person>
事件回调类SAXHandler.java
import java.io.*;
import java.util.Hashtable;
import org.xml.sax.*;
public class SAXHandler extends HandlerBase
{
private Hashtable table = new Hashtable();
private String currentElement = null;
private String currentValue = null;
public void setTable(Hashtable table)
{
this.table = table;
}
public Hashtable getTable()
{
return table;
}
public void startElement(String tag, AttributeList attrs)
throws SAXException
{
currentElement = tag;
}
public void characters(char[] ch, int start, int length)
throws SAXException
{
currentValue = new String(ch, start, length);
}
public void endElement(String name) throws SAXException
{
if (currentElement.equals(name))
table.put(currentElement, currentValue);
}
}
JSP内容显示源码,SaxXml.jsp:
<HTML>
<HEAD>
<TITLE>剖析XML文件people.xml</TITLE>
</HEAD>
<BODY>
<%@ page errorPage="ErrPage.jsp"
contentType="text/html;charset=GB2312" %>
<%@ page import="java.io.*" %>
<%@ page import="java.util.Hashtable" %>
<%@ page import="org.w3c.dom.*" %>
<%@ page import="org.xml.sax.*" %>
<%@ page import="javax.xml.parsers.SAXParserFactory" %>
<%@ page import="javax.xml.parsers.SAXParser" %>
<%@ page import="SAXHandler" %>
<%
File file = new File("c://people.xml");
FileReader reader = new FileReader(file);
Parser parser;
SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser sp = spf.newSAXParser();
SAXHandler handler = new SAXHandler();
sp.parse(new InputSource(reader), handler);
Hashtable hashTable = handler.getTable();
out.println("<TABLE BORDER=2><CAPTION>教师信息表</CAPTION>");
out.println("<TR><TD>姓名</TD>" + "<TD>" +
(String)hashTable.get(new String("name")) + "</TD></TR>");
out.println("<TR><TD>学院</TD>" + "<TD>" +
(String)hashTable.get(new String("college"))+"</TD></TR>");
out.println("<TR><TD>电话</TD>" + "<TD>" +
(String)hashTable.get(new String("telephone")) + "</TD></TR>");
out.println("<TR><TD>备注</TD>" + "<TD>" +
(String)hashTable.get(new String("notes")) + "</TD></TR>");
out.println("</TABLE>");
%>
</BODY>
</HTML>

EJB方面

1、EJB2.0有哪些内容?分别用在什么场合? EJB2.0和EJB1.1的区别?
答:规范内容包括Bean提供者,应用程序装配者,EJB容器,EJB配置工具,EJB服务提供者,系统管理员。这里面,EJB容器是EJB之所以能够运行的核心。EJB容器管理着EJB的创建,撤消,激活,去活,与数据库的连接等等重要的核心工作。JSP,Servlet,EJB,JNDI,JDBC,JMS.....

2、EJB与JAVA BEAN的区别?
答:Java Bean 是可复用的组件,对Java Bean并没有严格的规范,理论上讲,任何一个Java类都可以是一个Bean。但通常情况下,由于Java Bean是被容器所创建(如Tomcat)的,所以Java Bean应具有一个无参的构造器,另外,通常Java Bean还要实现Serializable接口用于实现Bean的持久性。Java Bean实际上相当于微软COM模型中的本地进程内COM组件,它是不能被跨进程访问的。Enterprise Java Bean 相当于DCOM,即分布式组件。它是基于Java的远程方法调用(RMI)技术的,所以EJB可以被远程访问(跨进程、跨计算机)。但EJB必须被布署在诸如Webspere、WebLogic这样的容器中,EJB客户从不直接访问真正的EJB组件,而是通过其容器访问。EJB容器是EJB组件的代理,EJB组件由容器所创建和管理。客户通过容器来访问真正的EJB组件。

3、EJB的基本架构
答:一个EJB包括三个部分:
Remote Interface 接口的代码
package Beans;
import javax.ejb.EJBObject;
import java.rmi.RemoteException;
public interface Add extends EJBObject
{
//some method declare
}
Home Interface 接口的代码
package Beans;
import java.rmi.RemoteException;
import jaax.ejb.CreateException;
import javax.ejb.EJBHome;
public interface AddHome extends EJBHome
{
//some method declare
}
EJB类的代码
package Beans;
import java.rmi.RemoteException;
import javax.ejb.SessionBean;
import javx.ejb.SessionContext;
public class AddBean Implements SessionBean
{
//some method declare
}

J2EE,MVC方面

1、MVC的各个部分都有那些技术来实现?如何实现?
答:MVC是Model-View-Controller的简写。"Model" 代表的是应用的业务逻辑(通过JavaBean,EJB组件实现), "View" 是应用的表示面(由JSP页面产生),"Controller" 是提供应用的处理过程控制(一般是一个Servlet),通过这种设计模型把应用逻辑,处理过程和显示逻辑分成不同的组件实现。这些组件可以进行交互和重用。

2、应用服务器与WEB SERVER的区别?
希望大家补上,谢谢


3、J2EE是什么?
答:Je22是Sun公司提出的多层(multi-diered),分布式(distributed),基于组件(component-base)的企业级应用模型(enterpriese application model).在这样的一个应用系统中,可按照功能划分为不同的组件,这些组件又可在不同计算机上,并且处于相应的层次(tier)中。所属层次包括客户层(clietn tier)组件,web层和组件,Business层和组件,企业信息系统(EIS)层。

4、WEB SERVICE名词解释。JSWDL开发包的介绍。JAXP、JAXM的解释。SOAP、UDDI,WSDL解释。
答:Web Service描述语言WSDL
SOAP即简单对象访问协议(Simple Object Access Protocol),它是用于交换XML编码信息的轻量级协议。
UDDI 的目的是为电子商务建立标准;UDDI是一套基于Web的、分布式的、为Web Service提供的、信息注册中心的实现标准规范,同时也包含一组使企业能将自身提供的Web Service注册,以使别的企业能够发现的访问协议的实现标准。


5、BS与CS的联系与区别。
希望大家补上,谢谢

6、STRUTS的应用(如STRUTS架构)
答:Struts是采用Java Servlet/JavaServer Pages技术,开发Web应用程序的开放源码的framework。 采用Struts能开发出基于MVC(Model-View-Controller)设计模式的应用构架。 Struts有如下的主要功能:
一.包含一个controller servlet,能将用户的请求发送到相应的Action对象。
二.JSP自由tag库,并且在controller servlet中提供关联支持,帮助开发员创建交互式表单应用。
三.提供了一系列实用对象:XML处理、通过Java reflection APIs自动处理JavaBeans属性、国际化的提示和消息。

设计模式方面

1、开发中都用到了那些设计模式?用在什么场合?
答:每个模式都描述了一个在我们的环境中不断出现的问题,然后描述了该问题的解决方案的核心。通过这种方式,你可以无数次地使用那些已有的解决方案,无需在重复相同的工作。主要用到了MVC的设计模式。用来开发JSP/Servlet或者J2EE的相关应用。简单工厂模式等。


2、UML方面
答:标准建模语言UML。用例图,静态图(包括类图、对象图和包图),行为图,交互图(顺序图,合作图),实现图,

JavaScript方面

1、如何校验数字型?
var re=/^/d{1,8}$|/./d{1,2}$/;
var str=document.form1.all(i).value;
var r=str.match(re);
if (r==null)
{
sign=-4;
break;
}
else{
document.form1.all(i).value=parseFloat(str);
}


CORBA方面

1、CORBA是什么?用途是什么?
答:CORBA 标准是公共对象请求代理结构(Common Object Request Broker Architecture),由对象管理组织 (Object Management Group,缩写为 OMG)标准化。它的组成是接口定义语言(IDL), 语言绑定(binding:也译为联编)和允许应用程序间互操作的协议。 其目的为:
用不同的程序设计语言书写
在不同的进程中运行
为不同的操作系统开发


LINUX方面

1、LINUX下线程,GDI类的解释。
答:LINUX实现的就是基于核心轻量级进程的"一对一"线程模型,一个线程实体对应一个核心轻量级进程,而线程之间的管理在核外函数库中实现。
GDI类为图像设备编程接口类库

- 作者: youyou2005 2005年09月14日, 星期三 12:06  回复(0) |  引用(0) 加入博采

Java 编程技术中汉字问题的分析及解决(从根源上解决)
Java 编程技术中汉字问题的分析及解决(从根源上解决)
很难找到的一篇极棒的文章,它从本质上解决了java的汉字编码问题............

在基于 Java 语言的编程中,我们经常碰到汉字的处理及显示的问题。一大堆看不懂的乱码肯定不是我们愿意看到的显示效果,怎样才能够让那些汉字正确显示呢?Java 语言默认的编码方式是UNICODE ,而我们中国人通常使用的文件和数据库都是基于 GB2312 或者 BIG5 等方式编码的,怎样才能够恰当地选择汉字编码方式并正确地处理汉字的编码呢?本文将从汉字编码的常识入手,结合 Java 编程实例,分析以上两个问题并提出解决它们的方案。

现在 Java 编程语言已经广泛应用于互联网世界,早在 Sun 公司开发 Java 语言的时候,就已经考虑到对非英文字符的支持了。Sun 公司公布的 Java 运行环境(JRE)本身就分英文版和国际版,但只有国际版才支持非英文字符。不过在 Java 编程语言的应用中,对中文字符的支持并非如同 Java Soft 的标准规范中所宣称的那样完美,因为中文字符集不只一个,而且不同的操作系统对中文字符的支持也不尽相同,所以会有许多和汉字编码处理有关的问题在我们进行应用开发中困扰着我们。有很多关于这些问题的解答,但都比较琐碎,并不能够满足大家迫切解决问题的愿望,关于 Java 中文问题的系统研究并不多,本文从汉字编码常识出发,分析 Java 中文问题,希望对大家解决这个问题有所帮助。

汉字编码的常识

我们知道,英文字符一般是以一个字节来表示的,最常用的编码方法是 ASCII 。但一个字节最多只能区分256个字符,而汉字成千上万,所以现在都以双字节来表示汉字,为了能够与英文字符分开,每个字节的最高位一定为1,这样双字节最多可以表示64K格字符。我们经常碰到的编码方式有 GB2312、BIG5、UNICODE 等。关于具体编码方式的详细资料,有兴趣的读者可以查阅相关资料。我肤浅谈一下和我们关系密切的 GB2312 和 UNICODE。GB2312 码,中华人民共和国国家标准汉字信息交换用编码,是一个由中华人民共和国国家标准总局发布的关于简化汉字的编码,通行于中国大陆地区及新加坡,简称标码。两个字节中,第一个字节(高字节)的值为区号值加32(20H),第二个字节(低字节)的值为位号值加32(20H),用这两个值来表示一个汉字的编码。UNICODE 码是微软提出的解决多国字符问题的多字节等长编码,它对英文字符采取前面加"0"字节的策略实现等长兼容。如 "A" 的 ASCII 码为0x41,UNICODE 就为0x00,0x41。利用特殊的工具各种编码之间可以互相转换。

Java 中文问题的初步认识

我们基于 Java 编程语言进行应用开发时,不可避免地要处理中文。Java 编程语言默认的编码方式是 UNICODE,而我们通常使用的数据库及文件都是基于 GB2312 编码的,我们经常碰到这样的情况:浏览基于 JSP 技术的网站看到的是乱码,文件打开后看到的也是乱码,被 Java 修改过的数据库的内容在别的场合应用时无法继续正确地提供信息。

String sEnglish = "apple";

String sChinese = "苹果";

String s = "苹果 apple ";

sEnglish 的长度是5,sChinese的长度是4,而 s 默认的长度是14。对于 sEnglish来说, Java 中的各个类都支持得非常好,肯定能够正确显示。但对于 sChinese 和 s 来说,虽然 Java Soft 声明 Java 的基本类已经考虑到对多国字符的支持(默认 UNICODE 编码),但是如果操作系统的默认编码不是 UNICODE ,而是国标码等。从 Java 源代码到得到正确的结果,要经过 "Java 源代码-> Java 字节码-> ;虚拟机->操作系统->显示设备"的过程。在上述过程中的每一步骤,我们都必须正确地处理汉字的编码,才能够使最终的显示结果正确。

" Java 源代码-> Java 字节码",标准的 Java 编译器 javac 使用的字符集是系统默认的字符集,比如在中文 Windows 操作系统上就是 GBK ,而在 Linux 操作系统上就是ISO-8859-1,所以大家会发现在 Linux 操作系统上编译的类中源文件中的中文字符都出了问题,解决的办法就是在编译的时候添加 encoding 参数,这样才能够与平台无关。用法是

javac -encoding GBK。

" Java 字节码->虚拟机->操作系统", Java 运行环境 (JRE) 分英文版和国际版,但只有国际版才支持非英文字符。 Java 开发工具包 (JDK) 肯定支持多国字符,但并非所有的计算机用户都安装了 JDK 。很多操作系统及应用软件为了能够更好的支持 Java ,都内嵌了 JRE 的国际版本,为自己支持多国字符提供了方便。

"操作系统->显示设备",对于汉字来说,操作系统必须支持并能够显示它。英文操作系统如果不搭配特殊的应用软件的话,是肯定不能够显示中文的。

还有一个问题,就是在 Java 编程过程中,对中文字符进行正确的编码转换。例如,向网页输出中文字符串的时候,不论你是用out.println(string);还是用<%=string%>,都必须作 UNICODE 到 GBK 的转换,或者手动,或者自动。在 JSP 1.0中,可以定义输出字符集,从而实现内码的自动转换。用法是

<%@page contentType="text/html;charset=gb2312" %>

但是在一些 JSP 版本中并没有提供对输出字符集的支持,(例如 JSP 0.92),这就需要手动编码输出了,方法非常多。最常用的方法是

String s1 = request.getParameter("keyword");

String s2 = new String(s1.getBytes("ISO-8859-1"),"GBK");

getBytes 方法用于将中文字符以"ISO-8859-1"编码方式转化成字节数组,而"GBK" 是目标编码方式。我们从以ISO-8859-1方式编码的数据库中读出中文字符串 s1 ,经过上述转换过程,在支持 GBK 字符集的操作系统和应用软件中就能够正确显示中文字符串 s2 。

Java 中文问题的表层分析及处理

背景

开发环境

JDK1.15

Vcafe2.0

JPadPro

服务器端

NT IIS

Sybase System

Jconnect(JDBC)

客户端

IE5.0

Pwin98

?span >

.CLASS 文件存放在服务器端,由客户端的浏览器运行 APPLET , APPLET 只起调入FRAME 类等主程序的作用。界面包括 Textfield ,TextArea,List,Choice 等。

I.用 JDBC 执行 SELECT 语句从服务器端读取数据(中文)后,将数据用 APPEND 方法加到 TextArea(TA) ,不能正确显示。但加到 List 中时,大部分汉字却可正确显示。

将数据按"ISO-8859-1" 编码方式转化为字节数组,再按系统缺省编码方式 (Default Character Encoding) 转化为 STRING ,即可在 TA 和 List 中正确显示。

程序段如下:

dbstr2 = results.getString(1);

//After reading the result from DB server,converting it to string.

dbbyte1 = dbstr2.getBytes("iso-8859-1");

dbstr1 = new String(dbbyte1);

在转换字符串时不采用系统默认编码方式,而直接采用" GBK" 或者 "GB2312" ,在 A 和 B 两种情况下,从数据库取数据都没有问题。

II.处理方式与"取中文"相逆,先将 SQL 语句按系统缺省编码方式转化为字节数组,再按"ISO-8859-1"编码方式转化为 STRING ,最后送去执行,则中文信息可正确写入数据库。

程序段如下:

sqlstmt = tf_input.getText();

//Before sending statement to DB server,converting it to sql statement.

dbbyte1 = sqlstmt.getBytes();

sqlstmt = newString(dbbyte1,"iso-8859-1");

_stmt = _con.createStatement();

_stmt.executeUpdate(sqlstmt);

......

问题:如果客户机上存在 CLASSPATH 指向 JDK 的 CLASSES.ZIP 时(称为 A 情况),上述程序代码可正确执行。但是如果客户机只有浏览器,而没有 JDK 和 CLASSPATH 时(称为 B 情况),则汉字无法正确转换。

我们的分析:

1.经过测试,在 A 情况下,程序运行时系统的缺省编码方式为 GBK 或者 GB2312 。在 B 情况下,程序启动时浏览器的 JAVA 控制台中出现如下错误信息:

Can't find resource for sun.awt.windows.awtLocalization_zh_CN

然后系统的缺省编码方式为"8859-1"。

2.如果在转换字符串时不采用系统缺省编码方式,而是直接采用 "GBK" 或"GB2312",则在 A 情况下程序仍然可正常运行,在 B 情况下,系统出现错误:

UnsupportedEncodingException。

3.在客户机上,把 JDK 的 CLASSES.ZIP 解压后,放在另一个目录中, CLASSPATH 只包含该目录。然后一边逐步删除该目录中的 .CLASS 文件,另一边运行测试程序,最后发现在一千多个 CLASS 文件中,只有一个是必不可少的,该文件是:

sun.io.CharToByteDoubleByte.class。

将该文件拷到服务器端和其它的类放在一起,并在程序的开头 IMPORT 它,在 B 情况下程序仍然无法正常运行。

4.在A 情况下,如果在 CLASSPTH 中去掉 sun.io.CharToByteDoubleByte.class ,则程序运行时测得默认编码方式为"8859-1",否则为 "GBK" 或 "GB2312" 。

如果 JDK 的版本为1.2以上的话,在 B 情况下遇到的问题得到了很好的解决,测试的步骤同上,有兴趣的读者可以尝试一下。

Java 中文问题的根源分析及解决

在简体中文 MS Windows 98 + JDK 1.3 下,可以用 System.getProperties() 得到 Java 运行环境的一些基本属性,类 PoorChinese 可以帮助我们得到这些属性。

类 PoorChinese 的源代码:

public class PoorChinese {

}

执行 java PoorChinese 后,我们会得到:

系统变量 file.encoding 的值为 GBK ,user.language 的值为 zh , user.region 的值为 CN ,这些系统变量的值决定了系统默认的编码方式是 GBK 。

在上述系统中,下面的代码将 GB2312 文件转换成 Big5 文件,它们能够帮助我们理解 Java 中汉字编码的转化:

?

import java.io.*;

import java.util.*;

 

public class gb2big5 {

 

static int iCharNum=0;

 

public static void main(String[] args) {

System.out.println("Input GB2312 file, output Big5 file.");

if (args.length!=2) {

System.err.println("Usage: jview gb2big5 gbfile big5file");

System.exit(1);

String inputString = readInput(args[0]);

writeOutput(inputString,args[1]);

System.out.println("Number of Characters in file: "+iCharNum+".");

}

 

static void writeOutput(String str, String strOutFile) {

try {

FileOutputStream fos = new FileOutputStream(strOutFile);

Writer out = new OutputStreamWriter(fos, "Big5");

out.write(str);

out.close();

}

catch (IOException e) {

e.printStackTrace();

e.printStackTrace();

}

}

static String readInput(String strInFile) {

StringBuffer buffer = new StringBuffer();

try {

FileInputStream fis = new FileInputStream(strInFile);

InputStreamReader isr = new InputStreamReader(fis, "GB2312");

Reader in = new BufferedReader(isr);

int ch;

while ((ch = in.read()) > -1) {

iCharNum += 1;

buffer.append((char)ch);

}

in.close();

return buffer.toString();

}

catch (IOException e) {

e.printStackTrace();

return null;

}

}

}

?

编码转化的过程如下:

GB2312------------------>Unicode------------->Big5

执行 java gb2big5 gb.txt big5.txt ,如果 gb.txt 的内容是"今天星期三",则得到的文件 big5.txt 中的字符能够正确显示;而如果 gb.txt 的内容是"情人节快乐",则得到的文件 big5.txt 中对应于"节"和"乐"的字符都是符号"?"(0x3F),可见 sun.io.ByteToCharGB2312 和 sun.io.CharToByteBig5 这两个基本类并没有编好。

正如上例一样, Java 的基本类也可能存在问题。由于国际化的工作并不是在国内完成的,所以在这些基本类发布之前,没有经过严格的测试,所以对中文字符的支持并不像 Java Soft 所声称的那样完美。前不久,我的一位技术上的朋友发信给我说,他终于找到了 Java Servlet 中文问题的根源。两周以来,他一直为 Java Servlet 的中文问题所困扰,因为每面对一个含有中文字符的字符串都必须进行强制转换才能够得到正确的结果(这好象是大家公认的唯一的解决办法)。后来,他确实不想如此继续安分下去了,因为这样的事情确实不应该是高级程序员所要做的工作,他就找出 Servlet 解码的源代码进行分析,因为他怀疑问题就出在解码这部分。经过四个小时的奋斗,他终于找到了问题的根源所在。原来他的怀疑是正确的, Servlet 的解码部分完全没有考虑双字节,直接把 %XX 当作一个字符。(原来 Java Soft 也会犯这幺低级的错误!)

如果你对这个问题有兴趣或者遇到了同样的烦恼的话,你可以按照他的步骤Servlet.jar 进行修改

找到源代码 HttpUtils 中的 static private String parseName ,在返回前将 sb(StringBuffer) 复制成 byte bs[] ,然后 return new String(bs,"GB2312")。作上述修改后就需要自己解码了:

HashTable form=HttpUtils .parseQueryString(request.getQueryString())或者

form=HttpUtils.parsePostData(......)

千万别忘了编译后放到 Servlet.jar 里面。

五、 关于 Java 中文问题的总结

Java 编程语言成长于网络世界,这就要求 Java 对多国字符有很好的支持。 Java 编程语言适应了计算的网络化的需求,为它能够在网络世界迅速成长奠定了坚实的基础。 Java 的缔造者 (Java Soft) 已经考虑到 Java 编程语言对多国字符的支持,只是现在的解决方案有很多缺陷在里面,需要我们付诸一些补偿性的措施。而世界标准化组织也在努力把人类所有的文字统一在一种编码之中,其中一种方案是 ISO10646 ,它用四个字节来表示一个字符。当然,在这种方案未被采用之前,还是希望 Java Soft 能够严格地测试它的产品,为用户带来更多的方便。

- 作者: youyou2005 2005年09月6日, 星期二 23:46  回复(1) |  引用(0) 加入博采

JSTL简化JSP编码

随着J2EE瘦客户机技术JavaServer Pages(JSP)在过去几年中的流行,独立开发人员已经创建了许多自定义的JSP标记库。虽然很多标记库是编写用来实现不同目标的,但它们往往也对迭代、条件及其他通用操作都提供了类似的解决方案。

为了减少对解决类似通用问题的独立标记库的需求,在Java Community Process(JSR 52)的赞助下创建了JSTL(JavaServer Pages Standard Tag Library,JSP)标准标记库,为解决这些通用功能提供一个单一的标准解决方案。

JSTL库

JSTL特别为条件处理、迭代、国际化、数据库访问和可扩展标记语言(XML)处理提供支持。JSTL还引入了expression language(EL,表达式语言),极大地简化了对JSP中应用数据的访问和操作。JSTL包括4个JSP 1.2自定义标记库,每一个都涵盖了一个特定的功能领域。

核心(Core)标记库为日常任务提供通用支持,如显示和设置变量、重复使用一组项目、测试条件以及其他操作(如导入和重定向Web内容)。

XML标记库提供了对XML处理和操作的支持,包括XML节点的解析、迭代、基于XML数据的条件评估以及可扩展样式表语言转换(Extensible Style Language Transformations,XSLT)的执行。

国际化(Internationalization)标记库支持多语种的应用程序。

数据库(Database)标记库对访问和修改数据库数据提供标准化支持。

表1:JSTL的四个标记库
功能领域 URI 前缀 例子 核心(Core) http://java.sun.com/jstl/core c <c:tagname ...> XML http://java.sun.com/jstl/xml x <x:tagname ...> 国际化(Internationalization) http://java.sun.com/jstl/fmt fmt <fmt:tagname ...> 数据库(Database) http://java.sun.com/jstl/sql sql <sql:tagname ...>

 

JSTL入门

初步了解JSTL的最好方法是访问Apache网站--jakarta.apache.org,并下载JSTL的参考实现。在Apache站点还可找到详细的安装指南。可下载的参考实现是一个JAR文件、文档和简单代码示例的组合包。

要在你的J2EE Web应用程序中使用JSTL,只需简单地将"lib"目录下的JSTL JAR文件复制到你应用程序的WEB-INF/lib目录下。要在一个特定的JSP中使用JSTL标记,你还必须提供一个taglib指令。例如,要将"核心"JSTL库导入到你的页面中,你应该在你的JSP顶端包含下面的指令,如下所示:

<%@ taglib uri="http://java.sun.com /jstl/core" prefix="c" %>

 

JSTL的EL支持

JSTL的一个重要优势是它采用了简单的expression language(EL),该语言提供一个访问和操作应用程序数据(如存储在servlet上下文中的数据)的简单方式。

EL的语法很简单,而且比Java中具有相同功能的表示要对用户更为友好。例如, pageContext.getAttribute("aName")表达式在EL中就成了${aName}。所有的JSTL标记在其属性值中都使用EL表达式。EL表达式在访问嵌套属性时使用${Java.expression}或${ data.reference}格式。数据参考可以是对象及其属性或者对象及其属性数组:

${myobject.property}

 

数组存取操作符也用于以索引元素集合显示的数据,如Java数组或java.util.List:

${myList[2]}$

 

在EL表达式中除了可以使用属性和数组元素操作符以及算术、关系和逻辑操作符以外,你还可以使用特别操作符来测试对象是否为空。

除了对象和数组存取,EL还提供了一个完整的常用操作符集合,包括=、!、<、>、<=、>=、+、-、*、/等。

在任何JSP范围(页面、请求、会话或应用程序)中的对象都可以在EL表达式中引用。例如,如果你有一个带有一个属性"Ename"的Java bean--Employee,那么可以用EL表达式${Employee.Ename}访问这个变量。

除了显式变量,EL还提供了对隐式变量的请求和应答对象中的隐式变量的直接访问。例如,以下语句将访问一个名为"empname"的请求参数:

${param.empname}

 

即将推出的JSP 2.0和JSTL 1.0都使用EL。然而,JSP 2.0中使用的EL稍有一点不同。JSTL专家组(JSR-052)已经同意在即将推出的JSTL维护版中使用EL的JSP 2.0版本。

使用JSTL核心标记库

JSTL核心标记库为诸如显示、迭代和设置变量等操作提供了最常用的标记。下面,我们更详细地介绍一些最常用的JSTL核心标记库。首先,在使用任何JSTL核心标记之前,你需要将以下指令添加到你的JSP中:

<%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %>

 

你最常使用的JSTL操作之一是显示动态值。为了显示动态数据,核心库提供了c:out标记。c:out标记在一个页面中显示一个EL表达式的值。例如:

First name: <c:out value="${Employee.Ename}" />

 

c:out的值属性还可以包含文本和表达式的组合:

<c:out value="First name: ${Employee.Ename}" />

 

(注意:当JSP 2.0提供对EL的支持时,你无需再使用c:out操作,你可以直接在页面中嵌入JSP表达式。)

另一个操作是设置变量。为了在一个页面中设置变量,核心标记库提供了c:set标记。这个例子显示了将变量Ename设置为参数"enameparm"的值:

<c:set var="Ename" value="${param.
enameparm}" />

 

JSTL核心标记库还提供了用于处理条件的标记。c:if处理简单的条件测试。计算test属性中布尔表达式的值;如果是真,计算主体中的内容。在下面的操作中,你还可以看到存储测试结果以备以后在页面(或者在别的地方,如果指定了其他的可选范围属性)中使用的可选的var属性:

<c:if test="${Employee.salary <= 10000}" >
It's time for a raise <c:out value="${Employee.name">! </c:if>

 

下面,你可以看到JSTL通过c:choose、c:when和c:otherwise对跳转逻辑的支持。你可以在一个选择(choose)标记中包含一组c:when操作;如果对c:when块中的表达式求值为真,则不对下面的c:choose操作中的测试进行计算。如果对c:when块中测试求值没有一个为真,则计算c:otherwise操作(如果存在的话)的内容:

<c:choose>
<c:when test="${dept.name ==
'development'}">
...
</c:when>
<c:when test="${dept.name ==
'marketing'}">
...
</c:when>
<c:otherwise>
...
</c:otherwise>
</c:choose>

 

c:forEach标记提供了一个对元素集合进行迭代的简单方法。如果你只想迭代集合中的部分元素,你可以分别指定开始和结束索引以及带有可选的开始、结束与步进属性的增量值。在下例中,我们对变量empNames中的一个集合的内容进行迭代;在每个循环中,下一个元素被放置在变量名中,并在c:forEach操作的主体中进行求值。

 
<table>
<c:forEach var="name" items="${empNames}">
<tr><td><c:out value="${name}"/></td></tr>
</c:forEach>
</table>

 

JSTL核心标记库还可以简化异常处理。以前,你必须将Java try/catch语句放置在Java scriptlet中,或者在错误页面中提供它们。JSTL通过c:catch标记提供了一个处理异常的高明方法,而无需使用scriptlet。

<c:catch>
<!—. . . some set of nested JSTL tags that fire an exception->

</c:catch>

 

可以在jakarta.apache.org的参考实现中提供的JSTL文档中找到其他JSTL标记库如XML、国际化和数据库标记库的例子。

- 作者: youyou2005 2005年09月6日, 星期二 23:43  回复(0) |  引用(0) 加入博采

阅读贪吃蛇源码

游戏wormgame是wtk里面自带的demo程序,虽然游戏很简单也很老,就是一般很常见的贪吃蛇游戏。但是既然作为demo程序,那么里面就有很多东西值得我们去思考,去学习。
    首先看看这个游戏所采用的数据结构。如果没有看过贪吃蛇的源码,让我们自己选择数据结构,那我们如何选择一个在空间和性能上都很好的数据结构呢。可能很多人首先想到的是链表,符合游戏中的蛇长度的动态增加和减少。但是我们讨论的是用JAVA写的,如果采用链表的话自己需要去封装很多方法。有没有JAVA本来已经封装好的数据结构可以用呢?对,Vector!这是JAVA的一个非常常用而且重要的数据结构。
    如果就让我们用Vector来做这个游戏,那Vector里面我们放什么呢?很多人会自然的想到,以组成贪吃蛇的每一个小方格子(在Demo中用的是Cell,英文不好,不知道该用什么词)为对象,然后放到Vector里面去,这样就可以实现贪吃蛇长度的增加和减少,因为增加对应addElement(),减少对应removeElement()。如果这样做的话,那么每一个对象该定义什么数据成员以及封装哪些方法呢?这样做是不是浪费空间了呢?因为当贪吃蛇的长度增长以后,处于中间的小方格子似乎没有起到什么作用。那还有没有更好的方法呢?
    让我们现在来看看这个demo是怎么做的吧!
    关于贪吃蛇的数据结构方面的类有两个Worm和WormLink,在WormLink的开头作者就写了一段注释:
     * WormLink represents one sub-section of a worm. Because the
     * worm will usually contain a few straight segments, this is
     * a relatively cost effective way to store the entire worm.
    所以可以从这里就可以知道作者并不是将每个方格作为对象存到Vector里面的,而是将贪吃蛇的一部份存进去的,其实真正存进去的只是一个带着这个贪吃蛇的很多信息的对象。
    WormLink的数据成员为 :
     private int x, y;
     private int len;
     private byte dir;  
    其中封装的方法有(以下只举例我认为比较重要的几个方法):
     public void increaseLength() {
     public void decreaseLength()
     public int getEndX()
     public int getEndX()
     public boolean contains(int x, int y)
    increaseLength()是增加长度,被封装到这个类里面,因为每个对象都携带了从它开始的长度信息,同理decreaseLength()也是的,至于它们的具体实现,将在下文讲到画图的时候再具体讲。
    getEndX(),getEndX()的作用是获得贪吃蛇尾部的坐标,从这个方法就可以看出作者的高明之处,并不需要将每个方格子都存到Vector中去。比如初始状态时贪吃蛇的长度为10的话,就不需要向Vector里面存入10个对象,只需要将一个代表的对象存进去就可以了,然后通过getEndX(),getEndX()来获取尾部的信息。这样做既节约空间又方便了以后的处理,特别是画图的处理。
    contains是碰撞检测简单的函数,在这个游戏里面的碰撞是比较简单的。
    下面看类Worm,我们主要看几个重要的函数,同时从这几个函数的处理上面可以看出作者在设计这个游戏时的高明之处。
  /**
   * Regenerate the worm in its initial position. This is used
   * to restart the game after the worm is killed.
   */
  public void regenerate() {
    synchronized (worm) {
      worm.removeAllElements();
      worm.addElement(new WormLink(INIT_X, INIT_Y,
                                   INIT_LEN, INIT_DIR));

      // Reset class variables
      currentDirection = 0;
      needUpdate = false;
      hasEaten = false;
      moveOnNextUpdate = false;
    }
  }
    这是一个初始化的函数,当游戏开始或者从来一次时被调用的函数,这个函数的作用很明显,在此就不作过多的解释了。
    下面结合贪吃蛇最简单的移动,从左到右横向移动来说明函数update,paint。
    我们先看paint:
  public void paint(Graphics g) {
    WormLink sl;
    int x1, x2, y1, y2;
    int len;

    for (int i = 0; i < worm.size(); i++) {
      sl = (WormLink) worm.elementAt(i);
      x1 = sl.getX(); x2 = sl.getEndX();
      y1 = sl.getY(); y2 = sl.getEndY();
      len = sl.getLength();
      drawLink(g, x1, y1, x2, y2, len);
    }
  } 
   其中函数drawLink的主要作用就是从x1到x2或者y1到y2画len个小方格子,至于其具体实现就不再列出了。
   从这个paint函数可以确定我们刚才的推断是正确的,就是整条贪吃蛇是一段一段被存放到vector里面去的,画图的时候又一段一段的画出来的,vector对象worm有多少个元素就存在着多少段。
   下面在来看update函数,其实update也是一个画图函数,但是奇怪的是,为什么这个游戏不象别的游戏一样,将所有的画图都放在paint里面呢?这里单独定义一个画图函数的作用又是什么呢?其实我们从这个函数的函数名就可以看出答案了,这个函数的作用是在于更新图像,但是又不是更新整个贪吃蛇,而是根据头和尾部,来部分更新,这样就没有别要在画图的时候,每次都重画整个屏幕,只需要更新贪吃蛇的部分就可以了,这也是一种节省系统开销的方法。虽然对于这个游戏本身来说性能不会有太明显的区别,但是对于一些复杂的游戏,画面丰富的游戏来说,这样做的效果就非常明显了。在后面的Canvas类WormPit里面调用这两个函数的时候,我们可以更加清楚地看到这个作用。
   下面讲解update函数的具体实现:
  
  public void update(Graphics g) throws WormException {
    WormLink head, sl;
    int headX, headY;

    if (!moveOnNextUpdate) {
      return;
    }

    synchronized (worm) {

      head = (WormLink) worm.lastElement(); // the worm 'head'
      head.increaseLength();
      if (!hasEaten) {
        WormLink tail;
        tail = (WormLink) worm.firstElement(); // the worm 'tail'
        int tailX = tail.getX();
        int tailY = tail.getY();
        tail.decreaseLength();
        if (tail.getLength() == 0) {
          worm.removeElement(tail);
        }
        // Clear last block of the tail
        g.setColor(WormPit.ERASE_COLOUR);
        drawLink(g, tailX, tailY, tailX, tailY, 1);

      }
      else {
        hasEaten = false;
      }

      needUpdate = false;

      // Make sure we're still in bounds
      if (!WormPit.isInBounds(head.getEndX(), head.getEndY())) {
        throw new WormException("over the edge"); // You're dead Jim
      }

      headX = (byte) head.getEndX();
      headY = (byte) head.getEndY();

      // Draw the head
      g.setColor(WormPit.DRAW_COLOUR);
      drawLink(g, headX, headY, headX, headY, 1);

      // See if we ate ourself
      for (int i = 0; i < worm.size() - 1; i++) {
        sl = (WormLink) worm.elementAt(i);
        if (sl.contains(headX, headY)) {
          throw new WormException("you ate yourself");
        }
      }
    }
  }
 
     首先moveOnNextUpdate是用于判断是否将要移动,如果不移动就退出此函数。
     我们结合刚才所说的例子,假设此时贪吃蛇是从左到右移动的,其实这个时候worm里面只有一个对象,tail和head是同一个对象。
     head.increaseLength(),首先将整个蛇的长度加1 ,hasEaten的作用我们暂时不管。然后到tail.decreaseLength(),作用是将长度缩短1,这里我们再回头去看decreaseLength()的实现部分:
    
  public void decreaseLength() {
 len--;
 switch (dir) {
 case Worm.LEFT:
     x--;
     break;
 case Worm.RIGHT:
     x++;
     break;
 case Worm.UP:
     y--;
     break;
 case Worm.DOWN:
     y++;
     break;
 }
    }
         
    从中我们可以看出,这个函数的作用不仅是将长度减1,同时还包括了坐标的移动。对于我们举的这个例子来说,就是tail的x坐标加1。
    然后回到函数update:
    if (tail.getLength() == 0) {
       worm.removeElement(tail);
    }
    // Clear last block of the tail
    g.setColor(WormPit.ERASE_COLOUR);
    drawLink(g, tailX, tailY, tailX, tailY, 1);
   
    判断语句的作用在于,如果当前尾部所带的长度已经为0了,就要把这个对象从worm中删除。然后就是这个update函数的主要部分了,清除原来的尾部所在的位置,其实就是将原来的小方格画成根背景一样的颜色。
    isInBounds我们先不管,下面就是该画头了
      g.setColor(WormPit.DRAW_COLOUR);
      drawLink(g, headX, headY, headX, headY, 1);
    其实我们有几个细节需要注意,变量tailX, tailY的赋值是在tail做descreaseLength之前,所以它们记录的是尾部前一个状态下面的位置,然后才将其在画布上面清除。还有一点,在画头的时候,headX, headY的值是head.getEndX()和head.getEndY(),这样我们就知道了,其实存到vector对象worm里面的是贪吃蛇每一段的尾部,对于这个例子来说,就是存放的贪吃蛇最后的那个小方格,当然还有它封装的一些数据信息。而这个时候headX, headY已经是贪吃蛇更新以后头所在的位置,对此例,头的位置已经向前移动了一个单位。
    所以对这个例子来说,贪吃蛇的移动过程就是先将长度加1,然后将长度减1并将尾部的x坐标加1,即右移一个单位,然后将原来位置上面的小方格清除掉,再将头的位置右移一个单位并在此位置画头。这样就实现了贪吃蛇的右移了。其实整个过程就只重画了两个方格,尾部和头部。
    刚才我们所举的例子跟方向是没有关系的,现在我们来看改变方向的函数。
    public void setDirection(byte direction) {
    synchronized (worm) {
      if ( (direction != currentDirection) && !needUpdate) {
        WormLink sl = (WormLink) worm.lastElement();
        int x = sl.getEndX();
        int y = sl.getEndY();
        switch (direction) {
          case UP:
            if (currentDirection != DOWN) {
              y--;
              needUpdate = true;
            }
            break;
          case DOWN:
            if (currentDirection != UP) {
              y++;
              needUpdate = true;
            }
            break;
          case LEFT:
            if (currentDirection != RIGHT) {
              x--;
              needUpdate = true;
            }
            break;
          case RIGHT:
            if (currentDirection != LEFT) {
              x++;
              needUpdate = true;
            }
            break;
        }
        if (needUpdate == true) {
          worm.addElement(new WormLink(x, y, 0, direction));
          currentDirection = direction;
        }
      }
    }
  }
 
    我们主要看这个函数的最后,如果needUpdate == true我们就要向worm里面添加一个对象,即贪吃蛇的头改变了方向。例如开始蛇从左向右移动,然后再改变方向向上移动,那么就要像worm里面添加一个新的对象,这个时候的贪吃蛇一共有两段,一段是原来的向右移动的水平方向上的一段,还有一段就是刚刚生成的向上移动的一段。如果这个时候贪吃蛇一直向上移动的话,头这一段的长度不断增加,而尾部这段的长度不断减少,当长度减少为0的时候,就将这个尾部的对象从worm里面删除,这样刚新增加的对象就变成了新的尾部了。如果这样不断改变方向下去,整个worm就是这样的添加元素和删除元素的过程。这里我们再回头去看为什么刚开始定义worm的时候用Vector(5,2)了。其实worm里面元素的个数是跟贪吃蛇有多少个弯是一样的,这样开始定义为5个,因为在开始阶段整个贪吃蛇的长度还比较短的时候生成5个新对象的几率是比较小的,所以开始才定义大小为5。
   
    下面我们看canvas类wormPit,首先看paint函数:
        public void paint(Graphics g) {
 if (forceRedraw) {
     // Redraw the entire screen
     forceRedraw = false;
           
     // Clear background
     g.setColor(WormPit.ERASE_COLOUR);
     g.fillRect(0, 0, getWidth(),
        getHeight());

     // Draw pit border
     g.setColor(WormPit.DRAW_COLOUR);
     g.drawRect(1, 1, (width - START_POS), (height - START_POS));

     // Display current level
     g.drawString("L: " + level, START_POS, height, g.TOP|g.LEFT);
     g.drawString("" + score,
    (width - (SCORE_CHAR_WIDTH * 3)),
    height, g.TOP|g.LEFT);

     // Display current score
     g.drawString("S: ",
    (width - (SCORE_CHAR_WIDTH * 4)),
    height, g.TOP|g.RIGHT);
     g.drawString("" + score,
    (width - (SCORE_CHAR_WIDTH * 3)),
    height, g.TOP|g.LEFT);

     // Display highest score for this level
     g.drawString("H: ",
    (width - (SCORE_CHAR_WIDTH * 4)),
    (height + SCORE_CHAR_HEIGHT),
    g.TOP|g.RIGHT);
     g.drawString("" + WormScore.getHighScore(level),
    (width - (SCORE_CHAR_WIDTH * 3)),
    (height + SCORE_CHAR_HEIGHT),
    g.TOP|g.LEFT);

     // Draw worm & food
     g.translate(START_POS, START_POS);
     g.setClip(0, 0, CellWidth*CELL_SIZE, CellHeight*CELL_SIZE);
     myWorm.paint(g);
     myFood.paint(g);
 } else {
     // Draw worm & food
     g.translate(START_POS, START_POS);
 }

 if (gamePaused) {
     Font pauseFont = g.getFont();
     int fontH = pauseFont.getHeight();
     int fontW = pauseFont.stringWidth("Paused");
     g.setColor(WormPit.ERASE_COLOUR);
     g.fillRect((width-fontW)/2 - 1, (height-fontH)/2,
         fontW + 2, fontH);
     g.setColor(WormPit.TEXT_COLOUR);
     g.setFont(pauseFont);
     g.drawString("Paused", (width-fontW)/2, (height-fontH)/2,
    g.TOP|g.LEFT);
 } else if (gameOver) {
     Font overFont = g.getFont();
     int fontH = overFont.getHeight();
     int fontW = overFont.stringWidth("Game Over");
     g.setColor(WormPit.ERASE_COLOUR);
     g.fillRect((width-fontW)/2 - 1, (height-fontH)/2,
         fontW + 2, fontH);
     g.setColor(WormPit.TEXT_COLOUR);
     g.setFont(overFont);
     g.drawString("Game Over", (width-fontW)/2, (height-fontH)/2,
    g.TOP|g.LEFT);
 } else {
     paintPitContents(g);
 }
 g.translate(-START_POS, -START_POS);
    }
   
    整个贪吃蛇的活动区域是在黑色边框内,所以通过g.translate(START_POS, START_POS)将坐标原点移动到(START_POS, START_POS),这个坐标系都作了坐标变换了。
    在paint函数的开始通过forceRedraw来判断是否对整个画布进行重画,如果是,(这里这样画分数,level这些东西我们就不具体讲了,直接跳到画myWorm.paint(g))就调用worm的paint函数,对整个贪吃蛇进行画图。然后再这个paint函数里面调用paintPitContents(g),这个函数是这个wormPit类的成员函数,也是整个游戏的主要逻辑部分所在。
    private void paintPitContents(Graphics g) {
 try {
     myWorm.update(g);    // update worm position
     if (myFood.isAt(myWorm.getX(), myWorm.getY())) {
  myWorm.eat();
  score += level;

  foodEaten++;

  if (foodEaten > (level << 1)) {
      /* Increase difficulty level */
      forceRedraw = true;
      foodEaten = 0;
      level++;

      if (tonePlayer != null) {
   try {
       tonePlayer.setMediaTime(0);
       tonePlayer.start();
   } catch (MediaException me) { }
      }
  } else {
      if (audioPlayer != null) {
   try {
       Manager.playTone(69, 50, 100);   // Play audio
   } catch (MediaException me) { }
      }
  }

  g.setColor(WormPit.ERASE_COLOUR);
  g.fillRect((width - (SCORE_CHAR_WIDTH * 3))-START_POS,
      height-START_POS,
      (SCORE_CHAR_WIDTH * 3),
      SCORE_CHAR_HEIGHT);
  g.setColor(WormPit.DRAW_COLOUR);

  // Display new score
  g.drawString("" + score,
        width - (SCORE_CHAR_WIDTH * 3) - START_POS,
        height - START_POS, g.TOP|g.LEFT);

  myFood.regenerate();
  int x = myFood.getX();
  int y = myFood.getY();
  while (myWorm.contains(x, y)) {
  // generate again if food placed under worm..
      myFood.regenerate();
      x = myFood.getX();  y = myFood.getY();
  }
     }

     myFood.paint(g);
 } catch (WormException se) {
     gameOver = true;
 }
    }
   
    从这个函数的开始我们就可以看到,worm的update函数就是在此被调用的。至此,整个贪吃蛇的数据结构以及如何画图就完成,至于如何处理food这个对象,以及如何处理分数,暂停等,在这里就不讲了。

- 作者: youyou2005 2005年08月17日, 星期三 14:14  回复(0) |  引用(0) 加入博采

jsp文件操作

文件的建立/检查与删除
<%@ page contentType="text/html;charset=gb2312"%>
<%@ page import="java.io.*"%>
<html>
<head>
<title>文件的建立、检查与删除</title>
</head>
<body>
<%
String path=request.getRealPath("");
//out.println(path);
File f=new File(path,"File.txt");
//out.println(f);
//out.println(f.exists());

if(f.exists()){//检查File.txt是否存在
f.delete();//删除File.txt文件
out.println(path + "//File.txt 存在,已删除。");
}else{
f.createNewFile();//在当前目录下建立一个名为File.txt的文件
out.println(path + "//File.txt 不存在,已建立。");//输出目前所在的目录路径
}
%>

目录的建立/检查与删除
<%@ page contentType="text/html;charset=gb2312"%>
<%@ page import="java.io.*"%>
<html>
<head>
<title>目录的建立/检查与删除</title>
</head>
<body>
<%
String path=request.getRealPath("");
path=path + "//Sub";//将要建立的目录路径
File d=new File(path);//建立代表Sub目录的File对象,并得到它的一个引用
if(d.exists()){//检查Sub目录是否存在
d.delete();
out.println("Sub目录存在,已删除");
}else{
d.mkdir();//建立Sub目录
out.println("Sub目录不存在,已建立");
}
%>
</body>
</html>


如何在JSP中处理虚拟目录
<%@ page contentType="text/html;charset=gb2312"%>
<%@ page import="java.io.*"%>
<html>
<head>
<title>JSP中如何处理虚拟目录</title>
</head>
<body>
取得虚拟目录对应的磁盘路径<br>
Web站点主目录的位置为<font color=#ff0000><%=request.getRealPath("/")%></font><br>
JSP网页所在的目录位置<font color=#ff0000><%=request.getRealPath("./")%></font><br>
JSP网页所在目录上一层目录的位置<font color=#ff0000><%=request.getRealPath("../")%></font><br>
</body>
</html>


文件属性的取得
<%@ page contentType="text/html;charset=gb2312"%>
<%@ page import="java.util.Date,java.io.*"%>
<html>
<head>
<title>文件属性的取得</title>
</head>
<body>
<%
String path=request.getRealPath("/");
File f=new File(path,"ReadData.txt");
if(f.exists()){
%>
<%=f.getName()%>的属性如下:<br><br>
文件长度为:<%=f.length()%>
<%=f.isFile()?"是文件":"不是文件"%><br>
<%=f.isDirectory()?"是目录":"不是目录"%><br>
<%=f.canRead()?"可读取":"不可读取"%><br>
<%=f.canWrite()?"可写入":"不可写入"%><br>
<%=f.isHidden()?"是隐藏文件":"不是隐藏文件"%><br>
文件的最后修改日期为:<%=new Date(f.lastModified())%><br>
<%
}else{
f.createNewFile();//在当前目录下建立一个名为ReaData.txt的文件
%>
<%=f.getName()%>的属性如下:<br><br>
文件长度为:<%=f.length()%>
<%=f.isFile()?"是文件":"不是文件"%><br>
<%=f.isDirectory()?"是目录":"不是目录"%><br>
<%=f.canRead()?"可读取":"不可读取"%><br>
<%=f.canWrite()?"可写入":"不可写入"%><br>
<%=f.isHidden()?"是隐藏文件":"不是隐藏文件"%><br>
文件的最后修改日期为:<%=new Date(f.lastModified())%><br>
<%
}
%>
</body>
</html>


取出目录中文件的方法
<%@ page contentType="text/html;charset=gb2312"%>
<%@ page import="java.io.*"%>
<html>
<head>
<title>取出目录中文件的方法--列出目录中的文件</title>
</head>
<body>
<%
String path=request.getRealPath("/");
File d=new File(path);//建立当前目录中文件的File对象
File list[]=d.listFiles();//取得代表目录中所有文件的File对象数组
out.println("<font color=#ff0000>" + path + "目录下的文件:</font><br>");
for(int i=0;i<list.length;i++){
if(list<I>.isFile()){
out.println(list<I>.getName() + "<br>");
}
}
out.println("<br><font color=#ff0000>" + path + "目录下的目录:</font><br>");
for(int i=0;i<list.length;i++){
if(list<I>.isDirectory()){
out.println(list<I>.getName() + "<br>");
}
}
%>
</body>
</html>


判断是否为空白文件
<%@ page contentType="text/html;charset=gb2312"%>
<%@ page import="java.io.*"%>
<html>
<head>
<title>判断是否为空白文件</title>
</head>
<body>
<%
String path=request.getRealPath("/");
out.println(path);
FileReader fr=new FileReader(path + "//AtEnd.txt");//建立FileReader对象,并实例化为fr
//对FileReader类生成的对象使用read()方法,可以从字符流中读取下一个字符。
if(fr.read()==-1)//判断是否已读到文件的结尾
{
out.print("AtEnd.txt文件中没有数据<br>");
}else{
out.println("AtEnd.txt文件中有数据");
}
fr.close();
%>
</body>
</html>


读取所有的文件数据
<%@ page contentType="text/html;charset=gb2312"%>
<%@ page import="java.io.*,java.lang.*"%>
<html>
<head>
<title>读取所有的文件数据</title>
</head>
<body>
<%
String path=request.getRealPath(".");
FileReader fr=new FileReader(path + "//ReadData.txt");
//关键在于读取过程中,要判断所读取的字符是否已经到了文件的末尾,并且这个字符是不是文件中的断行符,即判断该字符值是否为13。
int c=fr.read();//从文件中读取一个字符
//判断是否已读到文件结尾
while(c!=-1){
out.print((char)c);//输出读到的数据
c=fr.read();//从文件中继续读取数据
if(c==13){//判断是否为断行字符
out.print("<br>");//输出分行标签
fr.skip(1);//略过一个字符
//c=fr.read();//读取一个字符
}
}
fr.close();
%>
</body>
</html>


一行一行读取数据
<%@ page contentType="text/html;charset=gb2312"%>
<%@ page import="java.io.*"%>
<html>
<head>
<title>文件读取</title>
</head>
<body>
<%
String path=request.getRealPath("");//取得当前目录的路径
FileReader fr=new FileReader(path + "//file//inc//t.txt");//建立FileReader对象,并实例化为fr
BufferedReader br=new BufferedReader(fr);//建立BufferedReader对象,并实例化为br
String Line=br.readLine();//从文件读取一行字符串
//判断读取到的字符串是否不为空
while(Line!=null){
out.println(Line + "<br>");//输出从文件中读取的数据
Line=br.readLine();//从文件中继续读取一行数据
}
br.close();//关闭BufferedReader对象
fr.close();//关闭文件
%>
</body>
</html>


略过文件中的字符不读取
<%@ page contentType="text/html;charset=gb2312"%>
<%@ page import="java.io.*"%>
<html>
<head>
<title>略过字节不读取</title>
</head>
<body>
<%
String path=request.getRealPath(".");
FileReader fr=new FileReader(path + "//ReadData.txt");
fr.skip(2);//跳过2个字节
int c=fr.read();//读取一个字节
while(c!=-1){
out.print((char)c);
c=fr.read();
}
fr.close();
%>
</body>
</html>


将数据写入文件
<%@ page contentType="text/html;charset=gb2312"%>
<%@ page import="java.io.*"%>
<html>
<head>
<title>将数据写入文件</title>
</head>
<body>
<%
String path=request.getRealPath(".");
FileWriter fw=new FileWriter(path + "//WriteData.txt");//建立FileWriter对象,并实例化fw
//将字符串写入文件
fw.write("大家好!");
fw.write("本书是《JSP编程技巧》");
fw.write("请多多指教!");
fw.write("email:stride@sina.com");
fw.close();

FileReader fr=new FileReader(path + "//WriteData.txt");
BufferedReader br=new BufferedReader(fr);//建立BufferedReader对象,并实例化为br
String Line=br.readLine();
//读取一行数据
out.println(Line + "<br>");
br.close();//关闭BufferedReader对象
fr.close();
%>
</body>
</html>


将写入文件的数据分行
<%@ page contentType="text/html;charset=gb2312"%>
<%@ page import="java.io.*"%>
<html>
<head>
<title>将写入文件的数据分行</title>
</head>
<body>
<%
String path=request.getRealPath(".");
FileWriter fw=new FileWriter(path + "//WriteData.txt");
BufferedWriter bw=new BufferedWriter(fw);
bw.write("大家好!");
bw.write("本书是《JSP编程技巧》。");
bw.newLine();//断行
bw.write("请多多指教!");
bw.newLine();//断行
bw.write("email: stride@sina.com");
bw.flush();//将数据更新至文件
fw.close();//关闭文件流
out.println("写入文件内容为:<br>");
FileReader fr=new FileReader(path + "//WriteData.txt");
BufferedReader br=new BufferedReader(fr);
String Line=br.readLine();//读取一行数据
while(Line!=null){
out.println(Line + "<br>");
Line=br.readLine();
}
fr.close();
%>
</body>
</html>
如何将数据追加写入到文件
<%@ page contentType="text/html;charset=gb2312"%>
<%@ page import="java.io.*"%>
<html>
<head>
<title>将写入文件的数据分行</title>
</head>
<body>
<%
String path=request.getRealPath(".");
RandomAccessFile rf=new RandomAccessFile(path + "//WriteData.txt","rw");//定义一个类RandomAccessFile的对象,并实例化
rf.seek(rf.length());//将指针移动到文件末尾
rf.writeBytes("/nAppend a line to the file!");
rf.close();//关闭文件流
out.println("写入文件内容为:<br>");
FileReader fr=new FileReader(path + "//WriteData.txt");
BufferedReader br=new BufferedReader(fr);//读取文件的BufferedRead对象
String Line=br.readLine();
while(Line!=null){
out.println(Line + "<br>");
Line=br.readLine();
}
fr.close();//关闭文件
%>
</body>
</html></I></I></I></I>

- 作者: youyou2005 2005年08月12日, 星期五 16:37  回复(0) |  引用(0) 加入博采

java 学习步骤
J2EE又包括许多组件,如JSP,Servlet,JavaBean,EJB,JDBC,JavaMail等。要学习起来可不是一两天的事。

那么又该如何学习J2EE呢?当然Java语法得先看一看的,I/O包,Util包,Lang包你都熟悉了吗?然后再从JSP学起。如果你学过HTML,那么事情要好办的多,如果没有,那你快去补一补HTML基础吧。其实JSP中的Java语法也不多,它更象一个脚本语言,有点象ASP。然后你就该学一学Servlet了。Servlet就是服务器端小程序,他负责生成发送给客户端的HTML文件。JSP在执行时,也是先转换成Servlet再运行的。虽说JSP理论上可以完全取代Servlet,这也是SUN推出JSP的本意,可是Servlet用来控制流程跳转还是挺方便的,也令程序更清晰。接下来你应该学习一下Javabean了,可能你早就看不管JSP在HTML中嵌Java代码的混乱方式了,这种方式跟ASP又有什么区别呢?还好,SUN提供了Javabean可以把你的JSP中的Java代码封装起来,便于调用也便于重用。接着就是EJB了,EJB就是Enterprise JavaBean,看名字好象它是Javabean,可是它和Javabean还是有区别的。它是一个体系结构,你可以搭建更安全、更稳定的企业应用。它的大量代码已由中间件(也就是我们常听到的Weblogic,Websphere这些J2EE服务器)完成了,所以我们要做的程序代码量很少,大部分工作都在设计和配置中间件上。至于JDBC,就不用我多说了,你如果用java编过存取数据库的程序,就应该很熟悉。还有,如果你要用Java编发送电子邮件的程序,你就得看看JavaMail了。

好了,对Java和J2EE有了一些基本概念之后,你就应该编一些程序了,千万不要纸上谈兵哦。最好找一些有实例且带光盘的书来看,这样看到好的程序就可以直接Ctrl+C再Ctrl+V,也不用劳您老大架再亲自把它 再输一遍吧,再说直接复制还不用怕出错,何乐而不为呢!还有就是要经常上一些好的Java编程文章,有好的文章要Cut下来,有问题尽管问,只要问题不是太傻,一般高手都会回答你的。下面介绍几个好的Java方面的编程网站:
CSDN论坛 http://www.csdn.net/ 中国最有名的技术论坛,《程序员》杂志就是他们出版的,你可以在上面提出问题,马上就有人回答你,如果你觉得好,你可以给那人加分;
Java研究组织 http://www.javaresearch.org/ 上面有很多原创文章,高手还是挺多的;
Java开发者 http://www.chinajavaworld.com/ 那里Java资料比较全;
java.com.cn http://www.java.com.cn/ 看这域名就知道有多牛,注册用户快接近一万了,同时在线人数也在一千左右,人气很旺的;
IBM的开发者网络 http://www-900.ibm.com/developerWorks/cn/java/index.shtml IBM永远的蓝色巨人;

那么我书也看了,程序也做了,别人问我的问题我都能解决了,是不是就成为高手了呢?当然没那么简单,这只是万里长征走完了第一步。不信?那你出去接一个项目,你知道怎么下手吗,你知道怎么设计吗,你知道怎么组织人员进行开发吗?你现在脑子里除了一些散乱的代码之外,可能再没有别的东西了吧!你现在最缺的是实际的工作经验,而不是书本上那些凭空想出来的程序。所以你快去找一份Java的编程工作来做吧(如果是在校学生可以去做兼职啊),在实践中提高自己,那才是最快的。不过你得祈祷在公司里碰到一个高手,而且他还愿意不厌其烦地教你,这样好象有点难哦!

还有一个办法就是读开放源码的程序了。我们知道开放源码大都出自高手,他们设计合理,考虑周到,再加上有广大的程序员参与,代码的价值自然是字字珠叽,铿锵有力(对不起,偶最近《金装四大才子》看多了)。学Java必读的两个开源程序就是Jive和Pet Store。

Jive是国外一个非常著名的BBS程序,完全开放源码。论坛的设计采用了很多先进的技术,如Cache、用户认证、Filter、XML等,而且论坛完全屏蔽了对数据库的访问,可以很轻易的在不同数据库中移植。论坛还有方便的安装和管理程序,这是我们平时编程时容易忽略的一部份(中国程序员一般只注重编程的技术含量,却完全不考虑用户的感受,这就是我们与国外软件的差距所在)。Jive的资料在很多网站上都有,大家可以找来研究一下。相信你读完代码后,会有脱胎换骨的感觉。遗憾的是Jive从2.5以后就不再无条件的开放源代码,同时有licence限制。不过幸好还有中国一流的Java程序员关注它,外国人不开源了,中国人就不能开源吗?这里向大家推荐一个汉化的Jive版本—J道。Jive(J道版)是由中国Java界大名鼎鼎的banq在Jive 2.1版本基础上改编而成, 全中文,增加了一些实用功能,如贴图,用户头像和用户资料查询等,而且有一个开发团队在不断升级。你可以访问banq的网站 http://www.jdon.com/ 去下载,或到同济技术论坛的服务器上 ftp://nro.shtdu.edu.cn 去下,安装上有什么问题,可以到论坛上去提问。

Pet Store(宠物店)是SUN公司为了演示其J2EE编程规范而推出的开放源码的程序,应该很具有权威性,想学J2EE和EJB的朋友不要错过了。

你一定会高兴地说,哈哈,原来成为Java高手就这么简单啊!记得Tomjava也曾碰到过一个项目经理,号称Java很简单,只要三个月就可以学会。其实说这种话的人就如当年小日本号称“三个月拿下中国”一样大言不惭。不是Tomjava泼你冷水,你现在只是学到了Java的骨架,却还没有学到Java的精髓。接下来你得研究设计模式了。设计模式是高级程序员真正掌握面向对象核心思想的必修课。设计模式并不是一种具体"技术",它讲述的是思想,它不仅仅展示了接口或抽象类在实际案例中的灵活应用和智慧,让你能够真正掌握接口或抽象类的应用,从而在原来的Java语言基础上跃进一步,更重要的是,设计模式反复向你强调一个宗旨:要让你的程序尽可能的可重用。

关于设计模式的资料,还是向大家推荐banq的网站 http://www.jdon.com/ ,他把GOF的23种模式以通俗易懂的方式诠释出来,纯Java描述,真是经典中的经典。有时间再研究一下MVC结构(把Model-View-Control分离开的设计思想)吧,现在很流行的Struts就是它的一种实现方式,不过Struts用起来实在是很繁,我们只要学习其精髓即可,我们完全可以设计自己的MVC结构。然后你再研究一下软件Refactoring(重整)和极限XP编程,相信你又会上一个台阶。

做完这些,你不如整理一下你的Java代码,把那些经典的程序和常见的应用整理出来,再精心打造一番,提高其重用性和可扩展性。你再找几个志同道合的朋友成立一个工作室吧,你可以去承接一些项目做了,一开始可能有些困难,可是你有技术积累,又考虑周全,接下项目来可以迅速作完,相信大家以后都会来找你的,所以Money就哗啦啦的来了。。。。。。

当然你也可以参加一些开源项目,一方面可以提高自己,另一方面也是为中国软件事业做贡献嘛!开发者在互联网上用CVS合作开发,用QQ,MSN,E-mail讨论联系,天南海北的程序员分散在各地却同时开发同一个软件,是不是很有意思呢?
下面介绍两个好的开源项目网站:
湖北省软件公共开发平台 http://gro.clinux.org/
共创联盟 http://cosoft.org.cn/

哇,好高兴哦,我终于成为高手了!非也,非也。古人云:“识时务者为俊杰”。你知道计算机界现在的发展形势吗?你知道微软的.NET蓝图和SUN ONE计划之间的明争暗斗吗?你知道计算机技术将向何处发展吗?其实从各大计算机厂商最近的动作,都可以看出来“Web服务将是下一代互联网应用的制高点”,而微软的.NET蓝图和SUN ONE计划的斗争焦点,也就是Web服务。Web服务就是一个崭新的分布式计算模型,它是一系列标准的综合(XML,SOAP,UDDI,WSDL和WSFL等)。它使得不同语言编写的软件能够轻易的集成起来,使网络资源和Web站点变成一种服务而不是混乱的垃圾场。不远的将来,我们就可以在家里点击一下鼠标,就可以完成出门旅游的全部准备工作,包括定飞机票,定旅游线路,定好房间等。请注意,这所有的一切都是Web站点间自动完成的,再也不用象现在一样,表面上是电子商务,实际上很多环节都是人工操作。也许你会觉得这是天方夜谈,不过就近的说,你也很有可能承接一个项目,要集成两个企业的ERP系统。很有可能上游企业的系统是用Delphi编的,而下游企业的系统是用Java编的。你说你是Java高手,大家都看者你怎么做呢。所以啊,你还得学习新技术,如Web服务,而且你Delphi也要懂一点吧(Delphi6现在已经提供Web服务的控件了)。 你编的Java系统,可能要和.NET集成,所以你.NET要懂一点吧?到最后,你可能发现你已经成为Java高手了,但很多时间却在搞别的技术。太极张三丰里说,最厉害的招式就是没有招式,可能就是这个道理吧!

因为刚刚兴起,所以网上Web服务的资料不是很多,我还是给大家推荐几个网站吧:
中国UDDI技术联盟 http://www.uddi-china.org/
CSDN的柴晓路专栏 http://www.csdn.net/develop/author/ColumnAuthor/fennivel/ (注:柴晓路也是互联网上一个有名的人物,他发表过很多关于Web服务的文章,还出了一书,应该称的上是中国Web服务技术的先行者)
IBM的开发者网络的XML&Web Service专栏: http://www-900.ibm.com/developerWorks/cn/xml/index.shtml?csdn IBM可是Web服务的力推者

- 作者: youyou2005 2005年08月5日, 星期五 10:01  回复(0) |  引用(0) 加入博采

每个java初学者都应该搞懂的问题!
对于这个系列里的问题,每个学Java的人都应该搞懂。当然,如果只是学Java玩玩就无所谓了。如果你认为自己已经超越初学者了,却不很懂这些问题,请将你自己重归初学者行列。内容均来自于CSDN的经典老贴。

问题一:我声明了什么!

String s = "Hello world!";

许多人都做过这样的事情,但是,我们到底声明了什么?回答通常是:一个String,内容是“Hello world!”。这样模糊的回答通常是概念不清的根源。如果要准确的回答,一半的人大概会回答错误。
这个语句声明的是一个指向对象的引用,名为“s”,可以指向类型为String的任何对象,目前指向"Hello world!"这个String类型的对象。这就是真正发生的事情。我们并没有声明一个String对象,我们只是声明了一个只能指向String对象的引用变量。所以,如果在刚才那句语句后面,如果再运行一句:

String string = s;

我们是声明了另外一个只能指向String对象的引用,名为string,并没有第二个对象产生,string还是指向原来那个对象,也就是,和s指向同一个对象。

问题二:"=="和equals方法究竟有什么区别?

==操作符专门用来比较变量的值是否相等。比较好理解的一点是:
int a=10;
int b=10;
则a==b将是true。
但不好理解的地方是:
String a=new String("foo");
String b=new String("foo");
则a==b将返回false。

根据前一帖说过,对象变量其实是一个引用,它们的值是指向对象所在的内存地址,而不是对象本身。a和b都使用了new操作符,意味着将在内存中产生两个内容为"foo"的字符串,既然是“两个”,它们自然位于不同的内存地址。a和b的值其实是两个不同的内存地址的值,所以使用"=="操作符,结果会是false。诚然,a和b所指的对象,它们的内容都是"foo",应该是“相等”,但是==操作符并不涉及到对象内容的比较。
对象内容的比较,正是equals方法做的事。

看一下Object对象的equals方法是如何实现的:
boolean equals(Object o){

return this==o;

}
Object对象默认使用了==操作符。所以如果你自创的类没有覆盖equals方法,那你的类使用equals和使用==会得到同样的结果。同样也可以看出,Object的equals方法没有达到equals方法应该达到的目标:比较两个对象内容是否相等。因为答案应该由类的创建者决定,所以Object把这个任务留给了类的创建者。

看一下一个极端的类:
Class Monster{
private String content;
...
boolean equals(Object another){ return true;}

}
我覆盖了equals方法。这个实现会导致无论Monster实例内容如何,它们之间的比较永远返回true。

所以当你是用equals方法判断对象的内容是否相等,请不要想当然。因为可能你认为相等,而这个类的作者不这样认为,而类的equals方法的实现是由他掌握的。如果你需要使用equals方法,或者使用任何基于散列码的集合(HashSet,HashMap,HashTable),请察看一下java doc以确认这个类的equals逻辑是如何实现的。

问题三:String到底变了没有?

没有。因为String被设计成不可变(immutable)类,所以它的所有对象都是不可变对象。请看下列代码:

String s = "Hello";
s = s + " world!";

s所指向的对象是否改变了呢?从本系列第一篇的结论很容易导出这个结论。我们来看看发生了什么事情。在这段代码中,s原先指向一个String对象,内容是"Hello",然后我们对s进行了+操作,那么s所指向的那个对象是否发生了改变呢?答案是没有。这时,s不指向原来那个对象了,而指向了另一个String对象,内容为"Hello world!",原来那个对象还存在于内存之中,只是s这个引用变量不再指向它了。
通过上面的说明,我们很容易导出另一个结论,如果经常对字符串进行各种各样的修改,或者说,不可预见的修改,那么使用String来代表字符串的话会引起很大的内存开销。因为String对象建立之后不能再改变,所以对于每一个不同的字符串,都需要一个String对象来表示。这时,应该考虑使用StringBuffer类,它允许修改,而不是每个不同的字符串都要生成一个新的对象。并且,这两种类的对象转换十分容易。
同时,我们还可以知道,如果要使用内容相同的字符串,不必每次都new一个String。例如我们要在构造器中对一个名叫s的String引用变量进行初始化,把它设置为初始值,应当这样做:
public class Demo {
private String s;
...
public Demo {
s = "Initial Value";
}
...
}
而非
s = new String("Initial Value");
后者每次都会调用构造器,生成新对象,性能低下且内存开销大,并且没有意义,因为String对象不可改变,所以对于内容相同的字符串,只要一个String对象来表示就可以了。也就说,多次调用上面的构造器创建多个对象,他们的String类型属性s都指向同一个对象。
上面的结论还基于这样一个事实:对于字符串常量,如果内容相同,Java认为它们代表同一个String对象。而用关键字new调用构造器,总是会创建一个新的对象,无论内容是否相同。
至于为什么要把String类设计成不可变类,是它的用途决定的。其实不只String,很多Java标准类库中的类都是不可变的。在开发一个系统的时候,我们有时候也需要设计不可变类,来传递一组相关的值,这也是面向对象思想的体现。不可变类有一些优点,比如因为它的对象是只读的,所以多线程并发访问也不会有任何问题。当然也有一些缺点,比如每个不同的状态都要一个对象来代表,可能会造成性能上的问题。所以Java标准类库还提供了一个可变版本,即StringBuffer。

问题四:final关键字到底修饰了什么?

final使得被修饰的变量"不变",但是由于对象型变量的本质是“引用”,使得“不变”也有了两种含义:引用本身的不变,和引用指向的对象不变

引用本身的不变:
final StringBuffer a=new StringBuffer("immutable");
final StringBuffer b=new StringBuffer("not immutable");
a=b;//编译期错误

引用指向的对象不变:
final StringBuffer a=new StringBuffer("immutable");
a.append(" broken!"); //编译通过

可见,final只对引用的“值”(也即它所指向的那个对象的内存地址)有效,它迫使引用只能指向初始指向的那个对象,改变它的指向会导致编译期错误。至于它所指向的对象的变化,final是不负责的。这很类似==操作符:==操作符只负责引用的“值”相等,至于这个地址所指向的对象内容是否相等,==操作符是不管的。

理解final问题有很重要的含义。许多程序漏洞都基于此----final只能保证引用永远指向固定对象,不能保证那个对象的状态不变。在多线程的操作中,一个对象会被多个线程共享或修改,一个线程对对象无意识的修改可能会导致另一个使用此对象的线程崩溃。一个错误的解决方法就是在此对象新建的时候把它声明为final,意图使得它“永远不变”。其实那是徒劳的。

问题五:到底要怎么样初始化!

本问题讨论变量的初始化,所以先来看一下Java中有哪些种类的变量。
1. 类的属性,或者叫值域
2. 方法里的局部变量
3. 方法的参数

对于第一种变量,Java虚拟机会自动进行初始化。如果给出了初始值,则初始化为该初始值。如果没有给出,则把它初始化为该类型变量的默认初始值。

int类型变量默认初始值为0
float类型变量默认初始值为0.0f
double类型变量默认初始值为0.0
boolean类型变量默认初始值为false
char类型变量默认初始值为0(ASCII码)
long类型变量默认初始值为0
所有对象引用类型变量默认初始值为null,即不指向任何对象。注意数组本身也是对象,所以没有初始化的数组引用在自动初始化后其值也是null。

对于两种不同的类属性,static属性与instance属性,初始化的时机是不同的。instance属性在创建实例的时候初始化,static属性在类加载,也就是第一次用到这个类的时候初始化,对于后来的实例的创建,不再次进行初始化。这个问题会在以后的系列中进行详细讨论。

对于第二种变量,必须明确地进行初始化。如果再没有初始化之前就试图使用它,编译器会抗议。如果初始化的语句在try块中或if块中,也必须要让它在第一次使用前一定能够得到赋值。也就是说,把初始化语句放在只有if块的条件判断语句中编译器也会抗议,因为执行的时候可能不符合if后面的判断条件,如此一来初始化语句就不会被执行了,这就违反了局部变量使用前必须初始化的规定。但如果在else块中也有初始化语句,就可以通过编译,因为无论如何,总有至少一条初始化语句会被执行,不会发生使用前未被初始化的事情。对于try-catch也是一样,如果只有在try块里才有初始化语句,编译部通过。如果在catch或finally里也有,则可以通过编译。总之,要保证局部变量在使用之前一定被初始化了。所以,一个好的做法是在声明他们的时候就初始化他们,如果不知道要初始化成什么值好,就用上面的默认值吧!

其实第三种变量和第二种本质上是一样的,都是方法中的局部变量。只不过作为参数,肯定是被初始化过的,传入的值就是初始值,所以不需要初始化。

问题六:instanceof是什么东东?

instanceof是Java的一个二元操作符,和==,>,<是同一类东东。由于它是由字母组成的,所以也是Java的保留关键字。它的作用是测试它左边的对象是否是它右边的类的实例,返回boolean类型的数据。举个例子:

String s = "I AM an Object!";
boolean isObject = s instanceof Object;

我们声明了一个String对象引用,指向一个String对象,然后用instancof来测试它所指向的对象是否是Object类的一个实例,显然,这是真的,所以返回true,也就是isObject的值为True。
instanceof有一些用处。比如我们写了一个处理账单的系统,其中有这样三个类:

public class Bill {//省略细节}
public class PhoneBill extends Bill {//省略细节}
public class GasBill extends Bill {//省略细节}

在处理程序里有一个方法,接受一个Bill类型的对象,计算金额。假设两种账单计算方法不同,而传入的Bill对象可能是两种中的任何一种,所以要用instanceof来判断:

public double calculate(Bill bill) {
if (bill instanceof PhoneBill) {
//计算电话账单
}
if (bill instanceof GasBill) {
//计算燃气账单
}
...
}
这样就可以用一个方法处理两种子类。

然而,这种做法通常被认为是没有好好利用面向对象中的多态性。其实上面的功能要求用方法重载完全可以实现,这是面向对象变成应有的做法,避免回到结构化编程模式。只要提供两个名字和返回值都相同,接受参数类型不同的方法就可以了:

public double calculate(PhoneBill bill) {
//计算电话账单
}

public double calculate(GasBill bill) {
//计算燃气账单
}

所以,使用instanceof在绝大多数情况下并不是推荐的做法,应当好好利用多态。