通過前篇的《Java文件IO流的操作總結》,我們知道了基本輸入輸出流的使用方式,但是每次都需要在finally處關閉流資源,這樣操作起來既啰嗦又麻煩,有沒有更簡潔的方式呢?本篇就來講解jdk1.7引入的try with resources語法糖式寫法。
什么是語法糖
1.之所以稱之為語法糖,給人的感覺就是很甜,很甜。
2.在相同功能下,語法糖的寫法會讓代碼更加簡潔流暢,代碼更加語義自然。
3.通過編譯器在編譯期間以特定的字節碼或者特定的方式對這些語法做一些處理
4.語法糖雖然不會提供實質性的功能改進,但是它們或能提高性能、或能提升語法的嚴謹性、或能減少編碼出錯的機會。
使用try with resources捕獲異常
待讀取的文件內容
示例代碼
package com.lingyejun.io; import java.io.*; /** * Created by Lingye on 2018/9/28 15:03 */ public class SyntacticSugarTry { // 調用有finally的case值 public static final int OLD_TRY = 1; // 調用新式語法糖式的case值 public static final int SUGAR_TRY = 2; /** * 根據輸入參數執行不同方法 * * @param type * @return */ public InputStream invokeTry(int type) { InputStream inputStream = null; switch (type) { case OLD_TRY: inputStream = oldTryCatch(); break; case SUGAR_TRY: inputStream = newTryCatch(); break; default: System.out.println("error type"); } return inputStream; } /** * 采用舊式的finally寫法 * * @return */ public InputStream oldTryCatch(){ // 構建文件對象 File inputFile = new File("D:\\input.txt"); // 初始化輸入流 InputStream inputStream = null; try { // 創建字節輸入流 inputStream = new FileInputStream(inputFile); // 讀取到1KB字節數組中 byte[] buffer = new byte[12]; // 讀取數據並放到buffer數組中 inputStream.read(buffer); System.out.println("oldTryCatch讀取輸出"+new String(buffer)); } catch (Exception e) { e.printStackTrace(); } finally { if (inputStream != null) { try { // 關閉流過程,也有可能出現異常 inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } return inputStream; } /** * 采用語法糖式寫法 * * @return */ public InputStream newTryCatch() { // 構建文件對象 File inputFile = new File("D:\\input.txt"); // 初始化輸入流 InputStream returnStream = null; // try with resource 語法糖式寫法 try (InputStream inputStream = new FileInputStream(inputFile)) { byte[] buffer = new byte[12]; inputStream.read(buffer); System.out.println("newTryCatch讀取輸出"+new String(buffer)); returnStream = inputStream; } catch (Exception e) { e.printStackTrace(); } // 省略了繁瑣的finally return returnStream; } public static void main(String[] args) { SyntacticSugarTry sugarTry = new SyntacticSugarTry(); InputStream oldStream = sugarTry.invokeTry(OLD_TRY); InputStream sugarStream = sugarTry.invokeTry(SUGAR_TRY); // 檢查流是否正常關閉 try { // 再次嘗試讀取,檢查是否關閉 oldStream.read(); } catch (IOException e) { // 已關閉 System.out.println("oldStream 輸入流已關閉"); } try { // 再次嘗試讀取,檢查是否關閉 sugarStream.read(); } catch (IOException e) { // 已關閉 System.out.println("sugarStream 輸入流已關閉"); } } }
查看文件管道的關閉情況
語法糖式寫法,執行完畢后自動關閉輸入流
查看輸出結果
不難看出,語法糖的使用其實就是讓我們的寫的代碼更簡單,看起來也更容易理解。
使用原理
語法糖是一種幾乎每種語言或多或少都提供過的一些方便程序員開發代碼的語法,它只是編譯器實現的一些小把戲罷了,編譯期間以特定的字節碼或者特定的方式對這些語法做一些處理,開發者就可以直接方便地使用了。這些語法糖雖然不會提供實質性的功能改進,但是它們或能提高性能、或能提升語法的嚴謹性、或能減少編碼出錯的機會。
使用JD-GUI打開上面類的.class編譯文件后會發現編譯過后,編譯器給我們自動加上了資源流的close關閉動作(81行、95行)。
/* Error */ public InputStream newTryCatch() { // Byte code: // 0: new 49 java/io/File // 3: dup // 4: ldc 51 // 6: invokespecial 53 java/io/File:<init> (Ljava/lang/String;)V // 9: astore_1 // 10: aconst_null // 11: astore_2 // 12: aconst_null // 13: astore_3 // 14: aconst_null // 15: astore 4 // 17: new 55 java/io/FileInputStream // 20: dup // 21: aload_1 // 22: invokespecial 57 java/io/FileInputStream:<init> (Ljava/io/File;)V // 25: astore 5 // 27: bipush 12 // 29: newarray <illegal type> // 31: astore 6 // 33: aload 5 // 35: aload 6 // 37: invokevirtual 60 java/io/InputStream:read ([B)I // 40: pop // 41: getstatic 29 java/lang/System:out Ljava/io/PrintStream; // 44: new 64 java/lang/StringBuilder // 47: dup // 48: ldc 102 // 50: invokespecial 68 java/lang/StringBuilder:<init> (Ljava/lang/String;)V // 53: new 69 java/lang/String // 56: dup // 57: aload 6 // 59: invokespecial 71 java/lang/String:<init> ([B)V // 62: invokevirtual 74 java/lang/StringBuilder:append (Ljava/lang/String;)Ljava/lang/StringBuilder; // 65: invokevirtual 78 java/lang/StringBuilder:toString ()Ljava/lang/String; // 68: invokevirtual 37 java/io/PrintStream:println (Ljava/lang/String;)V // 71: aload 5 // 73: astore_2 // 74: aload 5 // 76: ifnull +55 -> 131 // 79: aload 5 // 81: invokevirtual 87 java/io/InputStream:close ()V // 84: goto +47 -> 131 // 87: astore_3 // 88: aload 5 // 90: ifnull +8 -> 98 // 93: aload 5 // 95: invokevirtual 87 java/io/InputStream:close ()V // 98: aload_3 // 99: athrow // 100: astore 4 // 102: aload_3 // 103: ifnonnull +9 -> 112 // 106: aload 4 // 108: astore_3 // 109: goto +15 -> 124 // 112: aload_3 // 113: aload 4 // 115: if_acmpeq +9 -> 124 // 118: aload_3 // 119: aload 4 // 121: invokevirtual 104 java/lang/Throwable:addSuppressed (Ljava/lang/Throwable;)V // 124: aload_3 // 125: athrow // 126: astore_3 // 127: aload_3 // 128: invokevirtual 82 java/lang/Exception:printStackTrace ()V // 131: aload_2 // 132: areturn // Line number table: // Java source line #75 -> byte code offset #0 // Java source line #77 -> byte code offset #10 // Java source line #79 -> byte code offset #12 // Java source line #80 -> byte code offset #27 // Java source line #81 -> byte code offset #33 // Java source line #82 -> byte code offset #41 // Java source line #83 -> byte code offset #71 // Java source line #84 -> byte code offset #74 // Java source line #85 -> byte code offset #127 // Java source line #88 -> byte code offset #131 // Local variable table: // start length slot name signature // 0 133 0 this SyntacticSugarTry // 9 13 1 inputFile java.io.File // 11 121 2 returnStream InputStream // 13 1 3 localObject1 Object // 87 16 3 localObject2 Object // 108 17 3 localObject3 Object // 126 2 3 e Exception // 15 1 4 localObject4 Object // 100 20 4 localThrowable Throwable // 25 69 5 inputStream InputStream // 31 27 6 buffer byte[] // Exception table: // from to target type // 27 74 87 finally // 17 100 100 finally // 12 126 126 java/lang/Exception }
參考文章: