??xml version="1.0" encoding="utf-8" standalone="yes"?>亚洲成人一区二区,相泽亚洲一区中文字幕,亚洲国产综合精品中文第一http://www.tkk7.com/juhongtao/category/5793.htmlzh-cnSun, 20 May 2007 17:05:52 GMTSun, 20 May 2007 17:05:52 GMT60JAVA之精髓IO?http://www.tkk7.com/juhongtao/archive/2007/05/19/118585.htmljavaGrowingjavaGrowingSat, 19 May 2007 12:11:00 GMThttp://www.tkk7.com/juhongtao/archive/2007/05/19/118585.htmlhttp://www.tkk7.com/juhongtao/comments/118585.htmlhttp://www.tkk7.com/juhongtao/archive/2007/05/19/118585.html#Feedback0http://www.tkk7.com/juhongtao/comments/commentRss/118585.htmlhttp://www.tkk7.com/juhongtao/services/trackbacks/118585.html
一Q?Input和Output
1. stream代表的是M有能力出数据的数据源,或是M有能力接收数据的接收源。在Java的IO中,所有的streamQ包括Input和Out streamQ都包括两种cdQ?
1.1 以字节ؓ(f)导向的stream
以字节ؓ(f)导向的streamQ表CZ字节为单位从stream中读取或往stream中写入信息。以字节为导向的stream包括下面几种cdQ?
1) input streamQ?
1) ByteArrayInputStreamQ把内存中的一个缓冲区作ؓ(f)InputStream使用
2) StringBufferInputStreamQ把一个String对象作ؓ(f)InputStream
3) FileInputStreamQ把一个文件作为InputStreamQ实现对文g的读取操?
4) PipedInputStreamQ实Cpipe的概念,主要在线E中使用
5) SequenceInputStreamQ把多个InputStream合ƈZ个InputStream
2) Out stream
1) ByteArrayOutputStreamQ把信息存入内存中的一个缓冲区?
2) FileOutputStreamQ把信息存入文g?
3) PipedOutputStreamQ实Cpipe的概念,主要在线E中使用
4) SequenceOutputStreamQ把多个OutStream合ƈZ个OutStream
1.2 以Unicode字符为导向的stream
以Unicode字符为导向的streamQ表CZUnicode字符为单位从stream中读取或往stream中写入信息。以Unicode字符为导向的stream包括下面几种cdQ?
1) Input Stream
1) CharArrayReaderQ与ByteArrayInputStream对应
2) StringReaderQ与StringBufferInputStream对应
3) FileReaderQ与FileInputStream对应
4) PipedReaderQ与PipedInputStream对应
2) Out Stream
1) CharArrayWriteQ与ByteArrayOutputStream对应
2) StringWriteQ无与之对应的以字节为导向的stream
3) FileWriteQ与FileOutputStream对应
4) PipedWriteQ与PipedOutputStream对应
以字Wؓ(f)导向的stream基本上对有与之相对应的以字节为导向的stream。两个对应类实现的功能相同,字是在操作时的导向不同。如CharArrayReaderQ和ByteArrayInputStream的作用都是把内存中的一个缓冲区作ؓ(f)InputStream使用Q所不同的是前者每ơ从内存中读取一个字节的信息Q而后者每ơ从内存中读取一个字W?
1.3 两种不现导向的stream之间的{?
InputStreamReader和OutputStreamReaderQ把一个以字节为导向的stream转换成一个以字符为导向的stream?
2. streamd属?
2.1 “ؓ(f)streamd属性”的作用
q用上面介绍的Java中操作IO的APIQ我们就可完成我们想完成的Q何操作了。但通过FilterInputStream和FilterOutStream的子c,我们可以为streamd属性。下面以一个例子来说明q种功能的作用?
如果我们要往一个文件中写入数据Q我们可以这h作:(x)
FileOutStream fs = new FileOutStream(“test.txt?;
然后可以通过产生的fs对象调用write()函数来往test.txt文g中写入数据了。但是,如果我们惛_现“先把要写入文g的数据先~存到内存中Q再把缓存中的数据写入文件中”的功能Ӟ上面的API没有一个能满我们的需求了。但是通过FilterInputStream和FilterOutStream的子c,为FileOutStreamd我们所需要的功能?
2.2 FilterInputStream的各U类?
2.2.1 用于装以字节ؓ(f)导向的InputStream
1) DataInputStreamQ从stream中读取基本类型(int、char{)(j)数据?
2) BufferedInputStreamQ用缓冲区
3) LineNumberInputStreamQ会(x)记录input stream内的行数Q然后可以调用getLineNumber()和setLineNumber(int)
4) PushbackInputStreamQ很用刎ͼ一般用于编译器开?
2.2.2 用于装以字Wؓ(f)导向的InputStream
1) 没有与DataInputStream对应的类。除非在要用readLine()时改用BufferedReaderQ否则用DataInputStream
2) BufferedReaderQ与BufferedInputStream对应
3) LineNumberReaderQ与LineNumberInputStream对应
4) PushBackReaderQ与PushbackInputStream对应
2.3 FilterOutStream的各U类?
2.2.3 用于装以字节ؓ(f)导向的OutputStream
1) DataIOutStreamQ往stream中输出基本类型(int、char{)(j)数据?
2) BufferedOutStreamQ用缓冲区
3) PrintStreamQ生格式化输出
2.2.4 用于装以字Wؓ(f)导向的OutputStream
1) BufferedWriteQ与对应
2) PrintWriteQ与对应
3. RandomAccessFile
1) 可通过RandomAccessFile对象完成Ҏ(gu)件的d操作
2) 在生一个对象时Q可指明要打开的文件的性质QrQ只读;wQ只写;rw可读?
3) 可以直接跛_文g中指定的位置
4. I/O应用的一个例?
import java.io.*;
public class TestIO{
public static void main(String[] args)
throws IOException{
//1.以行为单位从一个文件读取数?
BufferedReader in =
new BufferedReader(
new FileReader("F:\\nepalon\\TestIO.java"));
String s, s2 = new String();
while((s = in.readLine()) != null)
s2 += s + "\n";
in.close();

//1b. 接收键盘的输?
BufferedReader stdin =
new BufferedReader(
new InputStreamReader(System.in));
System.out.println("Enter a line:");
System.out.println(stdin.readLine());

//2. 从一个String对象中读取数?
StringReader in2 = new StringReader(s2);
int c;
while((c = in2.read()) != -1)
System.out.println((char)c);
in2.close();

//3. 从内存取出格式化输入
try{
DataInputStream in3 =
new DataInputStream(
new ByteArrayInputStream(s2.getBytes()));
while(true)
System.out.println((char)in3.readByte());
}
catch(EOFException e){
System.out.println("End of stream");
}

//4. 输出到文?
try{
BufferedReader in4 =
new BufferedReader(
new StringReader(s2));
PrintWriter out1 =
new PrintWriter(
new BufferedWriter(
new FileWriter("F:\\nepalon\\ TestIO.out")));
int lineCount = 1;
while((s = in4.readLine()) != null)
out1.println(lineCount++ + "Q? + s);
out1.close();
in4.close();
}
catch(EOFException ex){
System.out.println("End of stream");
}

//5. 数据的存储和恢复
try{
DataOutputStream out2 =
new DataOutputStream(
new BufferedOutputStream(
new FileOutputStream("F:\\nepalon\\ Data.txt")));
out2.writeDouble(3.1415926);
out2.writeChars("\nThas was pi:writeChars\n");
out2.writeBytes("Thas was pi:writeByte\n");
out2.close();
DataInputStream in5 =
new DataInputStream(
new BufferedInputStream(
new FileInputStream("F:\\nepalon\\ Data.txt")));
BufferedReader in5br =
new BufferedReader(
new InputStreamReader(in5));
System.out.println(in5.readDouble());
System.out.println(in5br.readLine());
System.out.println(in5br.readLine());
}
catch(EOFException e){
System.out.println("End of stream");
}

//6. 通过RandomAccessFile操作文g
RandomAccessFile rf =
new RandomAccessFile("F:\\nepalon\\ rtest.dat", "rw");
for(int i=0; i<10; i++)
rf.writeDouble(i*1.414);
rf.close();

rf = new RandomAccessFile("F:\\nepalon\\ rtest.dat", "r");
for(int i=0; i<10; i++)
System.out.println("Value " + i + "Q? + rf.readDouble());
rf.close();

rf = new RandomAccessFile("F:\\nepalon\\ rtest.dat", "rw");
rf.seek(5*8);
rf.writeDouble(47.0001);
rf.close();

rf = new RandomAccessFile("F:\\nepalon\\ rtest.dat", "r");
for(int i=0; i<10; i++)
System.out.println("Value " + i + "Q? + rf.readDouble());
rf.close();
}
}
关于代码的解释(以区为单位)(j)Q?
1ZQ当d文gӞ先把文g内容d~存中,当调用in.readLine()Ӟ再从~存中以字符的方式读取数据(以下U“缓存字节读取方式”)(j)?
1bZQ由于想以缓存字节读取方式从标准IOQ键盘)(j)中读取数据,所以要先把标准IOQSystem.inQ{换成字符导向的streamQ再q行BufferedReader装?
2ZQ要以字W的形式从一个String对象中读取数据,所以要产生一个StringReadercd的stream?
4ZQ对String对象s2d数据Ӟ先把对象中的数据存入~存中,再从~冲中进行读取;对TestIO.out文gq行操作Ӟ先把格式化后的信息输出到~存中,再把~存中的信息输出到文件中?
5ZQ对Data.txt文gq行输出Ӟ是先把基本类型的数据输出屋缓存中Q再把缓存中的数据输出到文g中;Ҏ(gu)件进行读取操作时Q先把文件中的数据读取到~存中,再从~存中以基本cd的Ş式进行读取。注意in5.readDouble()q一行。因为写入第一个writeDouble()Q所以ؓ(f)了正显C。也要以基本cd的Ş式进行读取?
6区是通过RandomAccessFilecd文gq行操作?br />



javaGrowing 2007-05-19 20:11 发表评论
]]>
全面理解Java中的String数据cdhttp://www.tkk7.com/juhongtao/archive/2006/03/03/33351.htmljavaGrowingjavaGrowingFri, 03 Mar 2006 01:03:00 GMThttp://www.tkk7.com/juhongtao/archive/2006/03/03/33351.htmlhttp://www.tkk7.com/juhongtao/comments/33351.htmlhttp://www.tkk7.com/juhongtao/archive/2006/03/03/33351.html#Feedback0http://www.tkk7.com/juhongtao/comments/commentRss/33351.htmlhttp://www.tkk7.com/juhongtao/services/trackbacks/33351.html1. 首先String不属?U基本数据类型,String是一个对象?/STRONG>

  因ؓ(f)对象的默认值是nullQ所以String的默认g是nullQ但它又是一U特D的对象Q有其它对象没有的一些特性?/P>

  2. new String()和new String(“?都是x一个新的空字符Ԍ是空串不是nullQ?/STRONG>

  3. String str=”kvill”;String str=new String (“kvill?;的区别:(x)

  在这里,我们不谈堆,也不谈栈Q只先简单引入常量池q个单的概念?/P>

  帔R?constant pool)指的是在~译期被定Qƈ被保存在已编译的.class文g中的一些数据。它包括了关于类、方法、接口等中的帔RQ也包括字符串常量?/P>

  看例1Q?/P>

String s0=”kvill?
String s1=”kvill?
String s2=”kv?+ “ill?
System.out.println( s0==s1 );
System.out.println( s0==s2 );

  l果为:(x)

true
true

  首先Q我们要知道Java?x)确保一个字W串帔R只有一个拷贝?/P>

  因ؓ(f)例子中的s0和s1中的”kvill”都是字W串帔RQ它们在~译期就被确定了Q所以s0==s1为trueQ而”kv”和”ill”也都是字符串常量,当一个字W串由多个字W串帔Rq接而成Ӟ它自p定也是字W串帔RQ所以s2也同样在~译期就被解析ؓ(f)一个字W串帔RQ所以s2也是帔R池中?kvill”的一个引用?/P>

  所以我们得出s0==s1==s2;

  用new String() 创徏的字W串不是帔RQ不能在~译期就定Q所以new String() 创徏的字W串不放入常量池中,它们有自q地址I间?/P>

  看例2Q?/P>

String s0=”kvill?
String s1=new String(”kvill?;
String s2=”kv?+ new String(“ill?;
System.out.println( s0==s1 );
System.out.println( s0==s2 );
System.out.println( s1==s2 );

  l果为:(x)

false
false
false

  ?中s0q是帔R池中”kvill”的应用Qs1因ؓ(f)无法在编译期定Q所以是q行时创建的新对象”kvill”的引用Qs2因ؓ(f)有后半部分new String(“ill?所以也无法在编译期定Q所以也是一个新创徏对象”kvill”的应用;明白了这些也q道ؓ(f)何得出此l果了?/P>

  4. String.intern()Q?/STRONG>

  再补充介l一点:(x)存在?class文g中的帔R池,在运行期被JVM装蝲Qƈ且可以扩充。String的intern()Ҏ(gu)是扩充帔R池的一个方法;当一个String实例str调用intern()Ҏ(gu)ӞJava查找帔R池中是否有相同Unicode的字W串帔RQ如果有Q则q回其的引用Q如果没有,则在帔R池中增加一个Unicode{于str的字W串q返回它的引用;看例3清楚了

  ?Q?/P>

String s0= “kvill?
String s1=new String(”kvill?;
String s2=new String(“kvill?;
System.out.println( s0==s1 );
System.out.println( ?*********?);
s1.intern();
s2=s2.intern(); //把常量池中“kvill”的引用赋给s2
System.out.println( s0==s1);
System.out.println( s0==s1.intern() );
System.out.println( s0==s2 );

  l果为:(x)

false
**********
false //虽然执行了s1.intern(),但它的返回值没有赋ls1
true //说明s1.intern()q回的是帔R池中”kvill”的引用
true

  最后我再破除一个错误的理解Q?/P>

  有h_(d)“用String.intern()Ҏ(gu)则可以将一个Stringcȝ保存C个全局String表中Q如果具有相同值的Unicode字符串已l在q个表中Q那么该Ҏ(gu)q回表中已有字符串的地址Q如果在表中没有相同值的字符Ԍ则将自己的地址注册到表中“如果我把他说的q个全局?String表理解ؓ(f)帔R池的话,他的最后一句话Q“如果在表中没有相同值的字符Ԍ则将自己的地址注册到表中”是错的Q?/P>

  看例4Q?/P>

String s1=new String("kvill");
String s2=s1.intern();
System.out.println( s1==s1.intern() );
System.out.println( s1+" "+s2 );
System.out.println( s2==s1.intern() );

  l果Q?/P>

false
kvill kvill
true

  在这个类中我们没有声名一个”kvill”常量,所以常量池中一开始是没有”kvill”的Q当我们调用s1.intern()后就在常量池中新d了一个?kvill”常量,原来的不在常量池中的”kvill”仍然存在,也就不是“将自己的地址注册到常量池中”了?/P>

  s1==s1.intern()为false说明原来的“kvill”仍然存在;

  s2现在为常量池中“kvill”的地址Q所以有s2==s1.intern()为true?/P>

  5. 关于equals()?=:

  q个对于String单来说就是比较两字符串的Unicode序列是否相当Q如果相{返回true;?=是比较两字符串的地址是否相同Q也是是否是同一个字W串的引用?/P>

  6. 关于String是不可变?/STRONG>

  q一说又要说很多Q大家只要知道String的实例一旦生成就不会(x)再改变了Q比如说QString str=”kv?”ill???”ans?是?个字W串帔RQ首先”kv”和”ill”生成了”kvill”存在内存中Q然后”kvill”又和??生成 ”kvill “存在内存中Q最后又和生成了”kvill ans?q把q个字符串的地址赋给了str,是因ؓ(f)String的“不可变”生了很多临时变量Q这也就是ؓ(f)什么徏议用StringBuffer的原因了Q因为StringBuffer是可改变?/P>

javaGrowing 2006-03-03 09:03 发表评论
]]>
深入理解嵌套cd内部c?/title><link>http://www.tkk7.com/juhongtao/archive/2006/01/07/26986.html</link><dc:creator>javaGrowing</dc:creator><author>javaGrowing</author><pubDate>Sat, 07 Jan 2006 08:45:00 GMT</pubDate><guid>http://www.tkk7.com/juhongtao/archive/2006/01/07/26986.html</guid><wfw:comment>http://www.tkk7.com/juhongtao/comments/26986.html</wfw:comment><comments>http://www.tkk7.com/juhongtao/archive/2006/01/07/26986.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.tkk7.com/juhongtao/comments/commentRss/26986.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/juhongtao/services/trackbacks/26986.html</trackback:ping><description><![CDATA[<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0> <TBODY> <TR> <TD vAlign=top width=363><B>一、什么是嵌套cd(qing)内部c?</B><BR>    可以在一个类的内部定义另一个类Q这U类UCؓ(f)嵌套c(nested classesQ?它有两种cd: 静态嵌套类和非静态嵌套类。静态嵌套类使用很少Q最重要的是非静态嵌套类Q也x被称作ؓ(f)内部c?inner)。嵌套类从JDK1.1开始引入。其中innercd可分ZU:(x)<BR>    其一、在一个类Q外部类Q中直接定义的内部类Q?BR>    其二、在一个方法(外部cȝҎ(gu)Q中定义的内部类;<BR>    其三、匿名内部类?BR>下面Q我说明这几种嵌套cȝ使用?qing)注意事V?</TD></TR> <TR> <TD colSpan=2 height=20> <P><BR></P></TD></TR></TBODY></TABLE> <P> <BR><B>二、静态嵌套类</B><BR>    如下所CZ码ؓ(f)定义一个静态嵌套类Q?BR></P> <CENTER> <DIV style="BACKGROUND: #aabbcc; WIDTH: 600px"> <P align=left><FONT color=#0000ff>public class StaticTest {<BR>  private static String name = "javaJohn";<BR>  private String id = "X001";<BR><BR>  static class Person{<BR>    private String address = "swjtu,chenDu,China";<BR>    public String mail = "josserchai@yahoo.com";//内部cd有成?BR>    public void display(){ <BR>      //System.out.println(id);//不能直接讉K外部cȝ非静态成?BR>      System.out.println(name);//只能直接讉K外部cȝ静态成?BR>      System.out.println("Inner "+address);//讉K本内部类成员?BR>    }<BR>  }<BR><BR>   public void printInfo(){<BR>     Person person = new Person();<BR>     person.display();<BR><BR>     //System.out.println(mail);//不可讉K<BR>     //System.out.println(address);//不可讉K<BR><BR>     System.out.println(person.address);//可以讉K内部cȝU有成员<BR>     System.out.println(person.mail);//可以讉K内部cȝ公有成员<BR><BR>   }<BR><BR>   public static void main(String[] args) {<BR>       StaticTest staticTest = new StaticTest();<BR>       staticTest.printInfo();<BR>   }<BR>}<BR></FONT></P></DIV></CENTER>     在静态嵌套类内部Q不能访问外部类的非静态成员,q是由Java语法?静态方法不能直接访问非静态成?所限定?BR>若想讉K外部cȝ变量Q必通过其它Ҏ(gu)解决Q由于这个原因,静态嵌套类使用很少。注意,外部c访问内<BR>部类的的成员有些特别Q不能直接访问,但可以通过内部cd例来讉KQ这是因为静态嵌套内的所有成员和Ҏ(gu)默认?BR>静态的了。同时注意,内部静态类Person只在cStaticTest 范围内可见,若在其它cM引用或初始化Q均是错误的?BR><BR><B>三、在外部cM定义内部c?/B><BR>    如下所CZ码ؓ(f)在外部类中定义两个内部类?qing)它们的调用关系Q? <DIV style="BACKGROUND: #aabbcc; WIDTH: 600px"><span id="64kseyq" class=style1><BR></SPAN><PRE class=style1>class Outer{ int outer_x = 100; private class Inner{//U有的内部类 public int y = 10; private int z = 9; int m = 5; public void display(){ System.out.println("display outer_x:"+ outer_x); } private void display2(){ System.out.println("display outer_x:"+ outer_x); } } public Inner getInner(){//即是对外公开的方?外部cM无法调用 return new Inner(); } void test(){ Inner inner = new Inner(); //可以讉K inner.display(); inner.display2(); //System.out.println("Inner y:" + y);//不能讉K内部内变? System.out.println("Inner y:" + inner.y);//可以讉K System.out.println("Inner z:" + inner.z);//可以讉K System.out.println("Inner m:" + inner.m);//可以讉K InnerTwo innerTwo = new InnerTwo(); innerTwo.show(); } class InnerTwo{ Inner innerx = getInner();//可以讉K public void show(){ //System.out.println(y);//不可讉KInnter的y成员 //System.out.println(Inner.y);//不可直接讉KInner的Q何成员和Ҏ(gu) innerx.display();//可以讉K innerx.display2();//可以讉K System.out.println(innerx.y);//可以讉K System.out.println(innerx.z);//可以讉K System.out.println(innerx.m);//可以讉K } } } public class Test { public static void main(String args[]){ Outer outer = new Outer(); // Outer.Inner a=outer.getInner();//InnercLU有?外部cM能访问,如果InnercLpublic ,则可以? outer.test(); } }</PRE><BR></DIV>    内部cInner?qing)InnterTwo只在cOuter的作用域内是可知的,如果cOuter外的M代码试初始化类Inner或用它Q编译就不会(x)通过。同Ӟ内部cȝ变量成员只在内部内内部可见,若外部类或同层次的内部类需要访问,需采用CZE序<BR>中的Ҏ(gu)Q不可直接访问内部类的变量?BR><BR><B>四、在Ҏ(gu)中定义内部类</B><BR>    如下所CZ码ؓ(f)在方法内部定义一个内部类:<BR><BR> <DIV style="BACKGROUND: #aabbcc; WIDTH: 600px"> <P align=left><FONT color=#0000ff><PRE>public class FunOuter { int out_x = 100; public void test(){ class Inner{ String x = "x"; void display(){ System.out.println(out_x); } } Inner inner = new Inner(); inner.display(); } public void showStr(String str){ //public String str1 = "test Inner";//不可定义Q只允许final修饰 //static String str4 = "static Str";//不可定义Q只允许final修饰 String str2 = "test Inner"; final String str3 = "final Str"; class InnerTwo{ public void testPrint(){ System.out.println(out_x);//可直接访问外部类的变? //System.out.println(str);//不可讉K本方法内部的非final变量 //System.out.println(str2);//不可讉K本方法内部的非final变量 System.out.println(str3);//只可讉K本方法的final型变量成? } } InnerTwo innerTwo = new InnerTwo(); innerTwo.testPrint(); } public void use(){ //Inner innerObj = new Inner();//此时Inner׃可见了? //System.out.println(Inner.x);//此时Inner׃可见了? } public static void main(String[] args) { FunOuter outer = new FunOuter(); outer.test(); } } </PRE></FONT> <P></P></DIV><BR>    从上面的例程我们可以看出定义在方法内部的内部cȝ可见性更,它只在方法内?可见Q在外部c??qing)外部类的其它方法?中都不可见了。同Ӟ它有一个特点,是Ҏ(gu)内的内部c连本方法的成员变量都不可访问,它只能访问本Ҏ(gu)的final型成员。同时另一个需引v注意的是Ҏ(gu)内部定义成员Q只允许final修饰或不加修饰符Q其它像static{均不可用?BR><BR><B>五、匿名内部类</B><BR>    如下所CZ码ؓ(f)定义一个匿名内部类:匿名内部c通常用在Java的事件处理上<BR><BR> <CENTER> <DIV style="BACKGROUND: #aabbcc; WIDTH: 600px"> <P align=left><FONT color=#0000ff><BR>import java.applet.*;<BR>import java.awt.event.*;<BR><BR>    public class AnonymousInnerClassDemo extends Applet{<BR>    public void init(){<BR>        addMouseListener(new MouseAdapter(){<BR>            public void mousePressed(MouseEvent me){<BR>             showStatus("Mouse Pressed!");<BR>        }<BR>        });<BR>    }<BR>    public void showStatus(String str){<BR>        System.out.println(str);<BR>    }<BR>    }<BR></FONT></P></DIV></CENTER><BR>在上面的例子中,Ҏ(gu)addMouseListener接受一个对象型的参数表辑ּQ于是,在参数里Q我们定义了一个匿名内部类,q个cL一个MouseAdaptercd的类Q同时在q个cM定义了一个承的Ҏ(gu)mousePressedQ整个类做ؓ(f)一个参数。这个类没有名称Q但是当执行q个表达式时它被自动实例化。同时因为,q个匿名内部cL定义在AnonymousInnerClassDemo cd部的Q所以它可以讉K它的Ҏ(gu)showStatus。这同前面的内部cL一致的?BR><BR><B>六、内部类使用的其它的问题</B><BR><BR>     通过以上Q我们可以清楚地看出内部cȝ一些用方法,同时Q在许多时候,内部cL在如Java的事件处理、或做ؓ(f)值对象来使用的。同Ӟ我们需注意最后一个问题,那就是,内部cd其它cM栯定义Q同样它也可以承外部其它包的类和实现外部其它地方的接口。同样它也可以承同一层次的其它的内部c?甚至可以l承外部cLw。下面我们给出最后一个例子做为结束:(x)<BR><BR> <DIV style="BACKGROUND: #aabbcc; WIDTH: 600px"> <P align=left><FONT color=#0000ff><BR><PRE>public class Layer { //Layercȝ成员变量 private String testStr = "testStr"; //Personc,基类 class Person{ String name; Email email; public void setName(String nameStr){ this.name = nameStr; } public String getName(){ return this.name; } public void setEmail(Email emailObj){ this.email = emailObj; } public String getEmail(){ return this.email.getMailStr(); } //内部cȝ内部c,多层内部c? class Email{ String mailID; String mailNetAddress; Email(String mailId,String mailNetAddress){ this.mailID = mailId; this.mailNetAddress = mailNetAddress; } String getMailStr(){ return this.mailID +"@"+this.mailNetAddress; } } } //另一个内部类l承外部cLw? class ChildLayer extends Layer{ void print(){ System.out.println(super.testStr);//讉K父类的成员变? } } //另个内部cȝ承内部类Person class OfficePerson extends Person{ void show(){ System.out.println(name); System.out.println(getEmail()); } } //外部cȝ试Ҏ(gu) public void testFunction(){ //试W一个内部类 ChildLayer childLayer = new ChildLayer(); childLayer.print(); //试W二个内部类 OfficePerson officePerson = new OfficePerson(); officePerson.setName("abner chai"); //注意此处Q必ȝ对象.new 出来对象的子cd? //而不是Person.new Email(...) //也不是new Person.Email(...) officePerson.setEmail(officePerson.new Email("josserchai","yahoo.com")); officePerson.show(); } public static void main(String[] args) { Layer layer = new Layer(); layer.testFunction(); } } </PRE><BR></FONT> <P></P></DIV> <P> </P><img src ="http://www.tkk7.com/juhongtao/aggbug/26986.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/juhongtao/" target="_blank">javaGrowing</a> 2006-01-07 16:45 <a href="http://www.tkk7.com/juhongtao/archive/2006/01/07/26986.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);参数http://www.tkk7.com/juhongtao/archive/2006/01/06/26898.htmljavaGrowingjavaGrowingFri, 06 Jan 2006 08:31:00 GMThttp://www.tkk7.com/juhongtao/archive/2006/01/06/26898.htmlhttp://www.tkk7.com/juhongtao/comments/26898.htmlhttp://www.tkk7.com/juhongtao/archive/2006/01/06/26898.html#Feedback1http://www.tkk7.com/juhongtao/comments/commentRss/26898.htmlhttp://www.tkk7.com/juhongtao/services/trackbacks/26898.html 用缺省设|创建时QResultSet 是一U只能访问一ơ(one-time-throughQ、只能向前访问(forward-onlyQ和只读的对象。?zhn)只能讉K数据一ơ,如果再次需要该 数据Q必重新查询数据库?br>
然而,q不只有q一U方式。通过讄 Statement 对象上的参数Q?zhn)可以控制它生?ResultSet。例如:(x)

...
         Class.forName(driverName);
         db = DriverManager.getConnection(connectURL);
         Statement statement = db.createStatement(
                            ResultSet.TYPE_SCROLL_SENSITIVE,
                                                  ResultSet.CONCUR_UPDATABLE
                        );         
         
         String orderElName = xmlfileEl.getElementsByTagName("order").item(0)
                                       .getFirstChild().getNodeValue();
...

q个 Statement 现在生可以更新ƈ应用其他数据库用户所作更改的 ResultSet。?zhn)q可以在q个 ResultSet 中向前和向后Ud?br>
W一个参数指?ResultSet 的类型。其选项有:(x)

TYPE_FORWARD_ONLYQ缺省类型。只允许向前讉K一ơ,q且不会(x)受到其他用户对该数据库所作更改的影响?
TYPE_SCROLL_INSENSITIVEQ允许在列表中向前或向后UdQ甚臛_以进行特定定位,例如U至列表中的W四个记录或者从当前位置向后Ud两个记录。不?x)受到其他用户对该数据库所作更改的影响?
TYPE_SCROLL_SENSITIVEQ象 TYPE_SCROLL_INSENSITIVE 一P允许在记录中定位。这U类型受到其他用h作更改的影响。如果用户在执行完查询之后删除一个记录,那个记录从 ResultSet 中消失。类似的Q对数据值的更改也将反映?ResultSet 中?
W二个参数设|?ResultSet 的ƈ发性,该参数确定是否可以更?ResultSet。其选项有:(x)

CONCUR_READ_ONLYQ这是缺省|指定不可以更?ResultSet
CONCUR_UPDATABLEQ指定可以更?ResultSet

javaGrowing 2006-01-06 16:31 发表评论
]]>
Session详解http://www.tkk7.com/juhongtao/archive/2006/01/04/26613.htmljavaGrowingjavaGrowingWed, 04 Jan 2006 14:22:00 GMThttp://www.tkk7.com/juhongtao/archive/2006/01/04/26613.htmlhttp://www.tkk7.com/juhongtao/comments/26613.htmlhttp://www.tkk7.com/juhongtao/archive/2006/01/04/26613.html#Feedback0http://www.tkk7.com/juhongtao/comments/commentRss/26613.htmlhttp://www.tkk7.com/juhongtao/services/trackbacks/26613.htmlSession详解 作者:(x)郎云?nbsp;   来自Qdev2dev

作者:(x)郎云鹏(dev2dev ID: hippiewolfQ?/p>

摘要Q虽然session机制在web应用E序中被采用已经很长旉了,但是仍然有很多h不清楚session机制的本质,以至不能正确的应用这一 技术。本文将详细讨论session的工作机制ƈ且对在Java web application中应用session机制时常见的问题作出解答?/p>

目录Q?br>一、术语session
二、HTTP协议与状态保?br>三、理解cookie机制
四、理解session机制
五、理解javax.servlet.http.HttpSession
六、HttpSession常见问题
七、跨应用E序的session׃n
八、ȝ
参考文?/p>

一、术语session
在我的经验里Qsessionq个词被滥用的程度大概仅ơ于transactionQ更加有的是transaction与session在某些语境下的含义是相同的?/p>

sessionQ中文经常翻译ؓ(f)?x)话Q其本来的含义是指有始有l的一pd动作/消息Q比如打?sh)话时从拿v?sh)话拨号到挂断?sh)话这中间的一pdq程可以 UCZ个session。有时候我们可以看到这L(fng)话“在一个浏览器?x)话期间Q?..”,q里的会(x)话一词用的就是其本义Q是指从一个浏览器H口打开到关 闭这个期间①。最混ؕ的是“用P客户端)(j)在一ơ会(x)话期间”这样一句话Q它可能指用L(fng)一pd动作Q一般情况下是同某个具体目的相关的一pd动作Q比如从 d到选购商品到结账登样一个网上购物的q程Q有时候也被称Z个transactionQ,然而有时候也可能仅仅是指一ơ连接,也有可能是指含义 ①,其中的差别只能靠上下文来推断②?/p>

然而当session一词与|络协议相关联时Q它又往往隐含了“面向连接”和/或“保持状态”这样两个含义,“面向连接”指的是在通信双方在通信? 前要先徏立一个通信的渠道,比如打电(sh)话,直到Ҏ(gu)接了?sh)话通信才能开始,与此相对的是写信Q在你把信发出去的时候你q不能确认对方的地址是否正确Q通信? 道不一定能建立Q但对发信h来说Q通信已经开始了。“保持状态”则是指通信的一方能够把一pd的消息关联v来,使得消息之间可以互相依赖Q比如一个服务员 能够认出再次光(f)的老顾客ƈ且记得上ơ这个顾客还?gu)Ơ店里一块钱。这一cȝ例子有“一个TCP session”或者“一个POP3 session”③?/p>

