使用try-with-resources優雅關閉資源


  JDK1.7之后,引入了try-with-resources,使得關閉資源操作無需層層嵌套在finally中,代碼簡潔不少,本質是一個語法糖,能夠使用try-with-resources關閉資源的類,必須實現AutoCloseable接口。

  1.7版本之前,傳統的關閉資源操作如下:

 
         
public static void main(String[] args){

FileInputStream fileInputStream = null;
try {
fileInputStream = new FileInputStream("file.txt");
fileInputStream.read();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
assert fileInputStream != null;
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

  可以看到,為了確保資源關閉正常,需要finall中再嵌入finally,try中打開資源越多,finall嵌套越深,可能會導致關閉資源的代碼比業務代碼還要多。

  但是使用了try-with-resources語法后,上面的例子可改寫為:

 
         
try(FileInputStream fileInputStream1 = new FileInputStream("file.txt")){
fileInputStream1.read();
} catch (IOException e) {
e.printStackTrace();
}

  如何判讀資源是否真的被關閉了呢,我們手寫個Demo:

  實現AutoCloseable的資源類

class MyResource implements AutoCloseable{

public void open(){
System.out.println("resource is open!");
}

@Override
public void close() throws Exception {
System.out.println("resource is close!");
}
}

  調用方:

 
         
public static void main(String[] args){

try(MyResource myResource = new MyResource()){
myResource.open();
} catch (Exception e) {
e.printStackTrace();
}
}

  輸出如下,可以看到close方法被自動調用了,

resource is open!
resource is close!

  底層原理是什么呢,看一下編譯后的class文件:

 
         
try {
MyResource myResource = new MyResource();
Throwable var2 = null;

try {
myResource.open();
} catch (Throwable var12) {
var2 = var12;
throw var12;
} finally {
if (myResource != null) {
if (var2 != null) {
try {
myResource.close();
} catch (Throwable var11) {
var2.addSuppressed(var11);
}
} else {
myResource.close();
}
}

}
} catch (Exception var14) {
var14.printStackTrace();
}

}

  很明顯,編譯器生成了finally代碼塊,並在其中調用了close 方法,同1.7之前的關閉資源操作的實現原理是相同的,但是可以看到,這里多調用了一個addSuppressed方法,這么做其實是為了處理異常屏蔽,什么是異常屏蔽,首先,我們先修改一下剛剛的Demo,使資源類在open和close方法中拋出異常,並且使用1.7之前的關閉資源的方法,資源類以及調用方代碼修改如下:

public void open() throws IOException {
System.out.println("resource is open!");
throw new IOException("open() exception!");
}

@Override
public void close() throws Exception {
System.out.println("resource is close!");
throw new IOException("close() exception!");
}
 
         
 
         
public static void main(String[] args) throws Exception {

MyResource myResource = null;

try{
myResource = new MyResource();
myResource.open();
}finally {
try {
myResource.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}

  控制台打印如下:

     

   open方法拋出的異常被自動忽略了,而異常信息丟失將導致程序調試困難,所以try-with-resources語法中加入了addSuppressed處理異常屏蔽,現在修改Demo為使用try-with-resource關閉資源,調用方代碼如下:

 
         
public static void main(String[] args) throws Exception {

try(MyResource myResource = new MyResource()){
myResource.open();
}

  控制台打印如下

  

  異常信息中多了提示:close方法中拋出的異常被open方法中拋出的異常抑制了。

  其他問題:使用try-catch-resources,並不能完全保證資源被關閉,在javaBIO中,使用了大量的裝飾器模式,調用裝飾類的close方法時實際是在調用其中包裹的流的close方法,但是在調用包裹的流的close方法時,裝飾類還做了一些其他的操作,如果這些操作出現異常,將導致包裹流的close方法被跳過,資源沒有被正確關閉,正確的方式是在try中單獨聲明底層資源類以及裝飾類,這樣就可以保證,每個類的close方法都被調用。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM