J2SE 5.0專題 之 語言特性 本文作者: 高宇翔(大胃) 1.1. 背景J2SE(TM) 5.0正式發布至今已超過3個月的時間了,就在前不久,大概是在兩周之前,Sun又發布了更新過的JDK 5.0 Update 1,改掉了一些第一個版本中出現的bug。 由於Java社群等待這一從1.4向5.0版本升級已經有相當長的一段時間,大家都很關心5.0中有哪些值得關注的變化,於是blog的相關信息滿天飛,我也興沖沖地在自己的blog中添上了一系列的文章。無奈這些blog文章,包括我自己的在內,通常都是泛泛而談,因此CSDN第二期Java電子雜志的編輯們計劃做一個專題對這一話題與相關人士進行一番深入的探討。 作為這期電子刊物的一部分,編輯們也邀請我更系統的探討一下:J2SE(TM) 5.0中新引入的語言特性究竟在實際中有哪些用途,以及為什麼要引入這些新特性。對此我深感榮幸。我本人很樂意將我的一些也許算得上經驗的Java經驗跟大家分享,希望這一篇小文能對大家了解J2SE(TM) 5.0有一定幫助。1.2. 准備工作首先,為了了解J2SE(TM) 5.0的新的語言特性,你需要下載新版的JDK,在這裡可以找到下載鏈接:http://java.sun.com/J2SE/1.5.0/download.JSP。當然,如果你已經有過手動配置Java環境的經歷,我也建議你使用一個支持J2SE(TM) 5.0的IDE,推薦Eclipse SDK 3.1 M4,或者NetBeans IDE 4.0。兩個都是開源免費的,且很容易找到(Eclipse不用說了,NetBeans IDE 4.0有與JDK 5.0 Update 1的捆綁版)。 說點題外話,Java的版本號自從1.2開始,似乎就多少顯得有點蹩腳。從1.2版本開始,Java (J2SE)被稱作Java 2,而不是Java 1.2,現在則顯得更加離奇:Java(TM) 2 Platform Standard Edition 5.0或者J2SE(TM) 5.0,而內部的版本號還是1.5.0。那麼到底是1、2、還是5呢?來看看Sun官方網站是怎麼說的: 從Java誕生至今已有9年時間,而從第二代Java平台J2SE算起也有5個年頭了。在這樣的背景下,將下一個版本的版本號從1.5改為5.0可以更好的反映出新版J2SE的成熟度、穩定性、可伸縮性和安全性。 好吧,現在我們將面對如下一些名稱,而它們指的基本上是同一個東西:l Tigerl Java(TM) 2 Platform Standard Edition 5.0l J2SE(TM) 5.0l Java version 1.5.0l …在本文中,為了方便起見,我將統一使用J2SE(TM) 5.0這個名稱。 如果你對Java各個版本的代號感興趣,就像這裡的"Tiger",可以參考如下網址:http://java.sun.com/J2SE/codenames.Html。透露一點:Java下一個版本(6.0)的代號是"Mustang"野馬,再下一個版本(7.0)的代號是"Dolphin"海豚。1.3. 概述J2SE(TM) 5.0引入了很多激進的語言元素變化,這些變化或多或少減輕了我們開發人員的一些編碼負擔,其中的大部分也必然會被應用到即將發布的J2EE(TM) 5.0中。主要的新特性包括:l 泛型l 增強的for循環l 自動裝箱和自動拆箱l 類型安全的枚舉l 可變長度參數l 靜態引入l 元數據(注解)l C風格的格式化輸出 這當中,泛型、枚舉和注解可能會占用較大的篇幅,而其余的因為用法直截了當,抑或相對簡單,我就稍作介紹,剩下的留給讀者去思考、去探索了。1.4. 泛型泛型這個題目相當大,大到完全可以就這個話題寫一本書。有關Java是否需要泛型和如何實現泛型的討論也早就在Java社群廣為流傳。終於,我們在J2SE(TM) 5.0中看到了它。也許目前Java對泛型的支持還算不上足夠理想,但這一特性的添加也經足以讓我們欣喜一陣了。 在接下來的介紹中,我們會了解到:Java的泛型雖然跟C++的泛型看上去十分相似,但其實有著相當大的區別,有些細節的東西也相當復雜(至少很多地方會跟我們的直覺背道而馳)。可以這樣說,泛型的引入在很大程度上增加了Java語言的復雜度,對初學者尤其是個挑戰。下面我們將一點一點往裡挖。 首先我們來看一個簡單的使用泛型類的例子:ArrayList
aList = new ArrayList(); aList.add(new Integer(1)); // ... Integer myInteger = aList.get(0);我們可以看到,在這個簡單的例子中,我們在定義aList的時候指明了它是一個直接受Integer類型的ArrayList,當我們調用aList.get(0)時,我們已經不再需要先顯式的將結果轉換成Integer,然後再賦值給myInteger了。而這一步在早先的Java版本中是必須的。也許你在想,在使用Collection時節約一些類型轉換就是Java泛型的全部嗎?遠不止。單就這個例子而言,泛型至少還有一個更大的好處,那就是使用了泛型的容器類變得更加健壯:早先,Collection接口的get()和Iterator接口的next()方法都只能返回Object類型的結果,我們可以把這個結果強制轉換成任何Object的子類,而不會有任何編譯期的錯誤,但這顯然很可能帶來嚴重的運行期錯誤,因為在代碼中確定從某個Collection中取出的是什麼類型的對象完全是調用者自己說了算,而調用者也許並不清楚放進Collection的對象具體是什麼類的;就算知道放進去的對象“應該”是什麼類,也不能保證放到Collection的對象就一定是那個類的實例。現在有了泛型,只要我們定義的時候指明該Collection接受哪種類型的對象,編譯器可以幫我們避免類似的問題溜到產品中。我們在實際工作中其實已經看到了太多的ClassCastException,不是嗎? 泛型的使用從這個例子看也是相當易懂。我們在定義ArrayList時,通過類名後面的<>括號中的值指定這個ArrayList接受的對象類型。在編譯的時候,這個ArrayList會被處理成只接受該類或其子類的對象,於是任何試圖將其他類型的對象添加進來的語句都會被編譯器拒絕。 那麼泛型是怎樣定義的呢?看看下面這一段示例代碼:(其中用E代替在實際中將會使用的類名,當然你也可以使用別的名稱,習慣上在這裡使用大寫的E,表示Collection的元素。)public class TestGenerics { Collection col; public void DOSth(E elem) { col.add(elem); // ... }}在泛型的使用中,有一個很容易有的誤解,那就是既然Integer是從Object派生出來的,那麼ArrayList當然就是ArrayList