posts - 0,  comments - 1,  trackbacks - 0

    JDK 5.0都是好几年前的产品了,她的特性早已不是什么新特性,想来大家都已经熟知了。只是自己一直没有认真去更新自己的知识,最近找到了《Thinking In Java 4th edition》的电子版(非扫描版),决心系统更新一下,这篇文章类似读书笔记吧。

    JDK 5.0在前一代的版本上有较大的改进,引入了许多新的语言特性,下面就一一道来。

静态导入(static import)

    静态导入非常简单,就是将一个类的静态变量或者方法导入,这样在引用时就不需要再写类名了,减少了代码输入的工作量。个人觉得这个带来的好处很是有限,如果不习惯还会对阅读代码带来一定麻烦,因为突然冒出来对一个变量或者方法的引用,还以为是当前的类定义的,可找半天找不到,就会非常困惑了。当然习惯以后也没什么大问题。例子如下:

1 import static java.lang.System.out;
2 
3 public class StaticImport {    
4     public static void main( String[] args ) {
5         out.println( "Good" );
6     }
7 }

    代码中用了最常用的输出语句,原来需要写System.out.println(),现在将out静态导入,则只需写out.println()了。通常导入的语句也可以写成import static java.lang.System.*来将该类中的所有静态变量和方法都导入。

    可以看到静态导入是非常简单的,只需记住虽然该特性一般称为“静态导入(static import)”,写代码时却要写成“import static”。

枚举类型(Enumerated Types)

    Java添加了对枚举类型的支持,使用enum关键字进行定义。下面是一个例子:

1 public enum Meal {
2     BREAKFAST, LUNCH, SUPPER;
3     public static void main( String[] args ) {
4         Meal m = Meal.BREAKFAST;
5         System.out.println( m );                          
6     }
7 }

    其中定义的每一个常量都是Meal的一个实例。可以看到枚举类型的定义非常像一个类,可以有方法,可以有构造函数,不过构造函数不能是public或者protected的;此外实例必须在一开始就定义。另外就是enum不能被继承,因为事实上定义了一个enum后,编译器会自动生成一个继承了java.lang.Enum的同名的类,而Java是不支持多重继承的,所以定义的enum就不能被继承了,这一点在某些时候带来了一些限制,比如想重用代码来扩展enum时。此外,注意上面定义中的语法,各个实例间用逗号分隔,实例全部结束后用分号。

    枚举类型还定义了一些方法:toString(), values()和ordinal()等。

    enum还支持静态导入,支持用在switch语句中。

Foreach循环

    JDK 5.0增加了一种新的循环方式,称为foreach,表示遍历包含的每一个值或对象。支持foreach的对象包括数组和实现了Iterable接口的对象。加入这种循环方式也是为了简化一些代码开发,尤其是对Collection容器的遍历,以前都需要得到Iterator,然后调用next()得到下一个对象,调用hasNext()判断是否还有,要不停写这种重复的代码,用foreach则很简单。如上面的枚举类型的values()返回所有实例的数组,则可以使用这一语法。

1 public enum Meal {
2     BREAKFAST, LUNCH, SUPPER;
3     public static void main( String[] args ) {
4         for ( Meal m : Meal.values() ) {
5             System.out.println( m );    
6         }                      
7     }
8 }
    这一特性也是很小的一个,没有它我们也能活,只是它给我们带来不少方便。

可变参数列表

    从C中借鉴来的功能,如果方法想要接受任意数目的参数的话,就可以用,得到的参数则用foreach语法一个一个读出来。以前则可能需要用一大堆的重载方法。
 1 public class Test {    
 2     public static void test( int  values ) {
 3         System.out.println( values.length );
 4         for ( int i : values ) {
 5             System.out.println( i );
 6         }
 7     }    
 8     public static void main( String[] args ) {
 9         test( 1 );
10         test( 12 );
11         test( 123 );
12     }
13 }
    上面代码中可以看到,可变参数列表还有个属性是length。

    可变参数列表和重载一起使用时,会更加复杂一些,还有可能产生歧义。不过编译器还算聪明,可以提示出问题。