而到了web服务器蓬勃发展的时代Qsession在web开发语境下的语义又有了新的扩展Q它的含义是指一cȝ来在客户端与服务器之间保持状态的 解决Ҏ(gu)④。有时候session也用来指q种解决Ҏ(gu)的存储结构,如“把xxx保存在session里”⑤。由于各U用于web开发的语言在一定程度上 都提供了对这U解x案的支持Q所以在某种特定语言的语境下Qsession也被用来指代该语a的解x案,比如l常把Java里提供的 javax.servlet.http.HttpSessionUCؓ(f)session⑥?/p>

鉴于q种混ؕ已不可改变,本文中session一词的q用也会(x)Ҏ(gu)上下文有不同的含义,请大家注意分辨?br>在本文中Q用中文“浏览器?x)话期间”来表达含义①,使用“session机制”来表达含义④,使用“session”表辑֐义⑤Q用具体的“HttpSession”来表达含义?/p>

二、HTTP协议与状态保?/strong>
HTTP协议本n是无状态的Q这与HTTP协议本来的目? 是相W的Q客L(fng)只需要简单的向服务器h下蝲某些文gQ无论是客户端还是服务器都没有必要纪录彼此过ȝ行ؓ(f)Q每一ơ请求之间都是独立的Q好比一个顾? 和一个自动售货机或者一个普通的Q非?x)员Ӟ?j)大卖Z间的关系一栗?/p>

然而聪明(或者贪心?Q的Z很快发现如果能够提供一些按需生成的动态信息会(x)使web变得更加有用Q就像给有线?sh)视加上?gu)功能一栗这U需求一? 面迫使HTML逐步d了表单、脚本、DOM{客L(fng)行ؓ(f)Q另一斚w在服务器端则出现了CGI规范以响应客L(fng)的动态请求,作ؓ(f)传输载体的HTTP协议? d了文件上载、cookieq些Ҏ(gu)。其中cookie的作用就是ؓ(f)了解决HTTP协议无状态的~陷所作出的努力。至于后来出现的session机制? 是又一U在客户端与服务器之间保持状态的解决Ҏ(gu)?/p>

让我们用几个例子来描qC下cookie和session机制之间的区别与联系。笔者曾l常ȝ一家咖啡店有喝5杯咖啡免费赠一杯咖啡的优惠Q然而一ơ性消?杯咖啡的Z(x)微乎其微Q这时就需要某U方式来U录某位֮的消Ҏ(gu)量。想象一下其实也无外乎下面的几种Ҏ(gu)Q?br>1、该店的店员很厉宻I能记住每位顾客的消费数量Q只要顾客一走进咖啡店,店员q道该怎么对待了。这U做法就是协议本w支持状态?br>2、发l顾客一张卡片,上面记录着消费的数量,一般还有个有效期限。每ơ消Ҏ(gu)Q如果顾客出C张卡片,则此ơ消费就?x)与以前或以后的消费相联pv来。这U做法就是在客户端保持状态?br>3、发l顾客一张会(x)员卡Q除了卡号之外什么信息也不纪录,每次消费Ӟ如果֮出示该卡片,则店员在店里的纪录本上找到这个卡号对应的U录d一些消费信息。这U做法就是在服务器端保持状态?/p>

׃HTTP协议是无状态的Q而出于种U考虑也不希望使之成ؓ(f)有状态的Q因此,后面两种Ҏ(gu)成为现实的选择。具体来说cookie机制采用的是? 客户端保持状态的Ҏ(gu)Q而session机制采用的是在服务器端保持状态的Ҏ(gu)。同时我们也看到Q由于采用服务器端保持状态的Ҏ(gu)在客L(fng)也需要保存一? 标识Q所以session机制可能需要借助于cookie机制来达C存标识的目的Q但实际上它q有其他选择?/p>

三、理解cookie机制
cookie机制的基本原理就如上面的例子一L(fng)单,但是q有几个问题需要解冻I(x)“会(x)员卡”如何分发;“会(x)员卡”的内容Q以?qing)客户如何用“会(x)员卡”?/p>

正统的cookie分发是通过扩展HTTP协议来实现的Q服务器通过在HTTP的响应头中加上一行特D的指示以提C浏览器按照指示生成相应的cookie。然而纯_的客户端脚本如JavaScript或者VBScript也可以生成cookie?/p>

而cookie的用是由浏览器按照一定的原则在后台自动发送给服务器的。浏览器(g)查所有存储的cookieQ如果某个cookie所声明的作用范 围大于等于将要请求的资源所在的位置Q则把该cookie附在h资源的HTTPh头上发送给服务器。意思是麦当劳的?x)员卡只能在麦当劳的店里出示Q如 果某家分店还发行了自q?x)员卡,那么q这家店的时候除了要出示麦当劳的?x)员卡,q要出示q家店的?x)员卡?/p>

cookie的内容主要包括:(x)名字Q|q期旉Q\径和域?br>其中域可以指定某一个域比如.google.comQ相当于d招牌Q比如宝z公司,也可以指定一个域下的具体某台机器比如www.google.com或者f(xi)roogle.google.comQ可以用飘柔来做比?br>路径是跟在域名后面的URL路径Q比?或?foo{等Q可以用某飘柔专柜做比?br>路径与域合在一起就构成了cookie的作用范围?br>? 果不讄q期旉Q则表示q个cookie的生命期为浏览器?x)话期间Q只要关闭浏览器H口Qcookie消׃。这U生命期为浏览器?x)话期? cookie被称Z(x)话cookie。会(x)话cookie一般不存储在硬盘上而是保存在内存里Q当然这U行为ƈ不是规范规定的。如果设|了q期旉Q浏览器 ׃(x)把cookie保存到硬盘上Q关闭后再次打开览器,q些cookie仍然有效直到过讑֮的过期时间?/p>

存储在硬盘上的cookie可以在不同的览器进E间׃nQ比如两个IEH口。而对于保存在内存里的cookieQ不同的览器有不同的处理方式? 对于IEQ在一个打开的窗口上按Ctrl-NQ或者从文g菜单Q打开的窗口可以与原窗口共享,而用其他方式新开的IEq程则不能共享已l打开的窗口的? 存cookieQ对于Mozilla Firefox0.8Q所有的q程和标{N都可以共享同L(fng)cookie。一般来说是用javascript的window.open打开的窗口会(x)与原H? 口共享内存cookie。浏览器对于?x)话cookie的这U只认cookie不认人的处理方式l常l采用session机制的web应用E序开发者造成? 大的困扰?/p>

下面是一个goolge讄cookie的响应头的例?br>HTTP/1.1 302 Found
Location: http://www.google.com/intl/zh-CN/
Set-Cookie: PREF=ID=0565f77e132de138:NW=1:TM=1098082649:LM=1098082649:S=KaeaCFPo49RiA_d8; expires=Sun, 17-Jan-2038 19:14:07 GMT; path=/; domain=.google.com
Content-Type: text/html


q是使用HTTPLookq个HTTP Sniffer软g来俘L(fng)HTTP通讯U录的一部分


览器在再次讉Kgoolge的资源时自动向外发送cookie


使用Firefox可以很容易的观察现有的cookie的?br>使用HTTPLook配合F(tun)irefox可以很容易的理解cookie的工作原理?/p>


IE也可以设|在接受cookie前询?/p>


q是一个询问接受cookie的对话框?/p>

四、理解session机制
session机制是一U服务器端的机制Q服务器使用一U类g散列表的l构Q也可能是使用散列表)(j)来保存信息?/p>

当程序需要ؓ(f)某个客户端的h创徏一个session的时候,服务器首先检查这个客L(fng)的请求里是否已包含了一个session标识 - UCؓ(f)session idQ如果已包含一个session id则说明以前已lؓ(f)此客L(fng)创徏qsessionQ服务器按照session id把这个session(g)索出来用(如果(g)索不刎ͼ可能?x)新Z个)(j)Q如果客L(fng)h不包含session idQ则为此客户端创Z个sessionq且生成一个与此session相关联的session idQsession id的值应该是一个既不会(x)重复Q又不容易被扑ֈ规律以仿造的字符Ԍq个session id被在本ơ响应中q回l客L(fng)保存?/p>

保存q个session id的方式可以采用cookieQ这样在交互q程中浏览器可以自动的按照规则把q个标识发挥l服务器。一般这个cookie的名字都是类g SEEESIONIDQ而。比如weblogic对于web应用E序生成的cookieQJSESSIONID= ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764Q它的名字就? JSESSIONID?/p>

׃cookie可以被h为的止Q必L其他机制以便在cookie被禁止时仍然能够把session id传递回服务器。经常被使用的一U技术叫做URL重写Q就是把session id直接附加在URL路径的后面,附加方式也有两种Q一U是作ؓ(f)URL路径的附加信息,表现形式为http://...../xxx; jsessionid=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764
另一U是作ؓ(f)查询字符串附加在URL后面Q表现Ş式ؓ(f)http://...../xxx?jsessionid=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764
q两U方式对于用h说是没有区别的,只是服务器在解析的时候处理的方式不同Q采用第一U方式也有利于把session id的信息和正常E序参数区分开来?br>Z在整个交互过E中始终保持状态,必d每个客户端可能请求的路径后面都包含这个session id?/p>

另一U技术叫做表单隐藏字Dc(din)就是服务器?x)自动修改表单,d一个隐藏字D,以便在表单提交时能够把session id传递回服务器。比如下面的表单
<form name="testform" action="/xxx">
<input type="text">
</form>
在被传递给客户端之前将被改写成
<form name="testform" action="/xxx">
<input type="hidden" name="jsessionid" value="ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764">
<input type="text">
</form>
q种技术现在已较少应用Q笔者接触过的很古老的iPlanet6(SunONE应用服务器的前n)׃用了q种技术?br>实际上这U技术可以简单的用对action应用URL重写来代ѝ?/p>

在谈论session机制的时候,常常听到q样一U误解“只要关闭浏览器Qsession消׃”。其实可以想象一下会(x)员卡的例子,除非֮d 对店家提出销卡,否则店家l对不会(x)L删除֮的资料。对session来说也是一L(fng)Q除非程序通知服务器删除一个sessionQ否则服务器?x)一直保 留,E序一般都是在用户做log off的时候发个指令去删除session。然而浏览器从来不会(x)d在关闭之前通知服务器它?yu)要关闭Q因此服务器Ҏ(gu)不会(x)有机?x)知道浏览器已经关闭Q之所 以会(x)有这U错觉,是大部分session机制都用会(x)话cookie来保存session idQ而关闭浏览器后这个session id消׃Q再ơ连接服务器时也无法找到原来的session。如果服务器讄的cookie被保存到盘上,或者用某U手D|写浏览器发出? HTTPh_(d)把原来的session id发送给服务器,则再ơ打开览器仍然能够找到原来的session?/p>

恰恰是由于关闭浏览器不会(x)Dsession被删除,q服务器ؓ(f)seesion讄了一个失效时_(d)当距dL(fng)上一ơ用session的时间超q这个失效时间时Q服务器可以认为客L(fng)已经停止了活动,才会(x)把session删除以节省存储空间?/p>

五、理解javax.servlet.http.HttpSession
HttpSession是Javaq_对session机制的实现规范,因ؓ(f)它仅仅是个接口,具体到每个web应用服务器的提供商,除了对规范支持之外,仍然?x)有一些规范里没有规定的细微差异。这里我们以BEA的Weblogic Server8.1作ؓ(f)例子来演C?/p>

首先QW(xu)eblogic Server提供了一pd的参数来控制它的HttpSession的实玎ͼ包括使用cookie的开关选项Q用URL重写的开关选项Qsession? 久化的设|,session失效旉的设|,以及(qing)针对cookie的各U设|,比如讄cookie的名字、\径、域Qcookie的生存时间等?/p>

一般情况下Qsession都是存储在内存里Q当服务器进E被停止或者重启的时候,内存里的session也会(x)被清I,如果讄了session? 持久化特性,服务器就?x)把session保存到硬盘上Q当服务器进E重新启动或q些信息能够被再次使用QW(xu)eblogic Server支持的持久性方式包括文件、数据库、客L(fng)cookie保存和复制?/p>

复制严格说来不算持久化保存,因ؓ(f)session实际上还是保存在内存里,不过同样的信息被复制到各个cluster内的服务器进E中Q这样即使某个服务器q程停止工作也仍然可以从其他q程中取得session?/p>

cookie生存旉的设|则?x)?jing)响浏览器生成的cookie是否是一个会(x)话cookie。默认是使用?x)话cookie。有兴趣的可以用它来试验我们在第四节里提到的那个误解?/p>

cookie的\径对于web应用E序来说是一个非帔R要的选项QW(xu)eblogic Server对这个选项的默认处理方式得它与其他服务器有明昄区别。后面我们会(x)专题讨论?/p>

关于session的设|参考[5] http://e-docs.bea.com/wls/docs70/webapp/weblogic_xml.html#1036869

六、HttpSession常见问题
Q在本小节中session的含义ؓ(f)⑤和⑥的混合Q?/p>


1、session在何时被创徏
一个常见的误解是以为session在有客户端访问时p创徏Q然而事实是直到某server端程 序调用HttpServletRequest.getSession(true)q样的语句时才被创徏Q注意如果JSP没有昄的? <%@page session="false"%> 关闭sessionQ则JSP文g在编译成Servlet时将?x)自动加上这样一条语句HttpSession session = HttpServletRequest.getSession(true);q也是JSP中隐含的session对象的来历?/p>

׃session?x)消耗内存资源,因此Q如果不打算使用sessionQ应该在所有的JSP中关闭它?/p>

2、session何时被删?br>l合前面的讨论,session在下列情况下被删除a.E序调用HttpSession.invalidate();或b.距离上一ơ收到客L(fng)发送的session id旉间隔过了session的超时设|?或c.服务器进E被停止Q非持久sessionQ?/p>

3、如何做到在览器关闭时删除session
严格的讲Q做不到q一炏V可以做一点努力的办法是在所有的客户端页面里使用javascript代码window.oncolose来监视浏览器的关闭动作,然后向服务器发送一个请求来删除session。但是对于浏览器崩溃或者强行杀死进E这些非常规手段仍然无能为力?/p>

4、有个HttpSessionListener是怎么回事
你可以创L(fng)listenerȝ控session的创建和销毁事Ӟ使得 在发生这L(fng)事g时你可以做一些相应的工作。注意是session的创建和销毁动作触发listenerQ而不是相反。类似的与HttpSession? 关的listenerq有HttpSessionBindingListenerQHttpSessionActivationListener? HttpSessionAttributeListener?/p>

5、存攑֜session中的对象必须是可序列化的?br>不是必需的。要求对象可序列化只是ؓ(f)了session能够在集中被复制或者能够持? 保存或者在必要时server能够暂时把session交换出内存。在Weblogic Server的session中放|一个不可序列化的对象在控制C?x)收C个警告。我所用过的某个iPlanet版本如果session中有不可序列? 的对象,在session销毁时?x)有一个ExceptionQ很奇怪?/p>

6、如何才能正的应付客户端禁止cookie的可能?br>Ҏ(gu)有的URL使用URL重写Q包括超链接Qform的actionQ和重定向的URLQ具体做法参见[6]
http://e-docs.bea.com/wls/docs70/webapp/sessions.html#100770

