JDK 5.0都是好幾年前的產品了,她的特性早已不是什么新特性,想來大家都已經熟知了。只是自己一直沒有認真去更新自己的知識,最近找到了《Thinking In Java 4th edition》的電子版(非掃描版),決心系統更新一下,這篇文章類似讀書筆記吧。
JDK 5.0在前一代的版本上有較大的改進,引入了許多新的語言特性,下面就一一道來。
靜態導入(static import)
靜態導入非常簡單,就是將一個類的靜態變量或者方法導入,這樣在引用時就不需要再寫類名了,減少了代碼輸入的工作量。個人覺得這個帶來的好處很是有限,如果不習慣還會對閱讀代碼帶來一定麻煩,因為突然冒出來對一個變量或者方法的引用,還以為是當前的類定義的,可找半天找不到,就會非常困惑了。當然習慣以后也沒什么大問題。例子如下:
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關鍵字進行定義。下面是一個例子:
2 BREAKFAST, LUNCH, SUPPER;
3 public static void main( String[] args ) {
4 Meal m = Meal.BREAKFAST;
5 System.out.println( m );
6 }
7 }
其中定義的每一個常量都是Meal的一個實例??梢钥吹矫杜e類型的定義非常像一個類,可以有方法,可以有構造函數,不過構造函數不能是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()返回所有實例的數組,則可以使用這一語法。
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語法一個一個讀出來。以前則可能需要用一大堆的重載方法。2 public static void test( int

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( 1, 2 );
11 test( 1, 2, 3 );
12 }
13 }
可變參數列表和重載一起使用時,會更加復雜一些,還有可能產生歧義。不過編譯器還算聰明,可以提示出問題。
自動裝箱/解箱(auto-boxing/unboxing)
Java中經常需要在原始數據類型和它們的包裝類之間轉換,尤其是將數據放入Collection容器中和取出來時。為了減少這些重復代碼,JDK 5.0添加了自動裝箱/解箱操作,從此之后,基本上可以將原始數據類型當作對象來操作了。TIJ4在Generics這一章的總結中進行了有趣的思考,如果現在重新設計Java語言的話,原始數據類型還有必要加入嗎?因為完全可以用類和對象來代替,這樣也就沒有什么自動裝箱/解箱的需要了。同樣,數組似乎也沒有存在的必要了,因為任何時候都可以使用Collection容器來存放對象。事實上,一些現代的語言都已經去掉了這兩個在傳統觀念上編程語言中必不可少的東西了。由此可見,很多現有的東西都有必要重新審視一下。
返回類型協變(Covariant Return Type)
協變聽上去挺嚇人的,好像是數學中見過這個說法,而covariant這個單詞也很令人畏懼,不過其實就是“一同變化”的意思。以往版本的Java,子類擴展了父類,并覆蓋(override)了父類的方法時,返回值類型只能完全按照父類的來。JDK 5.0添加了這一特性,以使子類中覆蓋的方法可以返回父類方法的返回值類型的子類。挺拗口的,看個例子就明白了(該例子來自于TIJ4)。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 }
這一特性也很簡單,只是用在繼承關系的方法覆蓋的返回值上。
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類型的值。