1.switch語句支持字符串變量
public String getTypeOfDayWithSwitchStatement(String dayOfWeekArg) { String typeOfDay; switch (dayOfWeekArg) { case "Monday": typeOfDay = "Start of work week"; break; case "Tuesday": case "Wednesday": case "Thursday": typeOfDay = "Midweek"; break; case "Friday": typeOfDay = "End of work week"; break; case "Saturday": case "Sunday": typeOfDay = "Weekend"; break; default: throw new IllegalArgumentException("Invalid day of the week: " + dayOfWeekArg); } return typeOfDay; }
switch 語句比較表達式中的String對象和每個case標簽關聯的表達式,就好像它是在使用String.equals方法一樣;因此,switch語句中 String對象的比較是大小寫敏感的。相比於鏈式的if-then-else語句,Java編譯器通常會從使用String對象的switch語句中生成更高效的字節碼。
2.泛型實例化類型自動推斷
以下兩個語句等價:
ArrayList<String> al1 = new ArrayList<String>(); // Old ArrayList<String> al2 = new ArrayList<>(); // New
3. 新的整數字面表達方式 - "0b"前綴和"_"連數符
a. 表示二進制字面值的前綴0b。
比如以下三個變量的值相同:
byte b1 = 0b00100001; // New byte b2 = 0x21; // Old byte b3 = 33; // Old
b. 字面常量數字的下划線。用下划線連接整數提升其可讀性,自身無含義,不可用在數字的起始和末尾。
Java編碼語言對給數值型的字面值加下划線有嚴格的規定。如上所述,你只能在數字之間用下划線。你不能用把一個數字用下划線開頭,或者已下划線結尾。這里有一些其它的不能在數值型字面值上用下划線的地方:
- 在數字的開始或結尾
- 對浮點型數字的小數點附件
- F或L下標的前面
- 該數值型字面值是字符串類型的時候
float pi1 = 3_.1415F; // 無效的; 不能在小數點之前有下划線 float pi2 = 3._1415F; // 無效的; 不能在小數點之后有下划線 long socialSecurityNumber1=999_99_9999_L;//無效的,不能在L下標之前加下划線 int a1 = _52; // 這是一個下划線開頭的標識符,不是個數字 int a2 = 5_2; // 有效 int a3 = 52_; // 無效的,不能以下划線結尾 int a4 = 5_______2; // 有效的 int a5 = 0_x52; // 無效,不能在0x之間有下划線 int a6 = 0x_52; // 無效的,不能在數字開頭有下划線 int a7 = 0x5_2; // 有效的 (16進制數字) int a8 = 0x52_; // 無效的,不能以下划線結尾 int a9 = 0_52; // 有效的(8進制數) int a10 = 05_2; // 有效的(8進制數) int a11 = 052_; // 無效的,不能以下划線結尾
4.在單個catch代碼塊中捕獲多個異常,以及用升級版的類型檢查重新拋出異常
在Java 7中,catch代碼塊得到了升級,用以在單個catch塊中處理多個異常。如果你要捕獲多個異常並且它們包含相似的代碼,使用這一特性將會減少代碼重復度。下面用一個例子來理解。
Java 7之前的版本:
catch (IOException ex) { logger.error(ex); throw new MyException(ex.getMessage()); catch (SQLException ex) { logger.error(ex); throw new MyException(ex.getMessage()); }catch (Exception ex) { logger.error(ex); throw new MyException(ex.getMessage()); }
在Java 7中,我們可以用一個catch塊捕獲所有這些異常:
catch(IOException | SQLException | Exception ex){ logger.error(ex); throw new MyException(ex.getMessage()); }
如果用一個catch塊處理多個異常,可以用管道符(|)將它們分開,在這種情況下異常參數變量(ex)是定義為final的,所以不能被修改。這一特性將生成更少的字節碼並減少代碼冗余。
另一個升級是編譯器對重新拋出異常(rethrown exceptions)的處理。這一特性允許在一個方法聲明的throws從句中指定更多特定的異常類型。
與以前版本相比,Java SE 7 的編譯器能夠對再次拋出的異常(rethrown exception)做出更精確的分析。這使得你可以在一個方法聲明的throws從句中指定更具體的異常類型。
我們先來看下面的一個例子:
static class FirstException extends Exception { } static class SecondException extends Exception { } public void rethrowException(String exceptionName) throws Exception { try { if (exceptionName.equals("First")) { throw new FirstException(); } else { throw new SecondException(); } } catch (Exception e) { throw e; } }
這個例子中的try語句塊可能會拋出FirstException或者SecondException類型的異常。設想一下,你想在rethrowException方法聲明的throws從句中指定這些異常類型。在Java SE 7之前的版本,你無法做到。因為在catch子句中的異常參數e是java.lang.Exception類型的,catch子句對外拋出異常參數e,你只能在rethrowException方法聲明的throws從句中指定拋出的異常類型為java.lang.Exception (或其父類java.lang.Throwable)。
不過,在Java SE 7中,你可以在rethrowException方法聲明的throws從句中指定拋出的異常類型為FirstException和SecondException。Java SE 7的編譯器能夠判定這個被throw語句拋出的異常參數e肯定是來自於try子句,而try子句只會拋出FirstException或SecondException類型的異常。盡管catch子句的異常參數e是java.lang.Exception類型,但是編譯器可以判斷出它是FirstException或SecondException類型的一個實例:
public class Java7MultipleExceptions { public static void main(String[] args) { try{ rethrow("abc"); }catch(FirstException | SecondException | ThirdException e){ //以下賦值將會在編譯期拋出異常,因為e是final型的 //e = new Exception(); System.out.println(e.getMessage()); } } static void rethrow(String s) throws FirstException, SecondException, ThirdException { try { if (s.equals("First")) throw new FirstException("First"); else if (s.equals("Second")) throw new SecondException("Second"); else throw new ThirdException("Third"); } catch (Exception e) { //下面的賦值沒有啟用重新拋出異常的類型檢查功能,這是Java 7的新特性 // e=new ThirdException(); throw e; } } static class FirstException extends Exception { public FirstException(String msg) { super(msg); } } static class SecondException extends Exception { public SecondException(String msg) { super(msg); } } static class ThirdException extends Exception { public ThirdException(String msg) { super(msg); } } }
不過,如果catch捕獲的異常變量在catch子句中被重新賦值,那么異常類型檢查的分析將不會啟用,因此在這種情況下,你不得不在方法聲明的throws從句中指定異常類型為java.lang.Exception。
更具體地說,從Java SE 7開始,當你在單個catch子句中聲明一種或多種類型的異常,並且重新拋出這些被捕獲的異常時,需符合下列條件,編譯器才會對再次拋出的異常進行類型驗證:
- try子句會拋出該異常。
- 在此之前,沒有其他的catch子句捕獲該異常。
- 該異常類型是catch子句捕獲的多個異常中的一個異常類型的父類或子類。
5.try-with-resources語句
try-with-resources語句是一個聲明一個或多個資源的try語句。一個資源作為一個對象,必須在程序結束之后關閉。try-with-resources語句確保在語句的最后每個資源都被關閉,任何實現了Java.lang.AutoCloseable和java.io.Closeable的對象都可以使用try-with-resource來實現異常處理和關閉資源。
下面通過對比來體會這個新特性。
JDK1.7之前:
/** * JDK1.7之前我們必須在finally塊中手動關閉資源,否則會導致資源的泄露 * @author Liao * */ public class PreJDK7 { public static String readFirstLingFromFile(String path) throws IOException { BufferedReader br = null; try { br = new BufferedReader(new FileReader(path)); return br.readLine(); } catch (IOException e) { e.printStackTrace(); } finally {//必須在這里關閉資源 if (br != null) br.close(); } return null; } }
JDK1.7及以后版本
/** * JDK1.7之后就可以使用try-with-resources,不需要 * 我們在finally塊中手動關閉資源 * @author Liao */ public class AboveJDK7 { static String readFirstLineFromFile(String path) throws IOException { try (BufferedReader br = new BufferedReader(new FileReader(path))) { return br.readLine(); } } }
通過上面的對比,try-with-resources的優點
-
代碼精煉,在JDK1.7之前都有finally塊(這時可能會有一個問題,1.7后的finally將何去何從,還有用處嗎?當然有用了,try-with-resources只是幫助我們避免了資源關閉的重復工作,但它並沒有代替finally的作用啊,所以1.7后finally仍然是用來處理一定會被執行的代碼塊,不過這個功能使用場景不是很大。),如果使用一些擴建可能會將finally塊交由框架處理,如spring。JDK及以后的版本只要資源類實現了AutoCloseable或Closeable程序在執行完try塊后會自動close所使用的資源無論br.readLine()是否拋出異常,我估計針對JDK1.7像Spring這些框架也會做出一些比較大的調整。
-
代碼更完全。在出現資源泄漏的程序中,很多情況是開發人員沒有或者開發人員沒有正確的關閉資源所導致的。JDK1.7之后采用try-with-resources的方式,則可以將資源關閉這種與業務實現沒有很大直接關系的工作交給JVM完成,省去了部分開發中可能出現的代碼風險。
異常拋出順序
在JDK1.7之前如果rd.readLine()與rd.close()都拋出異常則只會拋出finally塊中的異常,不會拋出rd.readLine()中的異常,這樣經常會導致得到的異常信息不是調用程序想要得到的。
在JDK1.7及以后采用了try-with-resource機制,如果在try-with-resource聲明中拋出異常(如文件無法打開或無法關閉)的同時rd.readLine()也拋出異常,則只會拋出rd.readLine()的異常。
try-with-resource可以聲明多個資源(聲明語句之間分號分割,最后一個可忽略分號)。下面的例子是在一個ZIP文件中檢索文件名並將檢索后的文件存入一個txt文件中。
JDK1.7及以上版本:
public class AboveJDK7_2 { public static void writeToFileZipFileContents(String zipFileName,String outputFileName) throws java.io.IOException { java.nio.charset.Charset charset = java.nio.charset.Charset.forName("US-ASCII"); java.nio.file.Path outputFilePath = java.nio.file.Paths.get(outputFileName); //打開zip文件,創建輸出流 try ( java.util.zip.ZipFile zf = new java.util.zip.ZipFile(zipFileName); java.io.BufferedWriter writer = java.nio.file.Files.newBufferedWriter(outputFilePath, charset) ) {//遍歷文件寫入txt for (java.util.Enumeration entries = zf.entries(); entries.hasMoreElements();) { String newLine = System.getProperty("line.separator"); String zipEntryName = ((java.util.zip.ZipEntry) entries.nextElement()).getName() + newLine; writer.write(zipEntryName, 0, zipEntryName.length()); } } } }
注:上面的例子,無論正常執行還是有異常拋出,zf和write都會被執行close()方法,不過需要注意的是在JVM里調用的順序是與生命的順序相反。在JVM中調用的順訊為:
writer.close();
zf.close();
所以在使用時一定要注意資源關閉的順序。