7、开两个览器窗口访问应用程序会(x)使用同一个sessionq是不同的session
参见W三节对cookie的讨论,对session来说是只认id不认人,因此不同的浏览器Q不同的H口打开方式以及(qing)不同的cookie存储方式都会(x)对这个问题的{案有媄(jing)响?/p>

8、如何防止用h开两个览器窗口操作导致的session混ؕ
q个问题与防止表单多ơ提交是cM的,可以通过讄客户端的令牌来解冟? 是在服务器每次生成一个不同的idq回l客L(fng)Q同时保存在session里,客户端提交表单时必须把这个id也返回服务器Q程序首先比较返回的id? 保存在session里的值是否一_(d)如果不一致则说明本次操作已经被提交过了。可以参看《J2EE核心模式》关于表C层模式的部分。需要注意的是对于 用javascript window.open打开的窗口,一般不讄q个idQ或者用单独的idQ以防主H口无法操作Q徏议不要再window.open打开的窗口里做修? 操作Q这样就可以不用讄?/p>

9、ؓ(f)什么在Weblogic Server中改变session的值后要重新调用一ơsession.setValue
做这个动作主要是Z在集环境中提示Weblogic Server session中的值发生了改变Q需要向其他服务器进E复制新的session倹{?/p>

10、ؓ(f)什么session不见?br>排除session正常失效的因素之外,服务器本w的可能性应该是微乎其微的,虽然W者在 iPlanet6SP1加若q补丁的Solaris版本上倒也遇到q;览器插件的可能性次之,W者也遇到q?721插g造成的问题;理论上防火墙或者代 理服务器在cookie处理上也有可能会(x)出现问题?br>出现q一问题的大部分原因都是E序的错误,最常见的就是在一个应用程序中去访问另外一个应用程序。我们在下一节讨个问题?/p>

七、跨应用E序的session׃n

常常有这L(fng)情况Q一个大目被分割成若干项目开发,Z能够互不q扰Q要 求每个小目作ؓ(f)一个单独的web应用E序开发,可是C最后突然发现某几个项目之间需要共享一些信息,或者想使用session来实现SSO (single sign on)Q在session中保存login的用户信息,最自然的要求是应用E序间能够访问彼此的session?/p>

然而按照Servlet规范Qsession的作用范围应该仅仅限于当前应用程序下Q不同的应用E序之间是不能够互相讉KҎ(gu)的session的? 各个应用服务器从实际效果上都遵守了这一规范Q但是实现的l节却可能各有不同,因此解决跨应用程序session׃n的方法也各不相同?/p>

首先来看一下Tomcat是如何实现web应用E序之间session的隔ȝQ从Tomcat讄的cookie路径来看Q它对不同的应用E序? |的cookie路径是不同的Q这样不同的应用E序所用的session id是不同的Q因此即使在同一个浏览器H口里访问不同的应用E序Q发送给服务器的session id也可以是不同的?br>

Ҏ(gu)q个Ҏ(gu),我们可以推测Tomcat中session的内存结构大致如下?br>

W者以前用q的iPlanet也采用的是同L(fng)方式Q估计SunONE与iPlanet之间不会(x)有太大的差别。对于这U方式的服务器,解决的思\? 单,实际实行h也不难。要么让所有的应用E序׃n一个session idQ要么让应用E序能够获得其他应用E序的session id?/p>

iPlanet中有一U很单的Ҏ(gu)来实现共享一个session idQ那是把各个应用程序的cookie路径都设?Q实际上应该?NASAppQ对于应用程序来讲它的作用相当于根)(j)?br><session-info>
<path>/NASApp</path>
</session-info>

需要注意的是,操作׃n的session应该遵@一些编E约定,比如在session attribute名字的前面加上应用程序的前缀Q得setAttribute("name", "neo")变成setAttribute("app1.name", "neo")Q以防止命名I间冲突Q导致互相覆盖?/p>


在Tomcat中则没有q么方便的选择。在Tomcat版本3上,我们q可以有一些手D|׃nsession。对于版?以上? TomcatQ目前笔者尚未发现简单的办法。只能借助于第三方的力量,比如使用文g、数据库、JMS或者客L(fng)cookieQURL参数或者隐藏字D늭? Dc(din)?/p>

我们再看一下Weblogic Server是如何处理session的?br>

从截屏画面上可以看到Weblogic ServerҎ(gu)有的应用E序讄的cookie的\径都?Q这是不是意味着在Weblogic Server中默认的可以共享session了呢Q然而一个小实验卛_证明即不同的应用程序用的是同一个sessionQ各个应用程序仍然只能访? 自己所讄的那些属性。这说明Weblogic Server中的session的内存结构可能如?br>

对于q样一U结构,在session机制本n上来解决session׃n的问题应该是不可能的了。除了借助于第三方的力量,比如使用文g、数据库? JMS或者客L(fng)cookieQURL参数或者隐藏字D늭手段Q还有一U较为方便的做法Q就是把一个应用程序的session攑ֈ ServletContext中,q样另外一个应用程序就可以从ServletContext中取得前一个应用程序的引用。示例代码如下,

应用E序A
context.setAttribute("appA", session);

应用E序B
contextA = context.getContext("/appA");
HttpSession sessionA = (HttpSession)contextA.getAttribute("appA");

值得注意的是q种用法不可ULQ因为根据ServletContext的JavaDocQ应用服务器可以处于安全的原因对于context.getContext("/appA");q回I|以上做法在Weblogic Server 8.1中通过?/p>

那么Weblogic ServerZ么要把所有的应用E序的cookie路径都设?呢?原来是ؓ(f)了SSOQ凡是共享这个session的应用程序都可以׃n认证的信息。一 个简单的实验可以证明这一点,修改首先d的那个应用程序的描述Wweblogic.xmlQ把cookie路径修改?appA讉K另外一个应用程? ?x)重新要求登录,即是反q来Q先讉Kcookie路径?的应用程序,再访问修改过路径的这个,虽然不再提示dQ但是登录的用户信息也会(x)丢失。注意做 q个实验时认证方式应该用FORMQ因为浏览器和web服务器对basic认证方式有其他的处理方式Q第二次h的认证不是通过session来实? 的。具体请参看[7] secion 14.8 AuthorizationQ你可以修改所附的CZE序来做q些试验?/p>

八、ȝ
session机制本nq不复杂Q然而其实现和配|上的灵zL却使得具体情况复杂多变。这也要求我们不能把仅仅某一ơ的l验或者某一个浏览器Q服务器的经验当作普遍适用的经验,而是始终需要具体情况具体分析?/p>

关于作者:(x)
郎云鹏(dev2dev ID: hippiewolfQ,软g工程师,从事J2EE开?br>?sh)子邮gQlangyunpeng@yahoo.com.cn
地址Q大qY件园?1L(fng)技大厦A座大q博涵咨询服务有限公?/p>

参考文档:(x)
[1] Preliminary Specification http://wp.netscape.com/newsref/std/cookie_spec.html
[2] RFC2109 http://www.rfc-editor.org/rfc/rfc2109.txt
[3] RFC2965 http://www.rfc-editor.org/rfc/rfc2965.txt
[4] The Unofficial Cookie FAQ http://www.cookiecentral.com/faq/
[5] http://e-docs.bea.com/wls/docs70/webapp/weblogic_xml.html#1036869
[6] http://e-docs.bea.com/wls/docs70/webapp/sessions.html#100770
[7] RFC2616 http://www.rfc-editor.org/rfc/rfc2616.txt



javaGrowing 2006-01-04 22:22 发表评论
]]>
Java 理论与实? 用弱引用堵住内存泄漏http://www.tkk7.com/juhongtao/archive/2006/01/04/26597.htmljavaGrowingjavaGrowingWed, 04 Jan 2006 10:35:00 GMThttp://www.tkk7.com/juhongtao/archive/2006/01/04/26597.htmlhttp://www.tkk7.com/juhongtao/comments/26597.htmlhttp://www.tkk7.com/juhongtao/archive/2006/01/04/26597.html#Feedback0http://www.tkk7.com/juhongtao/comments/commentRss/26597.htmlhttp://www.tkk7.com/juhongtao/services/trackbacks/26597.html  虽然? Java?语言~写的程序在理论上是不会(x)出现“内存泄漏”的Q但是有时对象在不再作ؓ(f)E序的逻辑状态的一部分之后仍然不被垃圾攉。本月,负责保障应用E序健康的工E师 Brian Goetz 探讨了无意识的对象保留的常见原因Qƈ展示了如何用弱引用堵住泄漏?
  要让垃圾攉QGCQ回收程序不再用的对象Q对象的逻辑 生命周期Q应用程序用它的时_(d)(j)和对该对象拥有的引用的实? 生命周期必须是相同的。在大多数时候,好的软g工程技术保证这是自动实现的Q不用我们对对象生命周期问题pq多心思。但是偶?dng)我们?x)创徏一个引用,它在 内存中包含对象的旉比我们预期的要长得多Q这U情늧为无意识的对象保留(unintentional object retentionQ?

  全局 Map 造成的内存泄?/strong>

   无意识对象保留最常见的原因是使用 Map 元数据与(f)时对象(transient objectQ相兌。假定一个对象具有中{生命周期,比分配它的那个方法调用的生命周期长,但是比应用程序的生命周期短,如客h的套接字q接。需要将 一些元数据与这个套接字兌Q如生成q接的用L(fng)标识。在创徏 Socket 时是不知道这些信息的Qƈ且不能将数据d?Socket 对象上,因ؓ(f)不能控制 Socket cL者它的子cR这Ӟ典型的方法就是在一个全局 Map 中存储这些信息,如清?1 中的 SocketManager cLC:(x)

  清单 1. 使用一个全局 Map 元数据兌C个对?/p>

public class SocketManager {
    private Map<Socket,User> m = new HashMap<Socket,User>();
   
    public void setUser(Socket s, User u) {
        m.put(s, u);
    }
    public User getUser(Socket s) {
        return m.get(s);
    }
    public void removeUser(Socket s) {
        m.remove(s);
    }
}

SocketManager socketManager;
...
socketManager.setUser(socket, user);

  q种Ҏ(gu)的问题是元数据的生命周期需要与套接字的生命周期挂钩Q但是除非准地知道什么时候程序不再需要这个套接字QƈC? Map 中删除相应的映射Q否则,Socket ?User 对象会(x)永远留在 Map 中,q远过响应了请求和关闭套接字的旉。这?x)阻? Socket ?User 对象被垃圾收集,即应用E序不会(x)再用它们。这些对象留下来不受控制Q很Ҏ(gu)造成E序在长旉q行后内存爆满。除了最单的情况Q在几乎所有情况下扑և什么时? Socket 不再被程序用是一件很?ch)h和容易出错的dQ需要h工对内存q行理?

  扑և内存泄漏

   E序有内存泄漏的W一个迹象通常是它抛出一? OutOfMemoryErrorQ或者因为频J的垃圾攉而表现出p糕的性能。幸q的是,垃圾攉可以提供能够用来诊断内存泄漏的大量信息。如果以 -verbose:gc 或?-Xloggc 选项调用 JVMQ那么每?GC q行时在控制C或者日志文件中?x)打印出一个诊断信息,包括它所p的时间、当前堆使用情况以及(qing)恢复了多内存。记?GC 使用情况q不hq扰性,因此如果需要分析内存问题或者调优垃圾收集器Q在生环境中默认启?GC 日志是值得的?

   有工具可以利?GC 日志输出q以囑Ş方式它昄出来QJTune 是q样的一U工P请参?参考资料)(j)。观?GC 之后堆大的图,可以看到E序内存使用的趋ѝ对于大多数E序来说Q可以将内存使用分ؓ(f)两部分:(x)baseline 使用?current load 使用。对于服务器应用E序Qbaseline 使用是应用E序在没有Q何负荗但是已l准备好接受h时的内存使用Qcurrent load 使用是在处理hq程中用的、但是在h处理完成后会(x)释放的内存。只要负荷大体上是恒定的Q应用程序通常?x)很快达C个稳定的内存使用水^。如果在应用 E序已经完成了其初始化ƈ且负h有增加的情况下,内存使用持箋增加Q那么程序就可能在处理前面的h时保留了生成的对象?

  清单 2 展示了一个有内存泄漏的程序。MapLeaker 在线E池中处理Q务,q在一? Map 中记录每一Q务的状态。不q的是,在Q务完成后它不?x)删除那一,因此状态项和Q务对象(以及(qing)它们的内部状态)(j)?x)不断地U篏?

  清单 2. hZ Map 的内存泄漏的E序

public class MapLeaker {
    public ExecutorService exec = Executors.newFixedThreadPool(5);
    public Map<Task, TaskStatus> taskStatus
        = Collections.synchronizedMap(new HashMap<Task, TaskStatus>());
    private Random random = new Random();

    private enum TaskStatus { NOT_STARTED, STARTED, FINISHED };

    private class Task implements Runnable {
        private int[] numbers = new int[random.nextInt(200)];

        public void run() {
            int[] temp = new int[random.nextInt(10000)];
            taskStatus.put(this, TaskStatus.STARTED);
            doSomeWork();
            taskStatus.put(this, TaskStatus.FINISHED);
        }
    }

    public Task newTask() {
        Task t = new Task();
        taskStatus.put(t, TaskStatus.NOT_STARTED);
        exec.execute(t);
        return t;
    }
}

  ?1 昄 MapLeaker GC 之后应用E序堆大随着旉的变化图。上升趋势是存在内存泄漏的警CZ受(在真实的应用E序中,坡度不会(x)q么大,但是在收集了_长时间的 GC 数据后,上升势通常?x)表现得很明显。)(j)


?1. 持箋上升的内存用趋?/p>

  信有了内存泄漏后,下一步就是找出哪U对象造成了这个问题。所有内存分析器都可以生成按照对象类q行分解的堆快照。有一些很好的商业堆分析工P但是扑և内存泄漏不一定要花钱买这些工? —?内置?hprof 工具也可完成q项工作。要使用 hprof q让它跟t内存用,需要以 -Xrunhprof:heap=sites 选项调用 JVM?

  清单 3 昄分解了应用程序内存用的 hprof 输出的相关部分。(hprof 工具在应用程序退出时Q或者用 kill -3 或在 Windows 中按 Ctrl+Break 时生成用分解。)(j)注意两次快照相比QMap.Entry、Task ?int[] 对象有了显著增加?

  请参?清单 3?/p>

  清单 4 展示?hprof 输出的另一部分Q给Z Map.Entry 对象的分配点的调用堆栈信息。这个输出告诉我们哪些调用链生成? Map.Entry 对象Qƈ带有一些程序分析,扑և内存泄漏来源一般来说是相当Ҏ(gu)的?

  清单 4. HPROF 输出Q显C?Map.Entry 对象的分配点

TRACE 300446:
 java.util.HashMap$Entry.<init>(<Unknown Source>:Unknown line)
 java.util.HashMap.addEntry(<Unknown Source>:Unknown line)
 java.util.HashMap.put(<Unknown Source>:Unknown line)
 java.util.Collections$SynchronizedMap.put(<Unknown Source>:Unknown line)
 com.quiotix.dummy.MapLeaker.newTask(MapLeaker.java:48)
 com.quiotix.dummy.MapLeaker.main(MapLeaker.java:64)

  弱引用来救援?/strong>

  SocketManager 的问题是 Socket-User 映射的生命周期应当与 Socket 的生命周期相匚wQ但是语a没有提供MҎ(gu)的方法实施这规则。这使得E序不得不用h工内存管理的老技术。幸q的是,? JDK 1.2 开始,垃圾攉器提供了一U声明这U对象生命周期依赖性的Ҏ(gu)Q这样垃圾收集器可以帮助我们防止这U内存泄? —?利用弱引用?

  弱引用是对一个对象(UCؓ(f) referentQ的引用的持有者。用弱引用后,可以l持? referent 的引用,而不?x)阻止它被垃圾收集。当垃圾攉器跟t堆的时候,如果对一个对象的引用只有弱引用,那么q个 referent ׃(x)成ؓ(f)垃圾攉的候选对象,像没有M剩余的引用一P而且所有剩余的弱引用都被清除。(只有弱引用的对象UCؓ(f)弱可?qing)(weakly reachableQ。)(j)

  WeakReference ?referent 是在构造时讄的,在没有被清除之前Q可以用 get() 获取它的倹{如果弱引用被清除了Q不是 referent 已经被垃圾收集了Q还是有用了 WeakReference.clear()Q,get() ?x)返?null。相应地Q在使用其结果之前,应当L(g)? get() 是否q回一个非 null |因ؓ(f) referent 最lL?x)被垃圾攉的?

  用一个普通的Q强Q引用拷贝一个对象引用时Q限?referent 的生命周期至与被拷贝的引用的生命周期一样长。如果不心Q那么它可能׃E序的生命周期一? —?如果一个对象放入一个全局集合中的话。另一斚wQ在创徏对一个对象的弱引用时Q完全没有扩? referent 的生命周期,只是在对象仍然存?gu)zȝ时候,保持另一U到辑֮的方法?

  弱引用对于构造弱集合最有用Q如那些在应用程序的其余部分使用对象期间存储关于q些对象的元数据的集? —?q就?SocketManager cL要做的工作。因是弱引用最常见的用法,W(xu)eakHashMap 也被d?JDK 1.2 的类库中Q它寚wQ而不是对|(j)使用弱引用。如果在一个普?HashMap 中用一个对象作为键Q那么这个对象在映射?Map 中删除之前不能被回收QW(xu)eakHashMap 使?zhn)可以用一个对象作?Map 键,同时不会(x)Lq个对象被垃圾收集。清?5 l出?WeakHashMap ?get() Ҏ(gu)的一U可能实玎ͼ它展CZ弱引用的使用Q?

  清单 5. WeakReference.get() 的一U可能实?/p>

public class WeakHashMap<K,V> implements Map<K,V> {

