1. 程式人生 > >java中的AutoCloseable介面與try with resources

java中的AutoCloseable介面與try with resources

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

介面是從Java SE7開始引入的,它僅僅提供了一個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.InputStreamjava.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