java中的AutoCloseable介面與try with resources
阿新 • • 發佈:2018-11-03
try-with-resources引入的背景
很多Java程式都需要操作一些資源,如檔案、流(streams)、套接字(sockets)和資料庫連線(database connections)。操作這些資源的時候得很小心,因為它們操作的時候獲取了作業系統的資源。所以你得保證,即使在發生錯誤的情況下也要把它們佔用的作業系統資源釋放掉。事實上,不正確的資源管理是導致生產環境應用中發生錯誤的常見原因,比如程式碼中發生了異常,而資料庫連線和檔案描述符(file descriptors)還是開啟狀態,這樣將頻繁導致伺服器資源耗盡的時候需要重啟,因為作業系統和伺服器通常都有資源的上限限制。
正確的操作方式是,操作資源完成後呼叫其close()
try/catch/finally
程式碼塊,它將保證finally中的close方法總能被呼叫。然而程式碼中會充斥著大量try/catch/finally
類似的程式碼塊。一些其它的程式語言,比如Python、Ruby都提供了語言級的特性,比如自動資源管理來解決這種問題。Java7引入了try-with-resources表示式來減少try/catch/finally樣板程式碼,程式碼如下:
//注意try後面的括號中,如果打開了多個資源,要用分號隔開
try (
FileOutputStream out = new FileOutputStream("output" );
FileInputStream in1 = new FileInputStream(“input1”);
FileInputStream in2 = new FileInputStream(“input2”)
) {
//這兒可以對三個流進行操作
} // out, in1 和 in2 將總會被關閉,不管是否發生異常
AutoCloseable介面
上面的try-with-resources
中的try後面的括號中的資源類必須得實現AutoCloseable介面,這樣就知道它們肯定有一個close()
方法以供呼叫。java.lang.AutoCloseable
void
方法close()
,該方法可能丟擲一個受檢異常(Checked Exception)。任何想要使用try-with-resources
的資源類都必須實現AutoCloseable
介面,並且強烈推薦各個資源類提供更詳細的異常類,而不是籠統的java.lang.Exception
,如果確定不可能有異常丟擲,也可以在重寫close()
方法的時候不丟擲異常。AutoCloseable介面有一個子介面
java.io.Closeable
。注意AutoCloseable位於java.lang
包下,而Closeable位於java.io
包下面。區別在於AutoCloseable針對的是任何資源,不僅僅是I/O,它不要求close()方法是冪等的,也就是說多次呼叫close()方法可能會有副作用。而Closeable介面是用於關閉I/O流的,java.io.InputStream
和java.io.OutputStream
這兩個抽象類都實現了Closeable介面,Closeable介面的close()方法要求是冪等的,多次呼叫不產生副作用。
try-with-resources語法糖揭祕
執行如下程式碼:
package com.pilaf;
import java.util.Arrays;
/**
* @description:
* @author: pilaf
* @create: 2018-10-20 21:20
*/
public class TestAutoCloseable {
public static void main(String[] args) {
//try後面小括號中的資源類都要實現AutoCloseable介面
try (ResourceOperation operation = new ResourceOperation()) {
System.out.println("使用資源類進行操作!");
} catch (Exception e) {
e.printStackTrace();
}
}
}
class ResourceOperation implements AutoCloseable {
@Override
public void close() throws Exception {
System.out.println("資源關閉了!");
}
}
控制檯輸出:
使用資源類進行操作!
資源關閉了!
可見,在資源操作完畢後,它的close()方法被自動呼叫。
將生成的Java位元組碼檔案反編譯後,看到如下的程式碼:
package com.pilaf;
import java.io.PrintStream;
public class TestAutoCloseable
{
public static void main(String[] paramArrayOfString)
{
try
{
ResourceOperation localResourceOperation = new ResourceOperation();
Object localObject1 = null;
try
{
System.out.println("使用資源類進行操作!");
}
catch (Throwable localThrowable2)
{
//先把localThrowable2賦值給localObject1,以便在finally塊中記錄
localObject1 = localThrowable2;
throw localThrowable2;
}
finally
{
if (localResourceOperation != null) {
if (localObject1 != null) {
try
{
localResourceOperation.close();
}
catch (Throwable localThrowable3)
{
//關閉資源的時候,呼叫其close方法丟擲的異常被新增為Suppressed異常了!
((Throwable)localObject1).addSuppressed(localThrowable3);
}
} else {
localResourceOperation.close();
}
}
}
}
catch (Exception localException)
{
localException.printStackTrace();
}
}
}
package com.pilaf;
import java.io.PrintStream;
import java.util.Arrays;
class ResourceOperation
implements AutoCloseable
{
public void close()
throws Exception
{
System.out.println("資源關閉了!");
}
}
可見,其實編譯好的位元組碼檔案中還是用try/catch/finally程式碼塊實現的。
參考網址:
https://www.oracle.com/technetwork/articles/java/trywithresources-401775.html