    private static class Entry<K,V> extends WeakReference<K>
      implements Map.Entry<K,V> {
        private V value;
        private final int hash;
        private Entry<K,V> next;
        ...
    }

    public V get(Object key) {
        int hash = getHash(key);
        Entry<K,V> e = getChain(hash);
        while (e != null) {
            K eKey= e.get();
            if (e.hash == hash && (key == eKey || key.equals(eKey)))
                return e.value;
            e = e.next;
        }
        return null;
    }

  调用 WeakReference.get() Ӟ它返回一个对 referent 的强引用Q如果它仍然存活的话Q,因此不需要担心映在 while 循环体中消失Q因为强引用?x)防止它被垃圾收集。WeakHashMap 的实现展CZ弱引用的一U常见用?—?一些内部对象扩?WeakReference。其原因在下面一节讨论引用队列时?x)得到解释?/p>

  在向 WeakHashMap 中添加映时Q请C映射可能?x)在以后“脱Z,因ؓ(f)键被垃圾攉了。在q种情况下,get() q回 nullQ这使得试 get() 的返回值是否ؓ(f) null 变得比^时更重要了?

  ?WeakHashMap 堵住泄漏

   ?SocketManager 中防止泄漏很Ҏ(gu)Q只要用 WeakHashMap 代替 HashMap p了,如清?6 所C。(如果 SocketManager 需要线E安全,那么可以?Collections.synchronizedMap() 包装 WeakHashMapQ。当映射的生命周期必M键的生命周期联系在一hQ可以用这U方法。不q,应当心不滥用这U技术,大多数时候还是应当? 普通的 HashMap 作ؓ(f) Map 的实现?

  清单 6. ?WeakHashMap 修复 SocketManager

public class SocketManager {
    private Map<Socket,User> m = new WeakHashMap<Socket,User>();
   
    public void setUser(Socket s, User u) {
        m.put(s, u);
    }
    public User getUser(Socket s) {
        return m.get(s);
    }
}

  引用队列

  WeakHashMap 用弱引用承蝲映射键,q得应用程序不再用键对象时它们可以被垃圾攉Qget() 实现可以Ҏ(gu) WeakReference.get() 是否q回 null 来区分死的映和zȝ映射。但是这只是防止 Map 的内存消耗在应用E序的生命周期中不断增加所需要做的工作的一半,q需要做一些工作以便在键对象被攉后从 Map 中删除死V否则,Map ?x)充满对应于死键的项。虽然这对于应用E序是不可见的,但是它仍然会(x)造成应用E序耗尽内存Q因为即佉K被收集了QMap.Entry 和值对象也不会(x)被收集?

  可以通过周期性地扫描 MapQ对每一个弱引用调用 get()Qƈ?get() q回 null 时删除那个映而消除死映射。但是如?Map 有许多活的项Q那么这U方法的效率很低。如果有一U方法可以在弱引用的 referent 被垃圾收集时发出通知好了,q就是引用队?的作用?

  引用队列是垃圾收集器向应用程序返回关于对象生命周期的信息的主要方法。弱引用有两个构造函敎ͼ(x)一个只? referent 作ؓ(f)参数Q另一个还取引用队列作为参数。如果用兌的引用队列创建弱引用Q在 referent 成ؓ(f) GC 候选对象时Q这个引用对象(不是 referentQ就在引用清除后加入 到引用队列中。之后,应用E序从引用队列提取引用ƈ了解到它?referent 已被攉Q因此可以进行相应的清理zdQ如L已不在弱集合中的对象的项。(引用队列提供了与 BlockingQueue 同样的出列模?—?polled、timed blocking ?untimed blocking。)(j)

  WeakHashMap 有一个名?expungeStaleEntries() 的私有方法,大多? Map 操作中会(x)调用它,它去掉引用队列中所有失效的引用Qƈ删除兌的映。清?7 展示? expungeStaleEntries() 的一U可能实现。用于存储键-值映的 Entry cd扩展?WeakReferenceQ因此当 expungeStaleEntries() 要求下一个失效的弱引用时Q它得到一?Entry。用引用队列代替定期扫描内容的方法来清理 Map 更有效,因ؓ(f)清理q程不会(x)触及(qing)zȝ,只有在有实际加入队列的引用时它才工作?

  清单 7. WeakHashMap.expungeStaleEntries() 的可能实?/p>

    private void expungeStaleEntries() {
 Entry<K,V> e;
        while ( (e = (Entry<K,V>) queue.poll()) != null) {
            int hash = e.hash;

            Entry<K,V> prev = getChain(hash);
            Entry<K,V> cur = prev;
            while (cur != null) {
                Entry<K,V> next = cur.next;
                if (cur == e) {
                    if (prev == e)
                        setChain(hash, next);
                    else
                        prev.next = next;
                    break;
                }
                prev = cur;
                cur = next;
            }
        }
    }

  l束?/strong>

  弱引用和弱集合是对堆q行理的强大工P使得应用E序可以使用更复杂的可及(qing)性方案,而不只是由普通(强)(j)引用所提供的“要么全部要么没有”可?qing)性。下个月Q我们将分析与弱引用有关的Y引用Q将分析在用弱引用和Y引用Ӟ垃圾攉器的行ؓ(f)?/p>

javaGrowing 2006-01-04 18:35 发表评论
]]>
Ant实践http://www.tkk7.com/juhongtao/archive/2005/12/29/25826.htmljavaGrowingjavaGrowingThu, 29 Dec 2005 01:16:00 GMThttp://www.tkk7.com/juhongtao/archive/2005/12/29/25826.htmlhttp://www.tkk7.com/juhongtao/comments/25826.htmlhttp://www.tkk7.com/juhongtao/archive/2005/12/29/25826.html#Feedback0http://www.tkk7.com/juhongtao/comments/commentRss/25826.htmlhttp://www.tkk7.com/juhongtao/services/trackbacks/25826.html
1. Ant是什么?
2. 安装Ant
3. q行Ant
4. ~写build.xml
5. 内置task(internet)
6. EAR task(internet)
7. WAR task(internet)
8. JUnit task(internet)

--------------------------------------------------------------------------------

1.Ant是什么?

--------------------------------------------------------------------------------

Ant是一U基于Java的build工具。理Z来说Q它有些cM于(UnixQC中的make Q但没有make的缺陗?br>
? 然我们已l有了make, gnumake, nmake, jam以及(qing)其他的build工具Z么还要要一U新的build工具呢?因ؓ(f)Ant的原作者在多种(g)q_上开发Y件时Q无法忍受这些工L(fng)限制和不 ѝ类gmake的工h质上是基于shellQ语aQ的Q他们计依赖关p,然后执行命o(h)Q这些命令与你在命o(h)行敲的命令没太大区别Q。这意味着? 可以很容易地通过使用OSҎ(gu)的或~写新的Q命令)(j)E序扩展该工P然而,q也意味着你将自己限制在了特定的OSQ或特定的OScd上,如Unix?br>
Makefile也很可恶。Q何用过他们的h都碰到过可恶的tab问题。Ant的原作者经常这样问自己Q“是否我的命令不执行只是因ؓ(f)在我的tab前有一个空|Q!”。类gjam的工具很好地处理了这c问题,但是Q用P(j)必须C和用一U新的格式?br>
Ant ׃同了。与Zshell命o(h)的扩展模式不同,Ant用Java的类来扩展。(用户Q不必编写shell命o(h)Q配|文件是ZXML的,通过调用 target?wi),可执行各种task。每个task由实C一个实C特定Task接口的对象来q行。(如果你对Ant一Ҏ(gu)念都没有的话Q可能看不懂 q一节,没有关系Q后面会(x)对target,task做详l的介绍。你如果没有太多的时间甚臛_以略q这一节,然后再回来浏览一下这里的介绍Q那时你׃(x)? 懂了。同P如果你对make之类的工具不熟?zhn)也没关系Q下面的介绍Ҏ(gu)不会(x)用到make中的概念。)(j)

必须承认Q这样做Q在构? shell命o(h)时会(x)失去一些特有的表达能力。如`find . -name foo -exec rm {}`Q但却给了你跨^台的能力Q你可以在Q何地方工作。如果你真的需要执行一些shell命o(h)QAnt有一?lt;exec> taskQ这个task允许执行特定OS上的命o(h)?br>

2.安装Ant

--------------------------------------------------------------------------------

׃Ant是一个Open Source的YӞ所以有两种安装Ant的方式,一U是用已~译好的binary 文g安装AntQ另一U是用源代码自己build Ant?br>
binary 形式的Ant可以从http://jakarta.apache.org/builds/ant/release/v1.4.1/bin下蝲。如果你希望 你能自己~译AntQ则可从 http://jakarta.apache.org/builds/ant/release/v1.4.1/src。注意所列出的连接都是最新发行版? Ant。如果你d此文Ӟ发现已经有了更新的版本,那么L(fng)新版本。如果你是一个疯狂的技术追求者,你也可以从Ant CVS repository下蝲最新版本的Ant?br>
pȝ需?br>
要想自己build Ant。你需要一个JAXP兼容的XML解析器(parserQ放在你的CLASSPATHpȝ变量中?br>
binary 形式的Ant包括最新版的Apache Crimson XML解析器。你可以从http://java.sun.com/xml/ 得到更多的关于JAXP的信息。如果你希望使用其他的JAXP兼容的解析器。你要从Ant的lib目录中删掉jaxp.jar以及(qing) crimson.jar。然后你可将你心q解析器的jar文g攑ֈAnt的lib目录中或攑֜你的CLASSPATHpȝ变量中?br>
对于当前版本的AntQ需要你的系l中有JDKQ?.1版或更高。未来的Ant版本?x)要求用JDK 1.2或更高版本?br>
安装Ant

binary 版的Ant包括三个目录:bin, docs 和lib。只有bin和lib目录是运行Ant所需的。要惛_装AntQ选择一个目录ƈ发行版的文件拷贝到该目录下。这个目录被UCANT_HOME?br>
在你q行Ant之前需要做一些配|工作?br>
bin目录加入PATH环境变量?
讑֮ANT_HOME环境变量Q指向你安装Ant的目录。在一些OS上,Ant的脚本可以猜ANT_HOMEQUnix和W(xu)indos NT/2000Q-但最好不要依赖这一Ҏ(gu)?
可选地Q设定JAVA_HOME环境变量Q参考下面的高񔞮节Q,该变量应该指向你安装JDK的目录?br>
注意Q不要将Ant的ant.jar文g攑ֈJDK/JRE的lib/ext目录下。Ant是个应用E序Q而lib/ext目录是ؓ(f)JDK扩展使用的(如JCEQJSSE扩展Q。而且通过扩展装入的类?x)有安全斚w的限制?br>
可选Task

Ant 支持一些可选task。一个可选task一般需要额外的库才能工作。可选task与Ant的内|task分开Q单独打包。这个可选包可以从你下蝲Ant? 同一个地方下载。目前包含可选task的jar文g名叫jakarta-ant-1.4.1-optional.jar。这个jar文g应该攑ֈAnt? 装目录的lib目录下?br>
每个可选task所需的外部库可参看依赖库节。这些外部库可以攑ֈAnt的lib目录下,q样Antp自动装入Q或者将其放入环境变量中?br>
Windows

假定Ant安装在c:\ant\目录下。下面是讑֮环境的命令:(x)

set ANT_HOME=c:\ant
set JAVA_HOME=c:\jdk1.2.2
set PATH=%PATH%;%ANT_HOME%\bin
Unix (bash)

假定Ant安装?usr/local/ant目录下。下面是讑֮环境的命令:(x)

export ANT_HOME=/usr/local/ant
export JAVA_HOME=/usr/local/jdk-1.2.2
export PATH=${PATH}:${ANT_HOME}/bin


要想q行Ant必须使用很多的变量。你臛_参考需要下面的内容Q?br>
Ant的CLASSPATH必须包含ant.jar以及(qing)你所选的JAXP兼容的XML解析器的jar文g?
? 你需要JDK的功能(如javac或rmic taskQ时Q对于JDK 1.1QJDK的classes.zip文g必须攑օCLASSPATH中;对于JDK 1.2或JDK 1.3Q则必须加入tools.jar。如果设定了正确的JAVA_HOME环境变量QAnt所带的脚本Q在bin目录下,?x)自动加入所需的JDKcR?
当你执行特定q_的程序(如exec task或cvs taskQ时Q必设定ant.home属性指向Ant的安装目录。同PAnt所带的脚本利用ANT_HOME环境变量自动讄该属性?
Building Ant

要想从源代码build AntQ你要先安装Ant源代码发行版或从CVS中checkout jakarta-ant模块?br>
安装好源代码后,q入安装目录?br>
讑֮JAVA_HOME环境变量指向JDK的安装目录。要想知道怎么做请参看安装Ant节?br>
? 保你已下载了M辅助jar文gQ以便build你所感兴的task。这些jar文g可以攑֜CLASSPATH中,也可以放? lib/optional目录下。参看依赖库节可知不同的task需要那些jar文g。注意这些jar文g只是用作build Ant之用。要惌行AntQ你q要像安装Ant节中所做的那样讑֮q些jar文g?br>
现在你可以build Ant了:(x)

build -Ddist.dir=<directory_to_contain_Ant_distribution> dist (Windows)
build.sh -Ddist.dir=<directory_to_contain_Ant_distribution> dist (Unix)

q样可你指定的目录中创Z个binary版本?br>
上面的命令执行下面的动作Q?br>
如果有必要可以bootstrap Ant的代码。bootstrap 包括手工~辑一些Ant代码以便q行Ant。bootstrap 用于下面的build步骤?
向build脚本传递参C调用bootstrap Ant。参数定义了Ant的属性值ƈ指定了Ant自己的build.xml文g?dist" target?br>
大多数情况下Q你不必直接bootstrap AntQ因为build脚本Z完成q一切。运行bootstrap.bat (Windows) ?bootstrap.sh (UNIX) 可以build一个新的bootstrap版Ant?


如果你希望将Ant安装到ANT_HOME目录下,你可以用:(x)

build install (Windows)
build.sh install (Unix)

如果你希望蟩q冗长的Javadoc步骤Q可以用Q?br>
build install-lite (Windows)
build.sh install-lite (Unix)

q样只?x)安装bin和lib目录?br>
注意install和install-lite都会(x)覆盖ANT_HOME中的当前Ant版本?br>
依赖?

如果你需要执行特定的taskQ你需要将对应的库攑օCLASSPATH或放到Ant安装目录的lib目录下。注意用mapper时只需要一个regexp库。同Ӟ你也要安装Ant的可选jar包,它包含了task的定义。参考上面的安装Ant节?br>
Jar Name Needed For Available At
An XSL transformer like Xalan or XSL:P style task http://xml.apache.org/xalan-j/index.html or http://www.clc-marketing.com/xslp/
jakarta-regexp-1.2.jar regexp type with mappers jakarta.apache.org/regexp/
jakarta-oro-2.0.1.jar regexp type with mappers and the perforce tasks jakarta.apache.org/oro/
junit.jar junit tasks www.junit.org
stylebook.jar stylebook task CVS repository of xml.apache.org
testlet.jar test task java.apache.org/framework
antlr.jar antlr task www.antlr.org
bsf.jar script task oss.software.ibm.com/developerworks/projects/bsf
netrexx.jar netrexx task www2.hursley.ibm.com/netrexx
rhino.jar javascript with script task www.mozilla.org
jpython.jar python with script task www.jpython.org
netcomponents.jar ftp and telnet tasks www.savarese.org/oro/downloads

3.q行Ant

--------------------------------------------------------------------------------

