譯註:但見新人笑,哪聞舊人哭。在你們都在興致勃勃的討論Java 8的時候,那個早被遺忘的Java 7,或許你歷來都沒有記得它的好。java
Java 8的發佈也有一個月了,我相信如今你們都在探索JDK 8中的新特性。可是,在你完全開始鑽研Java 8以前,最好先來回顧下Java 7有哪些新特性。若是你還記得的話,Java 6是沒有增長任何特性的,只是JVM的一些改動以及性能的提高,不過JDK 7卻是增長了很多有助於提高開發效率的很給力的特性。我如今寫這篇文章的目的是什麼呢?爲何別人都在討論Java 8的時候,我卻還在聊Java1.7的事?由於我認爲並非全部的Java開發人員都很清楚JDK 7中的改動,還有何時比新版本發佈的時候更適合介紹上一版本的特性的呢?我還不多看見有開發人員在代碼中使用自動資源管理(ARM),儘管IDE的輔助工具都已經支持這個特性了。不過確實看到有人在用string的switch功能以及<>在作類型推導,另外,也不多有人知道fork-join框架,或者在一個catch塊裏捕獲多個異常,或者在數值型字面量中使用下劃線。所以我借這個機會來寫一篇簡短的摘要,回顧一下這些能方便咱們平常開發工做的改動。NIO以及新的文件接口,還有不少API層面的改動也一樣值得關注。我相信和Java 8的lambda表達式結合起來後,寫出來的代碼確定會更加簡潔。git
類型推導算法
JDK 1.7引入一個新的操做符<>,也被稱做鑽石操做符,它使得構造方法也能夠進行類型推導 。在Java 7以前,類型推導只對方法可用,正如Joshua Bloch在Effiective Java第二版中所預言 的那樣,如今終於在構造方法中實現了。在這以前,你得在對象建立表達式的左右兩邊同時指定類型,如今你只須要在左邊指定就能夠了,就像下面這樣。數據庫
JDK 7以前服務器
Map<String, List<String>> employeeRecords = new HashMap<String, List<String>>(); List<Integer> primes = new ArrayList<Integer>();
JDK 7框架
Map<String, List<String>> employeeRecords = new HashMap<>(); List<Integer> primes = new ArrayList<>();
在Java 7中能夠少敲些代碼了,尤爲是在使用集合的時候,由於那裏大量用到了泛型。點擊這裏瞭解更多關於Java鑽石操做符的信息。(譯註:原文沒提供連接啊)工具
在switch中支持String性能
在JDK 7以前 ,只有整型才能用做switch-case語句的選擇因子。在JDK7中,你能夠將String用做選擇因子了。好比:操作系統
String state = "NEW"; switch (day) { case "NEW": System.out.println("Order is in NEW state"); break; case "CANCELED": System.out.println("Order is Cancelled"); break; case "REPLACE": System.out.println("Order is replaced successfully"); break; case "FILLED": System.out.println("Order is filled"); break; default: System.out.println("Invalid"); }
比較的時候會用到String的equals和hashCode()方法,所以這個比較是大小寫敏感的。在switch中使用String的好處是,和直接用if-else相比 ,編譯器能夠生成更高效的代碼。更詳細的說明請點擊這裏。線程
自動資源管理(Automatic Resource Management)
在JDK 7以前,咱們須要使用一個finally塊,來確保資源確實被釋放掉,無論try塊是完成了仍是中斷了。好比說讀取文件或者輸入流的時候,咱們須要在finally塊中關閉它們,這樣會致使不少的樣板代碼,就像下面這樣:
public static void main(String args[]) { FileInputStream fin = null; BufferedReader br = null; try { fin = new FileInputStream("info.xml"); br = new BufferedReader(new InputStreamReader(fin)); if (br.ready()) { String line1 = br.readLine(); 09 System.out.println(line1); } } catch (FileNotFoundException ex) { System.out.println("Info.xml is not found"); } catch (IOException ex) { System.out.println("Can't read the file"); } finally { try { if (fin != null) fin.close(); if (br != null) br.close(); } catch (IOException ie) { System.out.println("Failed to close files"); } } }
看下這段代碼 ,是否是不少樣板代碼?
而在Java 7裏面,你可使用try-with-resource的特性來自動關閉資源,只要是實現了AutoClosable和Cloaeable接口的均可以,Stream, File, Socket,數據庫鏈接等都已經實現了。JDK 7引入了try-with-resource語句,來確保每一個資源在語句結束後都會調用AutoCLosable接口的close()方法進行關閉。下面是Java 7中的一段示例代碼,它看起來但是簡潔多了:
public static void main(String args[]) { try (FileInputStream fin = new FileInputStream("info.xml"); BufferedReader br = new BufferedReader(new InputStreamReader(fin));) { if (br.ready()) { String line1 = br.readLine(); System.out.println(line1); } } catch (FileNotFoundException ex) { System.out.println("Info.xml is not found"); } catch (IOException ex) { System.out.println("Can't read the file"); } }
因爲Java負責關閉那些打開的資源好比文件和流這種,所以文件描述符泄露的事情應該不會再發生了,應該也不會再看到文件描述符錯誤的提示了。甚至JDBC 4.1都已經開始支持了AutoClosable了。
Fork Join框架
Fork/join框架是ExecutorService接口的實現,它使得你能夠充分利用現代服務器多處理器帶來的好處。這個框架是爲了那些能遞歸地拆分紅更小任務的工做而設計的。它的目標是去壓榨處理器的能力以提高程序的性能。就像別的ExecutorService的實現同樣,fork/join框架也是把任務分發給線程池中的多個線程。它的不一樣之處在於它使用的是一種工做竊取算法(work-stealing algorithm),這和生產者消費者的算法有很大的不一樣。已經處理完任務的工做線程能夠從別的繁忙的線程那裏竊取一些任務來執行。fork/join框架的核心是ForkJoinPool類,它繼承自AbstractExecutorService。ForkJoinPool類實現了核心的工做竊取算法,能夠執行ForkJoinTask進程。你能夠把代碼封裝在一個ForkJoinTask的子類裏,好比RecursiveTask或者RecursiveAction。更多信息就參考這裏。
數值字面量中使用下劃線
JDK 7中,你能夠在數值字面量中使用'_'來提高可讀性。這對在源代碼中使用了大數字的人來講尤爲有用,例如在金融或者計算領域中。比方說這麼寫,
int billion = 1_000_000_000; // 10^9 long creditCardNumber = 1234_4567_8901_2345L; //16 digit number long ssn = 777_99_8888L; double pi = 3.1415_9265; float pif = 3.14_15_92_65f;
你能夠在合適的位置插入下劃線使得它可讀性更強,好比說一個很大的數字能夠每隔三位放一個下劃線,對於信用卡卡號而言,一般是16位長度,你能夠每隔4個數字就放一個下劃線,就如它們在卡片上所顯示的那樣。順便說一句,要記住,你不能在小數後面,或者數字的開始和結束的地方放下劃線。好比說,下面的數值字面量就是不正確的,由於它們錯誤地使用了下劃線:
double pi = 3._1415_9265; // underscore just after decimal point long creditcardNum = 1234_4567_8901_2345_L; //underscore at the end of number long ssn = _777_99_8888L; //undersocre at the beginning
你能夠讀下個人這篇文章瞭解更多的一些關於下劃線使用的例子。
在一個catch塊中捕獲多個異常
JDK 7中,單個catch塊能夠處理多個異常類型。
好比說在JDK 7以前,若是你想捕獲兩種類型的異常你得須要兩個catch塊,儘管兩個的處理邏輯都是同樣的:
try { ...... } catch(ClassNotFoundException ex) { ex.printStackTrace(); } catch(SQLException ex) { ex.printStackTrace(); }
而在JDK 7中,你只須使用一個catch塊就搞定了,異常類型用‘|’進行分隔:
try { ...... } catch(ClassNotFoundException|SQLException ex) { ex.printStackTrace(); }
順便說一句,這種用法是不包括異常的子類型的。好比說,下面這個多個異常的捕獲語句就會拋出編譯錯誤:
try { ...... } catch (FileNotFoundException | IOException ex) { ex.printStackTrace(); }
這是由於FileNotFoundException是IOException 的子類,在編譯的時候會拋出下面的錯誤: java.io.FileNotFoundException is a subclass of alternative java.io.IOException at Test.main(Test.java:18)。
瞭解更多請點擊這裏。
使用"ob"前綴的二進制字面量
JDK7中,對於整型類型(byte, short, int 和long)來講,你能夠用'0b'前綴來代表這是一個二進制的字面量,就像C/C++中那樣。在這以前,你只能使用8進制(前綴'0')或者16進制(前綴是'0x'或者‘0X')的字面量。
int mask = 0b01010000101;
這樣寫好處更明顯:
int binary = 0B0101_0000_1010_0010_1101_0000_1010_0010;
8.Java NIO 2
Java SE 7中引入了java.nio.file包,以及相關的java.nio.file.attibute包,全面支持了文件IO以及對默認文件系統的訪問。它同時還引入了Path 類,你能夠用它來表明操做系統中的任意一個路徑。新的文件系統API兼容老的版本,而且提供了幾個 很是實用的方法,能夠用來檢查,刪除,拷貝和移動文件。好比,你能夠在Java中判斷一個文件是不是隱藏文件。你還能夠在Java中建立軟連接和硬連接。JDK 7的新的文件API還可以使用通配符來進行文件的搜索。你還能夠用它來監測某個目錄 是否有變更。我推薦你看下它的官方文檔來了解更多的一些有意思的特性。
G1垃圾回收器
JDK7中引入了一個新的垃圾回收器,G1,它是Garbage First的縮寫。G1回收器優先回收垃圾最多的區域。爲了實現這個策略它把堆分紅了多個區域,就比如Java 7以前分紅三個區域那樣(新生代,老生代和持久代)。G1回收器是一個可預測的回收器,同時對那些內存密集型的程序它還能保證較高的吞吐量。
重拋異常的改進
Java SE 7的編譯器和以前的版本相比,在從新拋出異常這塊進行了更精確的分析。這使得你在方法聲明的throws子句中能夠指定更精確的異常類型。在JDK 7以前,重拋的異常的類型被認爲是catch參數中的指定的異常類型。好比說,若是你的try塊中拋出了一個ParseException以及一個IOException,爲了捕獲全部的異常,而後從新拋出來,你會去捕獲Exception類型的異常,而且聲明你的方法拋出的異常類型是Exception。這種方式有點不太精確,由於你實際拋出的是一個通用的Exception類型,而調用你的方法的語句須要去捕獲這個通用的異常。看一下Java 1.7前的這段異常處理的代碼可能你會更明白些:
public void obscure() throws Exception{ try { new FileInputStream("abc.txt").read(); new SimpleDateFormat("ddMMyyyy").parse("12-03-2014"); } catch (Exception ex) { System.out.println("Caught exception: " + ex.getMessage()); throw ex; } }
JDK 7之後你就能夠在方法的throws子句中明確的指定異常類型了。精確的異常重拋指的是,若是你在catch塊中從新拋出異常,實際真正拋出的異常類型會是:
你的try塊拋出的異常
尚未被前面的catch塊處理過,而且
catch的參數類型是Exception的某個子類。
這使得異常重拋變得更精確。你能夠更準確的知道方法拋出的是何種異常,所以你能夠更好的處理它們,就像下面這段代碼這樣:
public void precise() throws ParseException, IOException { try { new FileInputStream("abc.txt").read(); new SimpleDateFormat("ddMMyyyy").parse("12-03-2014"); } catch (Exception ex) { System.out.println("Caught exception: " + ex.getMessage()); throw ex; } }
Java SE 7的編譯器容許你在preciese() 方法聲明的throws子句中指定ParseException和IOException類型,這是由於你拋出的異常是聲明的這些異常類型的父類,好比這裏咱們拋出的是java.lang.Exception,它是全部受檢查異常的父類。有的地方你會看到catch參數中帶final關鍵字,不過這個再也不是強制的了。
這些就是JDK 7中全部你應該回顧的內容了。這些新特性對寫出整潔的代碼以及提高開發效率很是有用。有了Java 8中的lambda表達式,Java中的代碼整潔之道則又上了一個新的里程碑。若是你認爲我這篇文章中漏掉了Java 1.7任何有用的特性,請記得提醒我。
P.S. 若是你喜歡讀書的話,那你也必定會喜歡Packet Publication的這本Java 7 New features Cookbook。