分类:
2008-09-09 12:38:47
你不必重新改写应用程序也能利用 5的新功能。我们为你介绍不必改写大量代码就能实现更新的方法。
你曾经得到一段以 1.4或更早版本编写的代码,并希望改写它以进行开发吗?这段代码当然能够在Java SE 5上正常运行,但详细了解Java SE 5的新功能会使接下来的开发过程更加顺利。下面我们来了解一些应用那些功能的简单实例,以及如何以最小的争论来介绍它们。
我们首先从StringBuffer类开始,你可以在任何代码中找到这个连接字符串的类。下面是它的一个典型应用:
StringBuffer sb=new StringBuffer();
sb.append("Some strings")
…
sb.append(someMethod());
String result=sb.toString();
StringBuffer看起来没什么特别,但它有一个难解的语义;它是线程的,所以你每次调用它的一个方法,都必须为它本身获得一个同步锁。现在,在大多数类型代码中,可能你不会有两个应用同样StringBuffer的线程,但StringBuffer仍然需要同步锁,这需要一小段时间。输入JavaSE 5的StringBuilder,它与StringBuffer相同,只有一点不同:它不是线程类,因此不需要获得同步锁。这是一个细微的性能改进,但执行起来很简单。
StringBuilder sb=new StringBuilder();
所有的其它方法与StringBuffer一样。按惯例,对于新代码,你应该使用StringBuilder,除非字符串确实由多线程组成。
如果你的代码由一系列‘myString=myString+appendString;’类型的操作构成,那么你的确需要改变它。在Java中,‘+’运算符将字符串串联起来,因为Java字符串不可改变,Java编译器在后台重写它,建立一个StringBuffer,在缓冲器中进行串联并返回结果字符串。如果你在进行实际的字符串处理,你就希望用StringBuilder来代替它,使其更加简洁。只有一种情况你不希望那样做:调试或日志代码:
System.out.println("Got "+someInteger+" and "+someString+" on "+someDate
你不必优化它,但是Java SE 5推出了一个你可能希望使用的方法printf,使打印与格式化输出更加方便。下面的代码与printf等价:
System.out.printf("Get %d and %s on %tc%n",someInteger,someString,someDate
如果你熟悉C语言的printf,对上面的代码就不会陌生。第一个自变量是一个格式化字符串,它使用%表示如何对下面的一个自变量进行格式化;因此%d意思是把第一个自变量打印为数字;%s指把第二个自变量打印为字符串,%c指把第三个自变量打印成格式化的日期。由于Java SE 5支持可变自变量,所以你可以向printf提交任何数量的自变量;而且它的格式化字符串指示也比C语言灵活。例如,你可以通过引用自变量的目录来多次引用一个自变量:
System.out.printf("Get %d and %s on %tc, that's %1$d%n",someInteger
%1$d部分很重要。如果%后面是自变量的数字目录,以$结尾,接着是格式化指示,它就从那个特殊的自变量取值。结尾处的%n生成一个换行符;如果你熟悉C语言,就知道用可插入一个新行,因为%n生成一个独立于平台的换行符,而不是。查看Sun的Java文件了解格式化指示的全部内容。它是在printf中唯一被调用的Formatter类,你也可以在自己的代码中使用。
可变自变量是Java 5的新功能,如果你发现代码中全是为提交自变量而建立的数组,就可以使用它。如:
process("print",new String[]);
这里的过程方法(process method)应该为:
private void process(String cmd,String[] args) {
if(cmd.equals("print")) {
for(int i=0;I
它使用一个String数组提交数量可变的自变量给过程调用,但又希望调用方已建立了那个数组。Vararg支持让我们指定一个在自变量表中从未出现或出现多次的参数,并将它们变成一个数组,从而避免了这一需要。我们只需去掉过程声明中的“[]”符号,并用“…”代替它即可。
private void process(String cmd,String... args) {
方法的其它部分不变。然后,调用过程会变得简单得多。
process("print","These","are","arguments");
它的巧妙之处在于,当你需要用Java 5自动管理它时,你仍然可以使用基于数组的调用类型;于是你可以更新方法声明,而不必修改调用代码。
在对一段代码进行重新开发时,我喜欢确信我没有对代码当前的运行方式做出假设。Java的静态类型非常适于这一点;但是如果我们处理集合,上述优点却不复存在。以下面这段代码为例:
ArrayList list=new ArrayList();
list.add(new Integer(1000));
list.add(new Integer(200));
Iterator i=list.iterator();
while(i.hasNext()) {
Integer ni=(Integer)i.next();
System.out.println(ni);
}
这些都是合法的Java代码,在运行时间也能正常执行,只是在列表中增加了Integer(整数)实例。如果建立和处理操作依次发生,就可以容易的发现问题,但还是没有静态编译时间检查。JavaSE 5在Java语言中增加了“Generics”特性来解决这个问题。
如果你编译这段代码,首先你会看到一个警告:
Note: Example.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
在编译器标记中增加-Xlint:unchecked,你会发现它在列表中增加了一个Integer类。
Example.java:41: warning: [unchecked] unchecked call to add(E) as a member of the raw type java.util.ArrayList
list.add(new Integer(1000));
Example.java:42: warning: [unchecked] unchecked call to add(E) as a member of the raw type java.util.ArrayList
list.add(new Integer(200));
这是因为ArrayList不知道自己会包含哪种类型的类,所以标记称之为“未经检查”(unchecked)。要解决这个问题,我们需要进入ArrayList声明,清楚说明ArrayList包含什么类:
ArrayList list=new ArrayList();
包含的类型在<和>之间,在此例中为Integer类。现在这是一个清楚知道它包含Integer类的ArrayList,并会按此执行。因此如果你尝试执行list.add(new Boolean(true))时,将会发生一个编译时间错误:
Example.java:43: cannot find symbol
symbol: method add(java.lang.Boolean)
location: class java.util.ArrayList
list.add(new Boolean(true));
不过还是要注意,错误说明的是“没有这种增加布尔值的add方法”,而不是“你在给一个只接受Integer类的ArrayList增加一个它不支持的类。”
现在其它代码将按这一变化执行。但我们可以进一步改进它。注意,我们用到Iterator的next方法,它返回一个我们给我们所认为的类转型的Object。理想情况下,我们希望转型尽可能的少。你们要让它明白它是一个循环Integer的Iterator。列表确实返回一个Iterator
Iterator i=list.iterator();
现在我们可以删除循环中的转型操作。于是Iterator为next()方法返回一个Integer,所以转型更少:
while(i.hasNext()) {
Integer ni=i.next();
System.out.println(ni);
}
为使代码更加简洁,值得指出的是,Java 5对循环进行了改进,使循环时的重复更加简化:
for(Integer ni:list) {
System.out.println(ni);
}
应用这个语法,就不用担心Iterator过于松散了,它也照顾到编译器。在for()里面的冒号右边是可以不断重复的内容,如一列或一组集合或一个数组。在冒号左边是一个保存每次循环结果的声明。新的for语法使代码更易阅读。
说到Map集合,它有一个键和一个值,你可以指定二者的类型。假如我们希望HashMap以Integer为键,以String为值,可以这样指定:
Map
利用这些generics知识,你可以强化你的Collections,并保证它们内容的类型安全。当然还有另一部分的generics知识,即建立你自己的generics类。但因为Collection类已经被一般化,你可以通过generics了解一些有用的知识,而不必钻研那一领域。
[1]