q行Ant非常单,当你正确地安装Ant后,只要输入ant可以了?br>
? 有指定Q何参数时QAnt?x)在当前目录下查询build.xml文g。如果找Cq该文件作为buildfile。如果你?-find 选项。Ant׃(x)在上U目录中LbuildfileQ直臛_达文件系l的栏V要惌Ant使用其他的buildfileQ可以用参数 -buildfile fileQ这里file指定了你想用的buildfile?br>
你也可以讑֮一些属性,以覆盖buildfile? 指定的属性|参看property taskQ。可以用 -Dproperty=value 选项Q这里property是指属性的名称Q而value则是指属性的倹{也可以用这U办法来指定一些环境变量的倹{你也可以用property task来存取环境变量。只要将 -DMYVAR=%MYVAR% (Windows) ?-DMYVAR=$MYVAR (Unix) 传递给Ant Q你可以在你的buildfile中用${MYVAR}来存取这些环境变量?br>
q有两个选项 -quiteQ告诉Antq行时只输出量的必要信息。?-verboseQ告诉Antq行时要输出更多的信息?br>
可以指定执行一个或多个target。当省略targetӞAnt使用标签<project>的default属性所指定的target?br>
如果有的话,-projecthelp 选项输出目的描qC息和目target的列表。先列出那些有描q的Q然后是没有描述的target?br>
命o(h)行选项ȝQ?br>
ant [options] [target [target2 [target3] ...]]
Options:
-help print this message
-projecthelp print project help information
-version print the version information and exit
-quiet be extra quiet
-verbose be extra verbose
-debug print debugging information
-emacs produce logging information without adornments
-logfile file use given file for log output
-logger classname the class that is to perform logging
-listener classname add an instance of class as a project listener
-buildfile file use specified buildfile
-find file search for buildfile towards the root of the filesystem and use the first one found
-Dproperty=value set property to value
例子

ant

使用当前目录下的build.xmlq行AntQ执行缺省的target?br>
ant -buildfile test.xml

使用当前目录下的test.xmlq行AntQ执行缺省的target?br>
ant -buildfile test.xml dist

使用当前目录下的test.xmlq行AntQ执行一个叫做dist的target?br>
ant -buildfile test.xml -Dbuild=build/classes dist

使用当前目录下的test.xmlq行AntQ执行一个叫做dist的targetQƈ讑֮build属性的gؓ(f)build/classes?br>
文g

在Unix 上,Ant的执行脚本在做Q何事之前都会(x)sourceQ读q计|(j)~/.antrc 文gQ在Windows上,Ant的批处理文g?x)在开始时调用%HOME%\antrc_pre.batQ在l束时调?HOME%\ antrc_post.bat。你可以用这些文仉|或取消一些只有在q行Ant时才需要的环境变量。看下面的例子?br>
环境变量

包裹脚本Qwrapper scriptsQ用下面的环境变量Q如果有的话Q:(x)

JAVACMD Java可执行文件的l对路径。用q个值可以指定一个不同于JAVA_HOME/bin/java(.exe)的JVM?
ANT_OPTS 传递给JVM的命令行变量Q例如,你可以定义属性或讑֮Java堆的最大?br>
手工q行Ant

如果你自己动手安装(DIYQAntQ你可以用下面的命o(h)启动Ant:

java -Dant.home=c:\ant org.apache.tools.ant.Main [options] [target]

q个命o(h)与前面的ant命o(h)一栗选项和target也和用ant命o(h)时一栗这个例子假定你的CLASSPATH包含:

ant.jar

jars/classes for your XML parser

the JDK's required jar/zip files

4.~写build.xml

--------------------------------------------------------------------------------

Ant的buildfile是用XML写的。每个buildfile含有一个project?br>
buildfile中每个task元素可以有一个id属性,可以用这个id值引用指定的d。这个值必L唯一的。(详情请参考下面的Task节Q?br>
Projects

project有下面的属性:(x)

Attribute Description Required
name 目名称. No
default 当没有指定target时用的~省target Yes
basedir 用于计算所有其他\径的\径。该属性可以被basedir property覆盖。当覆盖Ӟ该属性被忽略。如果属性和basedir property都没有设定,׃用buildfile文g的父目录?No

目的描qC一个顶U的<description>元素的Ş式出玎ͼ参看description节Q?br>
一个项目可以定义一个或多个target。一个target是一pd你想要执行的。执行AntӞ你可以选择执行那个target。当没有l定targetӞ使用project的default属性所定的target?br>
Targets

一个target可以依赖于其他的target。例如,你可能会(x)有一个target用于~译E序Q一个target用于生成可执行文件。你在生成可执行文g之前必须先编译通过Q所以生成可执行文g的target依赖于编译target。Ant?x)处理这U依赖关pR?br>
然而,应当注意刎ͼAnt的depends属性只指定了target应该被执行的序Q如果被依赖的target无法q行Q这Udepends对于指定了依赖关pȝtarget没有媄(jing)响?br>
Ant?x)依照depends属性中target出现的顺序(从左到右Q依ơ执行每个target。然而,要记住的是只要某个target依赖于一个targetQ后者就?x)被先执行?br>
<target name="A"/>
<target name="B" depends="A"/>
<target name="C" depends="B"/>
<target name="D" depends="C,B,A"/>

假定我们要执行target D。从它的依赖属性来看,你可能认为先执行CQ然后BQ最后A被执行。错了,C依赖于BQB依赖于AQ所以先执行AQ然后BQ然后CQ最后D被执行?br>
一个target只能被执行一ơ,x有多个target依赖于它Q看上面的例子)(j)?br>
? 果(或如果不Q某些属性被讑֮Q才执行某个target。这P允许Ҏ(gu)pȝ的状态(java version, OS, 命o(h)行属性定义等{)(j)来更好地控制build的过E。要惌一个targetq样做,你就应该在target元素中,加入ifQ或unlessQ属性,? 上target因该有所判断的属性。例如:(x)

<target name="build-module-A" if="module-A-present"/>
<target name="build-own-fake-module-A" unless="module-A-present"/>

如果没有if或unless属性,targetM(x)被执行?br>
可选的description属性可用来提供关于target的一行描qͼq些描述可由-projecthelp命o(h)行选项输出?br>
你的tstamp task在一个所谓的初始化target是很好的做法Q其他的target依赖q个初始化target。要保初始化target是出现在其他target依赖表中的第一个target。在本手册中大多数的初始化target的名字是"init"?br>
target有下面的属性:(x)

Attribute Description Required
name target的名?Yes
depends 用逗号分隔的target的名字列表,也就是依赖表?No
if 执行target所需要设定的属性名?No
unless 执行target需要清除设定的属性名?No
description 关于target功能的简短描q?No

Tasks

一个task是一D可执行的代码?br>
一个task可以有多个属性(如果你愿意的话,可以其UC为变量)(j)。属性只可能包含对property的引用。这些引用会(x)在task执行前被解析?br>
下面是Task的一般构造Ş式:(x)

<name attribute1="value1" attribute2="value2" ... />

q里name是task的名字,attributeN是属性名QvalueN是属性倹{?br>
有一套内|的Qbuilt-inQtaskQ以?qing)一些可选taskQ但你也可以~写自己的task?br>
所有的task都有一个task名字属性。Ant用属性值来产生日志信息?br>
可以ltask赋一个id属性:(x)

<taskname id="taskID" ... />

q里taskname是task的名字,而taskID是这个task的唯一标识W。通过q个标识W,你可以在脚本中引用相应的task。例如,在脚本中你可以这P(x)

<script ... >
task1.setFoo("bar");
</script>

讑֮某个task实例的foo属性。在另一个task中(用java~写Q,你可以利用下面的语句存取相应的实例?br>
project.getReference("task1").

注意1Q如果task1q没有运行,׃?x)被生效Q例如:(x)不设定属性)(j)Q如果你在随后配|它Q你所作的一切都?x)被覆盖?br>
注意2Q未来的Ant版本可能不会(x)兼容q里所提的属性,因ؓ(f)很有可能Ҏ(gu)没有task实例Q只有proxies?br>
Properties

