准备工作
如下一些名称,它们指的基本上是同一个东西:
Tiger
Java(TM) 2 Platform Standard Edition 5.0
J2SE(TM) 5.0
Java version 1.5.0
.....
Java下一个版本6.0的代号是"Mustang"野马,再下一个版本7.0的代号是"Dolphin"海豚。
概述
J2SE(TM) 5.0引入了很多激进的语言元素变化,这些变化或多或少减轻了我们开发人员的一些编码负担,其中的大部分也必然会被应用到即将发布的J2EE(TM) 5.0中。主要的新特性包括:
1- 泛型
2- 增强的for循环
3- 自动装箱和自动拆箱
4- 类型安全的枚举
5- 可变长度参数
6- 静态引入
7- 元数据(注解)
8- C风格的格式化输出
泛型
泛型这个题目相当大,大到完全可以就这个话题写一本书。
首先我们来看一个简单的使用泛型类的例子:
ArrayList<Integer> aList = new ArrayList<Integer>();
aList.add(new Integer(1));
// ...
Integer myInteger = aList.get(0);
那么泛型是怎样定义的呢?看看下面这一段示例代码:(其中用E代替在实际中将会使用的类名,当然你也可以使用别的名称,习惯上在这里使用大写的E,表示Collection的元素。)
public class TestGenerics<E> {
Collection<E> col;
public void doSth(E elem) {
col.add(elem);
// ...
}
}
泛型方法的定义类似下面的例子:
public static <T extends SomeClass> void add (Collection<T> c, T elem) {
c.add(elem);
}
其中T代表了我们这个方法期待的那个最终的具体的类,相关的声明必须放在方法签名的紧靠返回类型说明之前。在本例中,它可以是SomeClass或者SomeClass的任何子类,其说明<T entends SomeClass>放在void关键字之前(只能放在这里)。这样我们就可以让编译器确信当我们试图添加一个元素到泛型的ArrayList实例中时,可以保证类型安全。
增强的for循环
在5.0中,我们可以这样写:
public void showAll (Collection c) {
for (Object obj : c) {
System.out.println((String) obj);
}
}
public void showAll (String[] sa) {
for (String str : sa) {
System.out.println(str);
}
}
public void showAll (Collection<String> cs) {
for (String str : cs) {
System.out.println(str);
}
}
自动装箱/自动拆箱
public static void manualBoxingUnboxing(int i) {
ArrayList<Integer> aList = new ArrayList<Integer>();
aList.add(0, new Integer(i));
int a = aList.get(0).intValue();
System.out.println("The value of i is " + a);
}
public static void autoBoxingUnboxing(int i) {
ArrayList<Integer> aList = new ArrayList<Integer>();
aList.add(0, i);
int a = aList.get(0);
System.out.println("The value of i is " + a);
}
当然,你需要足够重视的是:一方面,对于值类型和引用类型,在资源的占用上有相当大的区别;另一方面,装箱和拆箱会带来额外的开销。在使用这一方便特性的同时,请不要简单的忘记了背后隐藏的这些也许会影响性能的因素。
类型安全的枚举
借用Java官网上的例子:
public enum Operation {
PLUS { double eval(double x, double y) { return x + y; } },
MINUS { double eval(double x, double y) { return x - y; } },
TIMES { double eval(double x, double y) { return x * y; } },
DIVIDE { double eval(double x, double y) { return x / y; } };
// Do arithmetic op represented by this constant
abstract double eval(double x, double y);
}
我们可以通过下面的代码来试验上面这个枚举类:
public static void main(String args[]) {
double x = Double.parseDouble(args[0]);
double y = Double.parseDouble(args[1]);
for (Operation op : Operation.values()) {
System.out.println(x + " " + op + " " + y + " = " + op.eval(x, y));
}
}
可变长度参数
public String testVararg(String... args) {
StringBuilder sb = new StringBuilder();
for (String str : args) {
sb.append(str);
}
return sb.toString();
}
这样的方法签名跟你写成testVararg(String[] args)的区别在于:在调用时,你不再需要传入一个包装好的String数组,你只需要简单的写一连串String参数,以逗号隔开即可,就如同这个方法正好有一个重载的版本是接受那么多个String参数一样。
静态引入
所谓静态引入就是指除了引入类之外,我们现在又多了一种选择:引入某个类的静态字段。如:
import static java.lang.Math.PI;或者import static java.lang.Math.*;
这样我们在接下来的代码中,当我们需要使用某个被引入的静态字段时,就不用再写上前面的类名了。当然,出现名字冲突时,跟原来的类引入一样,还是需要前缀以示区分。我个人认为这个新语言元素意义不大。当引入太多静态字段后,代码会变得难以阅读和维护。由于静态字段的名字通常不如类名那么具有描述性,我认为原先在静态字段前写上类名才是更好的选择。不过,毕竟每个人的喜好和需求不同,如果你觉得它对你有用,既然提供了,那么就用咯。
元数据(注解)
@interface MyAnnotationForMethods {
int index();
String info();
String developer() default "Sean GAO";
}
在使用时,我们需要在注解名称前面写上@,然后()中指定参数值,如:
@MyAnnotationForMethods (
index = 1,
info = "This is a method to test MyAnnotation.",
developer = "Somebody else"
)
public void testMethod1() {
// ...
}
注解的最大作用在于它在源代码的基础上增加了有用的信息,使得源代码的描述性更强。这些信息可以被代码之外的工具识别,从而可以很方便的增加外部功能,以及减少不必要的相关代码/文件维护。这里我想简单提一个超出J2SE(TM) 5.0范畴的话题:在未来的EJB 3.0规范中会有相当多的对注解的应用,让我们预览一下将来的无状态会话bean用注解来定义会是什么样子:
@Stateless public class BookShelfManagerBean {
public void addBook(Book aBook) {
// business logic goes here...
}
public Collection getAllBooks() {
// business logic goes here...
}
// ...
}
我们甚至不用写任何接口和部署描述符,这些工作将完全由外部工具通过读取注解加上反射来完成,这不是很好吗?
C风格格式化输出
Java总算也有类似C的printf()风格的方法了,方法名同样叫作printf(),这一特性依赖于前边提到的可变长度参数。举个例子来说,我们现在可以写:
System.out.printf("%s has a value of %d.%n", someString, a);
怎么样,看上去还不错吧?需要注意的是Java为了支持多平台,新增了%n标示符,作为对\n的补充。有关Java格式化输出的具体语法,请参考java.util.Formatter的API文档。
结语
其实不只是语言元素,J2SE(TM) 5.0的发布在其他很多方面都作了不小的改进,包括虚拟机、新的API类库等等,性能和功能上都有大幅提升。
对于主要靠J2EE吃饭的朋友来讲,也许真正意义上要在工作中充分利用这些新的元素,恐怕要等主流的J2EE服务器都支持J2EE(TM) 5.0的那一天了,对此我充满期待。