自动装箱/解箱(auto-boxing/unboxing)

    Java中经常需要在原始数据类型和它们的包装类之间转换,尤其是将数据放入Collection容器中和取出来时。为了减少这些重复代码,JDK 5.0添加了自动装箱/解箱操作,从此之后,基本上可以将原始数据类型当作对象来操作了。

    TIJ4在Generics这一章的总结中进行了有趣的思考,如果现在重新设计Java语言的话,原始数据类型还有必要加入吗?因为完全可以用类和对象来代替,这样也就没有什么自动装箱/解箱的需要了。同样,数组似乎也没有存在的必要了,因为任何时候都可以使用Collection容器来存放对象。事实上,一些现代的语言都已经去掉了这两个在传统观念上编程语言中必不可少的东西了。由此可见,很多现有的东西都有必要重新审视一下。

返回类型协变(Covariant Return Type)

    协变听上去挺吓人的,好像是数学中见过这个说法,而covariant这个单词也很令人畏惧,不过其实就是“一同变化”的意思。以往版本的Java,子类扩展了父类,并覆盖(override)了父类的方法时,返回值类型只能完全按照父类的来。JDK 5.0添加了这一特性,以使子类中覆盖的方法可以返回父类方法的返回值类型的子类。挺拗口的,看个例子就明白了(该例子来自于TIJ4)。
 1 class Grain {}
 2 class Wheat extends Grain {}
 3 class Mill {
 4     Grain process() {
 5         return new Grain();
 6     }
 7 }
 8 class WheatMill extends Mill {
 9     Wheat process() {
10         return new Wheat();
11     }
12 }
    上面例子中Mill和WheatMill是继承关系,WheatMill覆盖了Mill的process()方法,返回值分别是Grain和Wheat,而这两者也是继承关系。因此这里就是“一同变化”了。

    这一特性也很简单,只是用在继承关系的方法覆盖的返回值上。  

StringBuilder, Scanner和I/O

    StringBuilder功能基本等同于StringBuffer,因为它没有考虑thread safe的问题,所以速度更快。

    Scanner是为了简化读取输入而引入的,它的构造函数能接收一个File对象,也能接受一个InputStream,然后调用next(), nextLine(), nextXXX()等方法就可得到输入,其中的XXX代表各种数据类型。此外,它还能接受实现了Readable接口的对象。

    Java的I/O在5.0里没什么太多变化,这里主要提一下在1.4里就加入的nio包,也即new io,到现在已经不新了,不过过去从来没研究过,这次也是看了一下原理,如果以后有需要再仔细研究吧。所谓的new io,是为了提高输入输出的速度而设计的,它使用了Buffer和Channel的概念,例如从一个源读取数据,只需要建一个Channel,然后在Channel上添加上Buffer,代码中就只需要从Buffer中得到数据了。由于使用了缓冲,自然速度就快了。和new io相对应的传统的io包也用nio进行了改写,因此即使不直接用nio,也会获得速度提升的好处。

Collection

    原来Collection下只包含3种,List, Set和Map。JDK 5.0加入了Queue,6.0中加入了Deque,由于Deque和Queue很接近,所以这里把6.0的特性也加了进来。Queue就是一个队列,用来实现“先进先出”的功能,从一端进去了只能从另一端出来;Deque则是双头队列,从两端都可以进入和出来。Queue和Deque都有一些自己的方法,方法名对不是以英语为母语的人还挺难记的。

    Queue的方法:element(), offer(e), peek(), poll(), remove(e)
    Deque扩展了Queue,因此它也有上面的方法,然而为了支持双端操作,因此还有一大堆方法:addFirst(e), offerFirst(e), addLast(e), offerLast(e), removeFirst(), pollFirst(), removeLast(), pollLast(), getFirst(), peekFirst(), getLast(), peekLast()等等,相当麻烦。

    在java.util中实现了Queue接口的只有一个PriorityQueue,另外的一些Queue在新的线程包里,用来实现线程的新功能。实现了Deque接口的有新的ArrayDeque和以前就有的LinkedList。

    此外,还增加了和enum配合的EnumSet和EnumMap。EnumSet里面只能放enum类型的值,而EnumMap的key只能为enum类型的值。

posted on 2008-12-02 17:54 飞马凉 阅读(264) 评论(0)  编辑  收藏

只有注册用户登录后才能发表评论。


网站导航:
 
<2024年12月>
24252627282930
1234567
891011121314
15161718192021
22232425262728
2930311234

留言簿

文章档案

搜索

  •  

最新评论