一 个project可以有很多的properties。可以在buildfile中用property task来设定,或在Ant之外讑֮。一个property有一个名字和一个倹{property可用于task的属性倹{这是通过属性名攑֜"${" ?}"之间q放在属性值的位置来实现的。例如如果有一个property builddir的值是"build"Q这个property可用于属性|(x)${builddir}/classes。这个值就可被解析? build/classes?br>
内置属?br>
如果你用了<property> task 定义了所有的pȝ属性,Ant允许你用这些属性。例如,${os.name}对应操作pȝ的名字?br>
要想得到pȝ属性的列表可参考the Javadoc of System.getProperties?br>
除了Java的系l属性,Antq定义了一些自q内置属性:(x)
basedir project基目录的l对路径 (?lt;project>的basedir属性一??br> ant.file buildfile的绝对\径?br> ant.version Ant的版本?br> ant.project.name 当前执行的project的名字;?lt;project>的name属性设?
ant.java.version Ant(g)到的JVM的版本; 目前的值有"1.1", "1.2", "1.3" and "1.4".
   
例子

<project name="MyProject" default="dist" basedir=".">

<!-- set global properties for this build -->
<property name="src" value="."/>
<property name="build" value="build"/>
<property name="dist" value="dist"/>
   
<target name="init">
<!-- Create the time stamp -->
<tstamp/>
<!-- Create the build directory structure used by compile -->
<mkdir dir="${build}"/>
</target>
 
<target name="compile" depends="init">
<!-- Compile the java code from ${src} into ${build} -->
<javac srcdir="${src}" destdir="${build}"/>
</target>

<target name="dist" depends="compile">
<!-- Create the distribution directory -->
<mkdir dir="${dist}/lib"/>
<!-- Put everything in ${build} into the MyProject-${DSTAMP}.jar file -->
<jar jarfile="${dist}/lib/MyProject-${DSTAMP}.jar" basedir="${build}"/>
</target>

<target name="clean">
<!-- Delete the ${build} and ${dist} directory trees -->
<delete dir="${build}"/>
<delete dir="${dist}"/>
</target>

</project>

Token Filters

一个project可以有很多tokensQ这些tokens在文件拷贝时?x)被自动扩展Q这要求在支持这一行ؓ(f)的task中选择qo(h)拯功能。这一功能可用filter task在buildfile中设定?br>
? 然这很可能是一个有危害的行为,文g中的tokens必须采取@token@的Ş式,q里token是filter task中设定的token名。这Utoken语法与其他buildpȝ执行cMfiltering的语法相同,而且与大多数的编E和脚本语言以及(qing)文档p? lƈ不冲H,

注意Q如果在一个文件中发现了一个@token@形式的tokenQ但没有filter与这个token兌Q则不会(x)发生M事;因此Q没有{义方法-但只要你为token选择合适的名字Q就不会(x)产生问题?br>
警告Q如果你在拷贝binary文g时打开filtering功能Q你有可能破坏文件。这个功能只针对文本文g?br>
Path-like Structures
你可以用":"?;"作ؓ(f)分隔W,指定cMPATH和CLASSPATH的引用。Ant?x)把分隔W{换ؓ(f)当前pȝ所用的分隔W?br>
当需要指定类D\径的值时Q可以用嵌套元素。一般的形式?br>
<classpath>
<pathelement path="${classpath}"/>
<pathelement location="lib/helper.jar"/>
</classpath>
location属性指定了相对于project基目录的一个文件和目录Q而path属性接受逗号或分号分隔的一个位|列表。path属性一般用作预定义的\径-Q其他情况下Q应该用多个location属性?br>
为简zv见,classpath标签支持自己的path和location属性。所以:(x)

<classpath>
<pathelement path="${classpath}"/>
</classpath>
可以被简写作Q?br>
<classpath path="${classpath}"/>
也可通过<fileset>元素指定路径。构成一个fileset的多个文件加入path-like structure的顺序是未定的?br>
<classpath>
<pathelement path="${classpath}"/>
<fileset dir="lib">
<include name="**/*.jar"/>
</fileset>
<pathelement location="classes"/>
</classpath>
上面的例子构造了一个\径值包括:(x)${classpath}的\径,跟着lib目录下的所有jar文gQ接着是classes目录?br>
如果你想在多个task中用相同的path-like structureQ你可以?lt;path>元素定义他们Q与target同Q,然后通过id属性引用-Q参考Referencs例子?br>
path-like structure可能包括对另一个path-like structurede的引用(通过嵌套<path>元素Q:(x)

<path id="base.path">
<pathelement path="${classpath}"/>
<fileset dir="lib">
<include name="**/*.jar"/>
</fileset>
<pathelement location="classes"/>
</path>
<path id="tests.path">
<path refid="base.path"/>
<pathelement location="testclasses"/>
</path>

前面所提的关于<classpath>的简z写法对?lt;path>也是有效的,如:(x)

<path id="tests.path">
  <path refid="base.path"/>
<pathelement location="testclasses"/>
</path>
可写成:(x)

<path id="base.path" path="${classpath}"/>
命o(h)行变?br>
有些task可接受参敎ͼq将其传递给另一个进E。ؓ(f)了能在变量中包含I格字符Q可使用嵌套的arg元素?br>
Attribute Description Required
value 一个命令行变量Q可包含I格字符?只能用一?
line I格分隔的命令行变量列表?
file 作ؓ(f)命o(h)行变量的文g名;?x)被文g的绝对名替代?
path 一个作为单个命令行变量的path-like的字W串Q或作ؓ(f)分隔W,Ant?x)将其{变ؓ(f)特定q_的分隔符?

例子

<arg value="-l -a"/>
是一个含有空格的单个的命令行变量?br>
<arg line="-l -a"/>
是两个空格分隔的命o(h)行变量?br>
<arg path="/dir;/dir2:\dir3"/>
是一个命令行变量Q其值在DOSpȝ上ؓ(f)\dir;\dir2;\dir3Q在Unixpȝ上ؓ(f)/dir:/dir2:/dir3 ?br>
References

buildfile元素的id属性可用来引用q些元素。如果你需要一遍遍的复制相同的XML代码块,q一属性就很有用-Q如多次使用<classpath>l构?br>
下面的例子:(x)

<project ... >
<target ... >    
<rmic ...>      
<classpath>        
<pathelement location="lib/"/>        
<pathelement path="${java.class.path}/"/>        
<pathelement path="${additional.path}"/>      
</classpath>    
</rmic>  
</target>
<target ... >
<javac ...>
<classpath>
<pathelement location="lib/"/>
<pathelement path="${java.class.path}/"/>
<pathelement path="${additional.path}"/>
</classpath>
</javac>
</target>
</project>
可以写成如下形式Q?br>
<project ... >
<path id="project.class.path">  
<pathelement location="lib/"/>
<pathelement path="${java.class.path}/"/>  
<pathelement path="${additional.path}"/>
</path>
<target ... >
<rmic ...>
<classpath refid="project.class.path"/>
</rmic>
</target>
<target ... >
<javac ...>
<classpath refid="project.class.path"/>
</javac>
</target>
</project>
所有用PatternSets, FileSets ?path-like structures嵌套元素的task也接受这U类型的引用?

javaGrowing 2005-12-29 09:16 发表评论
]]>
用Java解决国际化问?/title><link>http://www.tkk7.com/juhongtao/archive/2005/12/14/23793.html</link><dc:creator>javaGrowing</dc:creator><author>javaGrowing</author><pubDate>Wed, 14 Dec 2005 02:57:00 GMT</pubDate><guid>http://www.tkk7.com/juhongtao/archive/2005/12/14/23793.html</guid><wfw:comment>http://www.tkk7.com/juhongtao/comments/23793.html</wfw:comment><comments>http://www.tkk7.com/juhongtao/archive/2005/12/14/23793.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.tkk7.com/juhongtao/comments/commentRss/23793.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/juhongtao/services/trackbacks/23793.html</trackback:ping><description><![CDATA[<table align="center" width="680"><tbody><tr><td align="center"><h2><font color="#0f3ccd">用Java解决国际化问?/font></h2> <br><b>首都lN大学信息学院 Ҏ(gu)v?/b> </td></tr><tr><td align="right">01-7-18 上午 09:11:22<br><hr color="#f46240" size="1" width="660"></td></tr></tbody></table><br> <table align="center" width="620"><tbody><tr><td class="a14">如果应用pȝ是面向多U语a的,~程时就不得不设法解军_际化问题Q包括操作界面的风格问题、提C和帮助语言的版本问题、界面定制个性化问题{?/td></tr></tbody></table><table align="center" width="620"><tbody><tr> <td class="a14"> ׃Java语言hq_无关、可UL性好{优点,q且提供了强大的cdQ所以Java语言可以辅助我们解决上述问题。Java语言本n采用双字节字W编 码,采用大汉字字W集Q这׃ؓ(f)解决国际化问题提供了很多方便。从设计角度来说Q只要把E序中与语言和文化有关的部分分离出来Q加上特D处理,可以部分解 军_际化问题。在界面风格的定制方面,我们把可以参数化的元素,如字体、颜色等Q存储在数据库里Q以便ؓ(f)用户提供友好的界面;如果某些部分包含无法参数? 的元素,那么我们可能不得不分别设计,通过有针Ҏ(gu)的~码来解军_体问题?/td> </tr></tbody></table><table align="center" width="620"><tbody><tr> <td class="a14"> <b>Javacd</b></td> </tr></tbody></table><table align="center" width="620"><tbody><tr> <td class="a14"> 在用Java解决国际化问题的q程中,可能利用到的主要的类都是由java.util包提供的。该cd中相关的cLLocale? ResourceBundle、ListResourceBundle、PropertyResourceBundle{,其承关pd下图所C?/td> </tr></tbody></table> <table align="center" width="620"> <tbody><tr align="center"> <td class="a14"><img src="http://www.ccw.com.cn/htm/app/aprog/01_7_18_2.jpg" height="194" width="400"> </td> </tr> </tbody></table> <table align="center" width="620"><tbody><tr> <td class="a14"> 其中各类提供的主要功能如下:(x)</td> </tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14"> LocaleQ该cd含对主要地理区域的地域化特征的封装。其特定对象表示某一特定的地理、政L文化区域。通过讑֮LocaleQ我们可以ؓ(f)特定的国? 或地区提供符合当地文化习(fn)惯的字体、符受图标和表达格式。例如,我们可以通过获得特定Locale下的Calendarcȝ实例Q显C符合特定表达格? 的日期?/td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14"> ResourceBundleQ该cL一个抽象类Q需要通过静态方法ResourceBundle.getBundle()指定具体实现cL属性文件的? 本名U。基本名UC(x)协同指定的或默认的Localec,军_具体调用的类或属性文件的唯一名称。例如:(x)指定基本cL属性文件名UCؓ(f)TestBundleQ? 而指定的Locale是CHINESEQ那么最适合匚w的类名称为TestBundle_zh_CN.classQ而最?jng)_配属性文件名UCؓ(f) TestBundle_zh_CN.properties。按照Java Doc和相x档的要求Q如果该cL属性文件没有找刎ͼpȝ?x)查找近似匹配(L件名依次为TestBundle_zh和TestBundle的类或属? 文gQ。该cL供的getKeys()Ҏ(gu)用于获得所有成员的键名Qƈ提供handleGetObjectҎ(gu)获得指定键的对应元素?/td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14"> ListResourceBundleQ该cȝ承ResourceBundlec,主要是增加了一些便于操作的成分Q但q是抽象cR如果希望用类的方式实现具体的ResourceBundleQ一般情况下最好承这个类?/td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14"> PropertyResourceBundleQ该cMl承ResourceBundlec,可以实例化。该cȝ行ؓ(f)特征如同java.util.propertiesc,可以从输入流中获得具体属性对?/td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14"> 如果涉及(qing)日期和时间显C等问题Ӟ可以利用java.text包以?qing)java.util包中的TimeZone、SimpleTimeZone和Calendar{类q行辅助处理?/td></tr></tbody></table><table align="center" width="620"><tbody><tr> <td class="a14"> <b>参数化解x?</b> </td> </tr></tbody></table><table align="center" width="620"><tbody><tr> <td class="a14"> 在具体应用时Q可以把具体国家或地区特征中可以参数化的部分攑֜l过Ҏ(gu)命名的属性文件中Q在定具体的Locale后,通过PropertyResourceBundlec读取相应的属性文Ӟ实现国际化特征?/td> </tr></tbody></table><table align="center" width="620"><tbody><tr> <td class="a14"> 使用PropertyResourceBundlec获得当地版本的国际化信息,部分代码如下Q?/td> </tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">   …?/td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">   public static final String BASE_PROP_FILE = </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14"> “DISP?</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">   public static final String SUFFIX = </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14"> ?properties?</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">   locale = Locale.getDefault();</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">   String propFile = BASE_PROP_FILE Q?“_?Q?locale.toString()Q?SUFFIX;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">   ResourceBundle rb;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">   try {</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    File file = new File(propFile);</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    if (file.exists()) {</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    is = new FileInputStream(file);</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    rb = new PropertyResourceBundle(is);</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    if (rb == null) System.out.println(“No Resource?;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    }</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">   } catch (IOException ioe) {</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    System.out.println(“Error open file named ?Q?propFile);</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">   }</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">   Enumeration e = rb.getKeys();</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">   while (e.hasMoreElements()){</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    key = (String)e.nextElement();</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    value = (String)rb.handleGetObject(key); </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    System.out.println(“KEY: ?Q?key Q?</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14"> “\t\t Value: ?Q?value);</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">   }</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">   …?/td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">   DISP_zh_TW.properties文g的具体内容如下:(x)</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">   Key1=\u53ef\u4ee5</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">   Key2=\u64a4\u9500</td></tr></tbody></table><table align="center" width="620"><tbody><tr> <td class="a14"> {号后面是利用native2asciiE序转化后的J体汉字Q如果不q行转化Q系l可能显CZؕ码?/td> </tr></tbody></table><table align="center" width="620"><tbody><tr> <td class="a14"> <b>处理提示和帮?/b></td> </tr></tbody></table><table align="center" width="620"><tbody><tr> <td class="a14"> 对于提示语言和帮助文仉分,可以把语a映射攑֜属性文件或者ListResourceBundlecȝ子类中。下面程序是一个ServletQ它通过接受客户端的选择Q把特定语言和字W版本的信息q回到客L(fng)?/td> </tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">   …?/td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">   public class ProcessServlet extends HttpServlet </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">   { //默认语言Z?/td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    public static final String DEFAULT_LANGUAGE = “zh? </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    //默认字符集ؓ(f)体中?/td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    public static final String DEFAULT_COUNTRY = “CN? </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    public void service(HttpServletRequest req, </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14"> HttpServletResponse res) throws IOException, ServletException {</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    HttpSession session = req.getSession(true);</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    // 从客L(fng)收到的指定语a和字W的参数应当与Sun公司相关规定一?/td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    String lang = req.getParameter</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14"> (“language?;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    String country = req.getParameter</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14"> (“country?;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    if (lang == null) </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">     {</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14"> //如果没有收到参数Q就试图从Session里获?/td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    lang = (String) session.getAttribute</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14"> (“language?;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    country = (String) session.getAttribute</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14"> (“country?</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    } else {</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    session.setAttribute(“language? lang);</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    session.setAttribute(“country? country);</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    }</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    if (lang == null) </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">     {</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14"> //如果无法从上q手D得到语a和字W信息,׃用默认?/td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    lang = DEFAULT_LANGUAGE;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    country = DEFAULT_COUNTRY</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    session.setAttribute(“language? lang);</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">     session.setAttribute(“country? country);</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    }</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    Locale locale = null;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    ResourceBundle bundle = null;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    try {</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    locale = new Locale(lang, country);</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    } catch (Exception e) {</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    System.out.println(“No locale with?Q?</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14"> country Q?“_?Q?lang);</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    locale = Locale.getDefault();</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    }</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    try {</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    bundle = ResourceBundle.getBundle( </td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14"> “DisplayList? locale);</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    } catch( MissingResourceException e) {</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    System.out.println( “No resources available for locale ?Q?locale);</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    bundle = ResourceBundle.getBundle</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14"> (“DisplayList? Locale.US);</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    }</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    res.setContentType(“text/html?;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    PrintWriter out = res.getWriter();</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    out.println(?lt;html>?;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    out.println(?lt;head>?;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    String title = bundle.getString(“title?;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">   String welcome =bundle.getString</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14"> (“welcome?;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    String notice = bundle.getString(“notice?;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    out.println(?lt;title>”+ title Q?</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14"> ?lt;/title>?;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    out.println(?lt;/head>?;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    out.println(?lt;body bgcolor=\?/td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14"> white\?gt;?;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    out.println(?lt;h3>?Q?welcome Q?</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14"> ?lt;/h3>?;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    out.println(?lt;br>?;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    out.println(?lt;b>?Q?notice Q?</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14"> ?lt;/b>?;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    out.println(?lt;/body>?;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    out.println(?lt;/html>?;</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">    }</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14">   }</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14"> 上述Servlet使用的属性文ӞDisplayList_zh_CN.</td></tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14"> propertiesQ内容如下:(x)</td></tr></tbody></table><table align="center" width="620"><tbody><tr> <td class="a14"> title=中文?/td> </tr></tbody></table><table align="center" width="620"><tbody><tr> <td class="a14"> welcome=q是体中文版?/td> </tr></tbody></table><table align="center" width="620"><tbody><tr> <td class="a14"> notice=体中文测试成?/td> </tr></tbody></table><table align="center" width="620"><tbody><tr> <td class="a14"> 注意Q该文g直接采用了中文,而不是经q{化的Unicode~码Q这是由于大多数Web服务器不需要上q{化?/td> </tr></tbody></table><table align="center" width="620"><tbody><tr> <td class="a14"> 在实际用中Q如果Web服务器支持Servlet 2.3规范Q如jakartaQtomcate 4.0Q,那么上面提到的Servlet应当E加改变Q以作ؓ(f)其他Servlet的处理器使用。另外,如果把ResourceBundle的特定版本存? 在无状态会(x)话Bean中,可以在一定程度上提高E序效率?/td> </tr></tbody></table><table align="center" width="620"><tbody><tr> <td class="a14"> <b>?l?/b></td> </tr></tbody></table><table align="center" width="620"><tbody><tr> <td class="a14"> W者在实际试中发C如下问题Q其中部分问题得C解决Q?/td> </tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14"> 1. 对于昄字符出现q的问题,如果是通过属性文件实现国际化解决Ҏ(gu)Q那么可能是直接在属性文件中写入了非标准ASCII文字。解x法是利用JDK提供 的工具native2ascii.exe扫描所有属性文Ӟ用扫描结果覆盖原有文件内宏V如果我们是利用cL件实现{换方案,那么需要重新编译相关类? Ӟq在~译时指定编码集。例如,~译使用国标码的cLӞ采用的编译命令如下:(x)</td></tr></tbody></table><table align="center" width="620"><tbody><tr> <td class="a14"> javac Qencoding GB2312 your_java_file</td> </tr></tbody></table><table align="center" width="620"><tbody><tr><td class="a14"> 2. 虽然Sun宣称Q在ResourceBundlecȝ实例化过E中Q该cM(x)查找与指定的基础cȝ对匹配和量与指定的Locale属性相匚w的类。例如:(x) 如果我们指定ResourceBundle基础cMؓ(f)TestBundleQ而Locale中指定用zh_CNQ中国大陆地区简体中文)(j)Q那么如果系l找 不到TestBundle_zh_CNQ系l应当顺ơ查找TestBundle_zh、TestBundle。但是笔者在pȝ开发过E中发现Q该匚w没有 产生M实际效果?/td></tr></tbody></table> W者的试q_是Windows 2000 ServerQ没有配|Q何Service PackQ用的JDK版本?.3.0版本。笔者试N过查看JDK目录下src.jar中附带的源码扑ֈ引v问题的原因,但是发现有关的操作被装? sun.misc包中Q而src.jar文g没有提供该包中Q何类的源码。本文把q个问题提出来,希望与有兛_发h员一h讨?img src ="http://www.tkk7.com/juhongtao/aggbug/23793.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/juhongtao/" target="_blank">javaGrowing</a> 2005-12-14 10:57 <a href="http://www.tkk7.com/juhongtao/archive/2005/12/14/23793.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java反射机制http://www.tkk7.com/juhongtao/archive/2005/12/09/23172.htmljavaGrowingjavaGrowingFri, 09 Dec 2005 08:58:00 GMThttp://www.tkk7.com/juhongtao/archive/2005/12/09/23172.htmlhttp://www.tkk7.com/juhongtao/comments/23172.htmlhttp://www.tkk7.com/juhongtao/archive/2005/12/09/23172.html#Feedback1http://www.tkk7.com/juhongtao/comments/commentRss/23172.htmlhttp://www.tkk7.com/juhongtao/services/trackbacks/23172.html阅读全文

javaGrowing 2005-12-09 16:58 发表评论
]]>
认识.NET的集?/title><link>http://www.tkk7.com/juhongtao/archive/2005/12/09/23114.html</link><dc:creator>javaGrowing</dc:creator><author>javaGrowing</author><pubDate>Fri, 09 Dec 2005 03:32:00 GMT</pubDate><guid>http://www.tkk7.com/juhongtao/archive/2005/12/09/23114.html</guid><wfw:comment>http://www.tkk7.com/juhongtao/comments/23114.html</wfw:comment><comments>http://www.tkk7.com/juhongtao/archive/2005/12/09/23114.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.tkk7.com/juhongtao/comments/commentRss/23114.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/juhongtao/services/trackbacks/23114.html</trackback:ping><description><![CDATA[ <meta http-equiv="content-type" content="text/html; charset=gb2312"><title>认识.NET的集?- 开发?- ZDNet China


认识.NET的集?/span>

作者:(x) BUILDER.COM
Wednesday, May 15 2002 11:06 AM

 集合QcollectionQ提供了一U结构化l织L对象的方式,而且我们早就知道集合在日常编E工作中的重要性?NETcd提供了丰富的集合数据cdQ其U类之繁多甚至许多人看得眼都花了,q些集合对象都具有各自的专用场合。不怎么_(d)更多的选择也就意味着更高的灵zL,但同时也意味着更高的复杂性。因此,寚w合各个类型的用途和使用条gh适度的了解是完全必要的。下面就请随我进行一?NET集合之旅吧!

.NET集合定义


?NET的角度看Q所谓的集合可以定义ZU对象,q种对象实现一个或者多?i>System.Collections.ICollection?i>System.Collections.IDictionary?i>System.Collections.IList接口。这一定义?i>System.Collections名称I间中的“内|”集合划分成了三U类别:(x)

  • 有序集合Q仅仅实现ICollection接口的集合,在通常情况下,其数据项目的插入序控制着从集合中取出对象的的序。System.Collections.Stack? System.Collections.Queuec都是ICollection集合的典型例子?/div>
  • 索引集合Q实现Ilist的集合,其内容能l由从零开始的数字(g)索取出,p数组一栗System.Collections.ArrayList对象是烦(ch)引集合的一个例子?/div>
  • 键式集合Q实?IDictionary 接口的集合,其中包含了能被某些类型的键值检索的目。IDictionary集合的内定w常按键值方式存储,可以用枚丄方式排序(g)索? System.Collections.HashTablecdCIDictionary 接口?/div>

正如你看到的那样Q给定集合的功能在很大程度上受到特定接口或其实现接口的控制。如果你寚w向对象编E缺乏了解,那么你可能对上面说的q些话感到难以理解。不q你臛_应该知道Q以接口q种方式构造对象的功能不但造就了具有整套类似方法的对象族,而且q能让这些对象在必要的情况下可以当作同类Q以OOPQ面向对象编E)(j)的术语来_(d)q就是大名鼎鼎的多态性技术?/p>

System.Collections概述

