JDK 5.0都是好幾年前的產(chǎn)品了,她的特性早已不是什么新特性,想來大家都已經(jīng)熟知了。只是自己一直沒有認(rèn)真去更新自己的知識(shí),最近找到了《Thinking In Java 4th edition》的電子版(非掃描版),決心系統(tǒng)更新一下,這篇文章類似讀書筆記吧。
JDK 5.0在前一代的版本上有較大的改進(jìn),引入了許多新的語言特性,下面就一一道來。
靜態(tài)導(dǎo)入(static import)
靜態(tài)導(dǎo)入非常簡(jiǎn)單,就是將一個(gè)類的靜態(tài)變量或者方法導(dǎo)入,這樣在引用時(shí)就不需要再寫類名了,減少了代碼輸入的工作量。個(gè)人覺得這個(gè)帶來的好處很是有限,如果不習(xí)慣還會(huì)對(duì)閱讀代碼帶來一定麻煩,因?yàn)橥蝗幻俺鰜韺?duì)一個(gè)變量或者方法的引用,還以為是當(dāng)前的類定義的,可找半天找不到,就會(huì)非常困惑了。當(dāng)然習(xí)慣以后也沒什么大問題。例子如下:
2
3 public class StaticImport {
4 public static void main( String[] args ) {
5 out.println( "Good" );
6 }
7 }
代碼中用了最常用的輸出語句,原來需要寫System.out.println(),現(xiàn)在將out靜態(tài)導(dǎo)入,則只需寫out.println()了。通常導(dǎo)入的語句也可以寫成import static java.lang.System.*來將該類中的所有靜態(tài)變量和方法都導(dǎo)入。
可以看到靜態(tài)導(dǎo)入是非常簡(jiǎn)單的,只需記住雖然該特性一般稱為“靜態(tài)導(dǎo)入(static import)”,寫代碼時(shí)卻要寫成“import static”。
枚舉類型(Enumerated Types)
Java添加了對(duì)枚舉類型的支持,使用enum關(guān)鍵字進(jìn)行定義。下面是一個(gè)例子:
2 BREAKFAST, LUNCH, SUPPER;
3 public static void main( String[] args ) {
4 Meal m = Meal.BREAKFAST;
5 System.out.println( m );
6 }
7 }
其中定義的每一個(gè)常量都是Meal的一個(gè)實(shí)例。可以看到枚舉類型的定義非常像一個(gè)類,可以有方法,可以有構(gòu)造函數(shù),不過構(gòu)造函數(shù)不能是public或者protected的;此外實(shí)例必須在一開始就定義。另外就是enum不能被繼承,因?yàn)槭聦?shí)上定義了一個(gè)enum后,編譯器會(huì)自動(dòng)生成一個(gè)繼承了java.lang.Enum的同名的類,而Java是不支持多重繼承的,所以定義的enum就不能被繼承了,這一點(diǎn)在某些時(shí)候帶來了一些限制,比如想重用代碼來擴(kuò)展enum時(shí)。此外,注意上面定義中的語法,各個(gè)實(shí)例間用逗號(hào)分隔,實(shí)例全部結(jié)束后用分號(hào)。
枚舉類型還定義了一些方法:toString(), values()和ordinal()等。
enum還支持靜態(tài)導(dǎo)入,支持用在switch語句中。
Foreach循環(huán)
JDK 5.0增加了一種新的循環(huán)方式,稱為foreach,表示遍歷包含的每一個(gè)值或?qū)ο?。支持foreach的對(duì)象包括數(shù)組和實(shí)現(xiàn)了Iterable接口的對(duì)象。加入這種循環(huán)方式也是為了簡(jiǎn)化一些代碼開發(fā),尤其是對(duì)Collection容器的遍歷,以前都需要得到Iterator,然后調(diào)用next()得到下一個(gè)對(duì)象,調(diào)用hasNext()判斷是否還有,要不停寫這種重復(fù)的代碼,用foreach則很簡(jiǎn)單。如上面的枚舉類型的values()返回所有實(shí)例的數(shù)組,則可以使用這一語法。
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 }
可變參數(shù)列表
從C中借鑒來的功能,如果方法想要接受任意數(shù)目的參數(shù)的話,就可以用,得到的參數(shù)則用foreach語法一個(gè)一個(gè)讀出來。以前則可能需要用一大堆的重載方法。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 }
可變參數(shù)列表和重載一起使用時(shí),會(huì)更加復(fù)雜一些,還有可能產(chǎn)生歧義。不過編譯器還算聰明,可以提示出問題。
自動(dòng)裝箱/解箱(auto-boxing/unboxing)
Java中經(jīng)常需要在原始數(shù)據(jù)類型和它們的包裝類之間轉(zhuǎn)換,尤其是將數(shù)據(jù)放入Collection容器中和取出來時(shí)。為了減少這些重復(fù)代碼,JDK 5.0添加了自動(dòng)裝箱/解箱操作,從此之后,基本上可以將原始數(shù)據(jù)類型當(dāng)作對(duì)象來操作了。TIJ4在Generics這一章的總結(jié)中進(jìn)行了有趣的思考,如果現(xiàn)在重新設(shè)計(jì)Java語言的話,原始數(shù)據(jù)類型還有必要加入嗎?因?yàn)橥耆梢杂妙惡蛯?duì)象來代替,這樣也就沒有什么自動(dòng)裝箱/解箱的需要了。同樣,數(shù)組似乎也沒有存在的必要了,因?yàn)槿魏螘r(shí)候都可以使用Collection容器來存放對(duì)象。事實(shí)上,一些現(xiàn)代的語言都已經(jīng)去掉了這兩個(gè)在傳統(tǒng)觀念上編程語言中必不可少的東西了。由此可見,很多現(xiàn)有的東西都有必要重新審視一下。
返回類型協(xié)變(Covariant Return Type)
協(xié)變聽上去挺嚇人的,好像是數(shù)學(xué)中見過這個(gè)說法,而covariant這個(gè)單詞也很令人畏懼,不過其實(shí)就是“一同變化”的意思。以往版本的Java,子類擴(kuò)展了父類,并覆蓋(override)了父類的方法時(shí),返回值類型只能完全按照父類的來。JDK 5.0添加了這一特性,以使子類中覆蓋的方法可以返回父類方法的返回值類型的子類。挺拗口的,看個(gè)例子就明白了(該例子來自于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 }
這一特性也很簡(jiǎn)單,只是用在繼承關(guān)系的方法覆蓋的返回值上。
StringBuilder, Scanner和I/O
StringBuilder功能基本等同于StringBuffer,因?yàn)樗鼪]有考慮thread safe的問題,所以速度更快。Scanner是為了簡(jiǎn)化讀取輸入而引入的,它的構(gòu)造函數(shù)能接收一個(gè)File對(duì)象,也能接受一個(gè)InputStream,然后調(diào)用next(), nextLine(), nextXXX()等方法就可得到輸入,其中的XXX代表各種數(shù)據(jù)類型。此外,它還能接受實(shí)現(xiàn)了Readable接口的對(duì)象。
Java的I/O在5.0里沒什么太多變化,這里主要提一下在1.4里就加入的nio包,也即new io,到現(xiàn)在已經(jīng)不新了,不過過去從來沒研究過,這次也是看了一下原理,如果以后有需要再仔細(xì)研究吧。所謂的new io,是為了提高輸入輸出的速度而設(shè)計(jì)的,它使用了Buffer和Channel的概念,例如從一個(gè)源讀取數(shù)據(jù),只需要建一個(gè)Channel,然后在Channel上添加上Buffer,代碼中就只需要從Buffer中得到數(shù)據(jù)了。由于使用了緩沖,自然速度就快了。和new io相對(duì)應(yīng)的傳統(tǒng)的io包也用nio進(jìn)行了改寫,因此即使不直接用nio,也會(huì)獲得速度提升的好處。
Collection
原來Collection下只包含3種,List, Set和Map。JDK 5.0加入了Queue,6.0中加入了Deque,由于Deque和Queue很接近,所以這里把6.0的特性也加了進(jìn)來。Queue就是一個(gè)隊(duì)列,用來實(shí)現(xiàn)“先進(jìn)先出”的功能,從一端進(jìn)去了只能從另一端出來;Deque則是雙頭隊(duì)列,從兩端都可以進(jìn)入和出來。Queue和Deque都有一些自己的方法,方法名對(duì)不是以英語為母語的人還挺難記的。
Queue的方法:element(), offer(e), peek(), poll(), remove(e)
Deque擴(kuò)展了Queue,因此它也有上面的方法,然而為了支持雙端操作,因此還有一大堆方法:addFirst(e), offerFirst(e), addLast(e), offerLast(e), removeFirst(), pollFirst(), removeLast(), pollLast(), getFirst(), peekFirst(), getLast(), peekLast()等等,相當(dāng)麻煩。
在java.util中實(shí)現(xiàn)了Queue接口的只有一個(gè)PriorityQueue,另外的一些Queue在新的線程包里,用來實(shí)現(xiàn)線程的新功能。實(shí)現(xiàn)了Deque接口的有新的ArrayDeque和以前就有的LinkedList。
此外,還增加了和enum配合的EnumSet和EnumMap。EnumSet里面只能放enum類型的值,而EnumMap的key只能為enum類型的值。