System.Collections名称I间包含了在你的应用E序中可以用到的6U内建通用集合。另一些更Z业化的集合则归属于System.Collections.SpecializedQ在某些情况下你?x)发现这些专用集合也是非常有用的。加上一些异常(exceptionQ类Q这些专业化集合在功能上和内建集合是cM的。现在就让我们审视一下通用集合以及(qing)量的不太富于专业化的集合?/p>

堆栈和队?/h5>

System.Collections.Stack ?System.Collections.Queue c,两者仅仅实CICollection 接口Q按照存储项目加到集合的序保存System.Objectcd的项目。对象只能按其加入顺序从集合中检索:(x)堆栈是后q先出,而队列则是先q先出。通常情况下,你在以下场合可以考虑采用以上q些集合Q?/p>

  • 接收和处理集合内目旉序比较重要?/div>
  • 你能在处理项目之后丢弃它?/div>
  • 你不需要访问集合中的Q意项目?/div>

ArrayList

System.Collections.ArrayListc,仅仅实现 IlistQ最适合描述ZU正常数l和集合的؜合类型。ArrayList按照目被加入集合的序存储目。每个项目都被分配一个烦(ch)引标识符而且能由兌它们的烦(ch)引数字以M序被检索。当新项目加入集合时?x)扩大ArrayList从而o(h)其相比普通数l更L(fng)zL。然而,ArrayList负蝲比传l数l更大而且没有实现严格的类型化Q也可以接受Q何{换ؓ(f)System.Object的对象(换句话说Q对什么东襉K来者不拒)(j)?/p>

SortedList

System.Collections.SortedListQ它实现了IDictionary和ICollection接口Q是最基本的排序集合,与Vb6下的Collection对象非常cM。SortedList存储对象q按照关联的键值对q些存储对象排序。它们也是同时支持烦(ch)引数字和键对象检索的唯一内徏?NET集合?/p>

HashTable

强有力的System.Collections.HashTable集合实现了IDictionary ? IcollectionQ能用来存储多种cd的对象连同关联的唯一字符串键倹{在HashTable集合中的目按照源自光值的哈希代码所定的顺序存储。集合内每个对象的键值都必须唯一Q而其哈希代码则不一定唯一?/p>


什么是哈希代码Q?/em>
哈希代码实质上就是从一快数据中消除所有冗余部分之后的l果Q它主要起到Ҏ(gu)据辅助分cL排序的作用?br>

当某个项目加入集合时QHashTable卌用键值的GetHashCodeҎ(gu)Q由于所有的c都是从System.Objecl承的,所以调用该Ҏ(gu)卛_定该类的哈希代码ƈ且按该代码排序存储。你可以使用定制的哈希函敎ͼҎ(gu)有二Q一是重载类?i>GetHashCodeҎ(gu)Q二是向HashTable构造器传递实CSystem.Collections.IHashcodeProvider接口的对象,在这U情况下Q该对象用于ؓ(f)所有加入集合的键g生哈希代码?/p>

从性能的角度看Q因为键值搜索仅限于h同样哈希代码的键|所以HashTable能够很快C集合中检索Q意一个元素,从而减了必须通过(g)查以发现匚w的键值的数量。然而,因ؓ(f)插入到集合中的每个对?键值对都必M生相应的哈希代码Q所以项目插入的代h(hun)有炚w了。因此,HashTable主要q用在按照Q意键值反复检索大量相寚w态的数据q一场合下?/p>

ListDictionary ?HybridDictionary

ListDictionary ?HybridDictionary cd属于System.Collections.Specialized。它们都在按照唯一键值的原则来组l项目,而且都实C IDictionary ? ICollection 。ListDictionary在内部以链表的方式存储项目,用在不会(x)增长过10个项目的集合中。HybridDictionary采用一个内部链表(实际上就是ListDictionaryQ作为小集合Q当集合变得_大(过10个项目)(j)以至于链表实现效率降低时׃(x)转换为HashTable?/p>

StringCollection ?StringDictionary

System.Collections.Specialized.StringCollection ? System.Collections.Specialized.StringDictionary 都对存储字符串的集合q行了优化? StringCollection实现?IList ?ICollection 而且实质上就是ArrayListQ只不过实现了强烈的cd化仅仅接受字W串而已。StringCollection最理想的应用场合是l常更新或增加的量数据Q而StringDictionary则最适用于不l常增加目到诸如HashTable之类集合中的大量数据?/p>

NameValueCollection

System.Collections.Specialized.NameValueCollection最有趣的地方在于它能包含关联同一键值的多个目Q这正是它与其他内徏集合的差别所在。除此以外,它在功能上类似HashTableQ按照源自每一目键值的哈希代码寚w目排序从而也hcd的优~点?/p>

存在的问?/h5>

如果说由 .NET cd所提供的内建集合也存在问题的话Q那多半是它们几乎都在内部把目存储?i>System.Object.cd。从最大灵zL的角度看那是一个好xQ但同时也给采用q些通用集合的程序员提出了一些问题。首先,只要你把一个新目加到集合中去Q运行时必d施类型{换操作(创徏值类型的索引以便可以当作对象引用Q。这是一U低效的操作而且在处理大型集合时?x)生相当可观的性能问题。其ơ,只要你访问通用集合中的一个项目,该项目都作?i>System.Objectcd被返回,q就意味着你不得不把它转换为真实的cd才能对其q行有意义的操作?/p>

责Q~辑Q?a href="mailto:zhang_yan@zdnet.com.cn?subject=%27http://www.zdnet.com.cn/developer/tech/story/0,2000081602,39034559,00.htm%27">炒饭



javaGrowing 2005-12-09 11:32 发表评论
]]>
java对象容器http://www.tkk7.com/juhongtao/archive/2005/12/09/23103.htmljavaGrowingjavaGrowingFri, 09 Dec 2005 03:09:00 GMThttp://www.tkk7.com/juhongtao/archive/2005/12/09/23103.htmlhttp://www.tkk7.com/juhongtao/comments/23103.htmlhttp://www.tkk7.com/juhongtao/archive/2005/12/09/23103.html#Feedback1http://www.tkk7.com/juhongtao/comments/commentRss/23103.htmlhttp://www.tkk7.com/juhongtao/services/trackbacks/23103.html数组与其它容器的区别体现在三个方面:(x)效率Q类型识别以?qing)可以持?/span>primitives.

数组?/span>java提供的,是能随机存储和访?/span>reference序列的诸多方法中Q最高效的一U。数l是UŞ序列Q所以它可以快速访问其中的元素Q但速度是有代h(hun)的,当你创徏了一个数l之后它的容量就固定了,而且在其生命周期里不能改变。也怽?x)提议先创徏一个数l,{到快不够用的时候,再创Z个新的,然后旧数组里的reference 全部导到新的里面。其?/span>ArrayList 是q么做的。但是这U灵zL所带来的开销Q?/span>ArrayList 的效率比       hl有了明显下降?/span>      

在我们写E序的时候往往不知道要用多对象,或者要用一U更复杂方式来存储对象情c(din)ؓ(f)此,Java 提供了“容器类(container class)”。其基本cd?/span>List, Set ?/span>Map。有了这些工P你就能解军_多问题了。它们还有一些别的特性。比方说Set 所持有的对象,个个都不同,Map则是一个“关联性数l?/span>(associative array)”,它能在两个对象之间徏立联pR此外,与数l不同,它们q能自动调整大小Q所以你可以往里面放Q意数量的对象。这样写E序的时候,׃用操心要开多大的空间了?/span>

 

Java2 的容器类要解决“怎样持有对象”,而它把这个问题分成两c:(x)

1. Collection: 通常是一l有一定规律的独立元素?/span>List 必须按照特定的顺序持有这些元素,?/span>Set 则不能保存重复的元素?/span>(bag没有q个限制Q但?/span>Java的容器类库没有实现它Q因?/span>List 已经提供q种功能了?/span>)

2. Map: 一l以“键——值?/span>(key-value)形式出现?/span>pair。初看上去,它应该是一?/span>pair?/span>CollectionQ但是真q么d的话Q它?yu)׃?x)变得很滑E,所以还是把q个概念独立列出来ؓ(f)好。退一步说Q真的要用到Map 的某个子集的时候,创徏一?/span>Collection 也是很方便的?/span>Map可以q回“键(key)的?/span>SetQ值的CollectionQ或?/span>pair?/span>Set。和数组一PMap 不需要什么修改,p很容易地扩展成多l。你只要直接?/span>Map 的D?/span>Map 可以了(然后它的值再?/span>MapQ以此类?/span>)。我们先来看看容器的一般特性,然后深入l节Q最后再看什么会(x)有这么多版本Q以?qing)如何进行选择?/span>

 

List ?x)老老实实地持有你所输入的所有对象,既不做排序也不做~辑?/span>Set 则每个对象只接受一ơ,而且q要用它自己的规则对元素q行重新排序(一般情况下Q你兛_的只?/span>Set 包没包括某个对象Q而不是它到底排在哪里——如果是那样Q你最好还是用List)。?/span>Map 也不接收重复?/span>pairQ至于是不是重复Q要?/span>key来决定。此外,它也有它自己的内部排序规则,不会(x)受输入顺序媄(jing)响。如果插入顺序是很重要的Q那你就只能使用LinkedHashSet ?/span>LinkedHashMap 了?/span>

test.bmp

W一眼看到这张图的时候,你会(x)觉得很震撹{不q你马上׃(x)知道Q实际上只有三种容器lg——MapQList 和SetQ而每U又有两C个实现。最常用的几个容器已l用_黑U框了v来。看到这里,q张囑ְ不再那么令h望而生畏了?br>用点hh的是interfaceQ用虚线框v来的是abstract c,实线
则表C普通的(“实体concrete?cR点U的头表示cdCq个interface(或者,abstract c表C部分实Cq个interface)。实U?br>头表示q个cd以制造箭头所指的那个cȝ对象。比如,Collection
能制造IteratorQ而List q能刉ListIterator(也能刉IteratorQ因为List 是承自Collection ??br>与存攑֯象有关的接口包括CollectionQListQSet 和Map。在理想情况下,l大多数代码应该只同q些接口打交道,只是在创建容器的时候才要精地指明它的切cd。所以你可以q样创徏一个List?br>List x = new LinkedList( );

当然Q你也可以选择让x 成ؓ(f)LinkedList(而不是泛型的List)Q这样x 带上了准确的类型信息interface 的优?(同时也是它的本意)在于,你想修改具体的实现的时候,只要改一下创建的声明可以了Q就
像这P(x)
List x = new ArrayList( );
无需惊动其它代码(用P代器也能获得一些这U泛型?。这个类p里面有很多以“Abstract”开头的c,初看hq可能会(x)让h有点不明白。实际上它们只是一些部分实现某个接口的办成品。假如你要编一个你自己的SetQ不要从Set 接口开始挨个实现它的方法;相反你最好承AbstractSetQ这样就能把~程的工作量压羃到最低了。但是,实际上容器类库的功能已经够强的了Q我们要求的事情它几乎都能做
到。所以对我们来说Q你完全可以忽略以“Abstract”开头的cR?/span>

List 的功?/span>
正如你从ArrayList 那里所看到的,List 的基本用法是相当单的。虽然绝大多数时候,你只是用add( )加对象,用get( )取对象,用iterator( )获取q个序列的IteratorQ但List q有一些别的很有用?br>Ҏ(gu)?br>实际上有两种ListQ擅长对元素q行随机讉K的,较常用的ArrayListQ和更强大的LinkedList。LinkedList 不是为快速的随机讉K而设计的Q但是它却有一l更加通用的方法?/span>

List (接口) List 的最重要的特征就是有序;它会(x)保以一定的序保存元素。List 在Collection 的基上添加了大量Ҏ(gu)Q之能在序列中间插入和删除元素?只对LinkedList 推荐使用?List 可以刉ListIterator 对象Q你除了能用它在List 的中间插入和删除元素之外Q还能用它沿两个方向遍历List?
ArrayList*一个用数组实现的List。能q行快速的随机讉KQ? 但是往列表中间插入和删除元素的时候比较慢。ListIterator 只能用在反向遍历ArrayList 的场 合,不要用它来插入和删除元素Q因为相比LinkedListQ在ArrayList 里面用ListIterator 的系l开销比较高?/td>
LinkedList寚w序访问进行了优化。在List 中间插入和删除元 素的代h(hun)也不高。随问的速度相对较慢?用ArrayList 吧?此外它还有addFirst( )Q?addLast( )QgetFirst( )QgetLast( )Q?removeFirst( )和removeLast( ){方?q些 Ҏ(gu)Q接口和基类均未定义)Q你能把它当成栈(stack)Q队?queue)或双向队?deque)来用? 下面q段E序把各U操作都集中到方法里面:(x)List 都能作的?basicTest( ))Q用Iterator 在列表中Ud(iterMotion( ))Q修? 列表的元?iterManipulation( ))Q查看List 的操作结?testVisual( ))Q以?qing)LinkedList 所独有的方法?/td>
Set 的功?/span>
Set 的接口就是Collection 的,所以不像那两个ListQ它没有额外?br>功能。实际上Set 确实实是一个Collection——只不过行ؓ(f)方式?br>同Ş了?q是l承和多态性的完美q用Q表达不同地行ؓ(f)?Set ?x)拒l?br>持有多个h相同值的对象的实?对象的“值”又是由什么决定的呢?
q个问题比较复杂Q我们以后会(x)讲的)?br>
Set (接口)加入Set 的每个元素必L唯一的;否则Q?Set 是不?x)把它加q去的。要惛_qSetQ?Object 必须定义equals( )Q这h能标明对象的唯一性。Set 的接口和Collection ?br>一模一栗Set 的接口不保证它会(x)用哪U顺序来存储元素?br>
HashSet* Z化查询速度而设计的Set。要放进HashSet 里面的Object q得定义hashCode( )?/td>
TreeSet 是一个有序的SetQ其底层是一|(wi)。这样你 p从Set 里面提取一个有序序列了?/td>
LinkedHashSet(JDK 1.4) 一个在内部使用链表的SetQ既有HashSet 的查询速度Q又能保存元素被加进ȝ序(插入序)。用Iterator 遍历Set 的时候, 它是按插入顺序进行访问的?/td>
Map 的功?/span>
ArrayList 能让你用数字在一个对象序列里面进行选择Q所以从某种意义上讲Q它是将数字和对象关联v来。但是,如果你想Ҏ(gu)其他条g在一个对象序列里面进行选择的话Q那又该怎么做呢Q从概念上讲Q它看上d是一个ArrayListQ但它不用数字,而是用另一个对象来查找对象Q这是一U至关重要的~程技巧。这一概念在Java 中表Cؓ(f)Map。put(Object key, Object value)Ҏ(gu)?x)往Map 里面加一个|q且把这个值同?你查找时所用的对象)联系h。给出键之后Qget(Object key)׃(x)q回与之相关联的倹{?br>你也可以用containsKey( ) ?containsValue( )试Map 是否包含有某个键或倹{?br>Java 标准cd里有好几UMapQHashMapQTreeMapQ?LinkedHashMapQW(xu)eakHashMapQIdentityHashMap?br>它们都实CMap 的基本接口,但是在行为方式方面有着明显的差异。这些差异体现在Q效率,持有和表C对象pair 的顺序,持有对象的时间长短,以及(qing)如何军_键的相等性。性能是Map 所要面对的一个大问题。如果你知道get( )是怎么工作的,你就?x)发?比方?在ArrayList 里面扑֯象会(x)是相当慢的。而这
正是HashMap 的强V它不是慢慢C个个地找q个键,而是用了一U被UCؓ(f)hash code的特D值来q行查找的。散?hash)是一U算法,它会(x)从目标对象当中提取一些信息,然后生成一个表C个对象的“相?br>独特”的int。hashCode( )是Object 根类的方法,因此所有Java对象都能生成hash code。HashMap 则利用对象的hashCode( )来进行快速的查找。这h能有了急剧的提高?br>
Map (接口)l持键-值的兌(即pairs)Q这样就能用键来扑րg?/td>
HashMap* Zhash表的实现?用它来代替Hashtable?提供旉恒定的插入与查询。在构造函C可以讄 hash表的capacity 和load factor。可以通过构? 函数来调节其性能?/td>
LinkedHashMap(JDK 1.4) 很像HashMapQ但是用Iterator q行 遍历的时候,它会(x)按插入顺序或最先? 的顺?least-recently-used (LRU) order)q行讉K。除了用Iterator 外, 其他情况下,只是比HashMap E慢一 炏V用Iterator 的情况下Q由于是使用 链表来保存内部顺序,因此速度?x)更快?/td>
TreeMap ZU黑?wi)数据结构的实现。当你查看键 或pair Ӟ?x)发现它们是按顺?(Ҏ(gu)Comparable 或ComparatorQ我们过 一?x)?排列的。TreeMap 的特Ҏ(gu)Q你 所得到的是一个有序的Map。TreeMap 是Map 中唯一有subMap( )Ҏ(gu)的实 现。这个方法能让你获取q个?wi)中的一? 分?/td>
WeakHashMap 一个weak key的MapQ是为某些特D问 题而设计的。它能让Map 释放其所持有? 对象。如果某个对象除了在Map 当中充当 键之外,在其它地斚w没有其reference 的话Q那它将被当作垃圑֛收?/td>
IdentityHashMap(JDK 1.4) 一个用==Q而不是equals( )来比较键 的hash map。不是ؓ(f)我们q_使用而设 计的Q是用来解决Ҏ(gu)问题的? 散列是往Map 里存数据的常用算法。有时你?x)需要知道散列算法的工作 l节Q所以我们会(x)E后再讲?/td>


javaGrowing 2005-12-09 11:09 发表评论
]]>
վ֩ģ壺 ŮѹۿˬˬˬƵ| Ʒѹۿ| AVվ߹ۿ| ĻĻmv| ޳ѵӰ| Ʒۺߵһ| avƬѿ| 99ѹۿƵ| ѰvƷƵַ| ҴýһAV | | һëƬƵ| Ļ޾Ʒһ| ޹Ʒav| Ů˱˾޴| ?VþþƷ| ³ѲƵ㽶| Ļһ| պav| ҹվѰ߹ۿ| ҹڵ| ˬƵ| ƷþƵ| ѱ̬Ƶַվ| ҹɫһ| ޾Ʒ岻| ޹¶| ¹ŮһëƬ| ts߹ۿƵ| av| Ů18ëƬaëƬƵ| ˳Ƶ| һëƬŮͼƬ| ޾ƷƬ߲| ؼaaaaaaëƬ| ҹþþþ| Ů18ëƬˮ| ۺ| 999þþѾƷ| ŮڵƵ| Ļ벻Ӱ|