1. 程式人生 > >Java 7中的Try-with-resources

Java 7中的Try-with-resources

原文連結 作者:Jakob Jenkov 譯者:fangqiang08([email protected])
Try-with-resources是java7中一個新的異常處理機制,它能夠很容易地關閉在try-catch語句塊中使用的資源。

利用Try-Catch-Finally管理資源(舊的程式碼風格)

在java7以前,程式中使用的資源需要被明確地關閉,這個體驗有點繁瑣。

下面的方法讀取檔案,然後用System.out列印:

private static void printFile() throws IOException {
    InputStream input = null;

    try {
        input = <strong>new FileInputStream("file.txt")</strong>;

        int data = <strong>input.read()</strong>;
        while(data != -1){
            System.out.print((char) data);
            data = <strong>input.read()</strong>;
        }
    } finally {
        if(input != null){
            <strong>input.close()</strong>;
        }
    }
}

上面程式碼中黑體字的程式可能會丟擲異常。正如你所看到的,try語句塊中有3個地方能丟擲異常,finally語句塊中有一個地方會能出異常。

不論try語句塊中是否有異常丟擲,finally語句塊始終會被執行。這意味著,不論try語句塊中發生什麼,InputStream 都會被關閉,或者說都會試圖被關閉。如果關閉失敗,InputStream’s close()方法也可能會丟擲異常。

假設try語句塊丟擲一個異常,然後finally語句塊被執行。同樣假設finally語句塊也丟擲了一個異常。那麼哪個異常會根據呼叫棧往外傳播?

即使try語句塊中丟擲的異常與異常傳播更相關,最終還是finally語句塊中丟擲的異常會根據呼叫棧向外傳播。

在java7中,對於上面的例子可以用try-with-resource 結構這樣寫:

private static void printFileJava7() throws IOException {

    try(FileInputStream input = new FileInputStream("file.txt")) {

        int data = input.read();
        while(data != -1){
            System.out.print((char) data);
            data = input.read();
        }
    }
}

注意方法中的第一行:

try(FileInputStream input = new FileInputStream("file.txt")) {

這就是try-with-resource 結構的用法。FileInputStream 型別變數就在try關鍵字後面的括號中宣告。而且一個FileInputStream 型別被例項化並被賦給了這個變數。

當try語句塊執行結束時,FileInputStream 會被自動關閉。這是因為FileInputStream 實現了java中的java.lang.AutoCloseable介面。所有實現了這個介面的類都可以在try-with-resources結構中使用。

當try-with-resources結構中丟擲一個異常,同時FileInputStreami被關閉時(呼叫了其close方法)也丟擲一個異常,try-with-resources結構中丟擲的異常會向外傳播,而FileInputStreami被關閉時丟擲的異常被抑制了。這與文章開始處利用舊風格程式碼的例子(在finally語句塊中關閉資源)相反。

使用多個資源

你可以在塊中使用多個資源而且這些資源都能被自動地關閉。下面是例子:

private static void printFileJava7() throws IOException {

    try(  FileInputStream     input         = new FileInputStream("file.txt");
          BufferedInputStream bufferedInput = new BufferedInputStream(input)
    ) {

        int data = bufferedInput.read();
        while(data != -1){
            System.out.print((char) data);
    data = bufferedInput.read();
        }
    }
}

上面的例子在try關鍵字後的括號裡建立了兩個資源——FileInputStream 和BufferedInputStream。當程式執行離開try語句塊時,這兩個資源都會被自動關閉。

這些資源將按照他們被建立順序的逆序來關閉。首先BufferedInputStream 會被關閉,然後FileInputStream會被關閉。

自定義AutoClosable 實現

這個try-with-resources結構裡不僅能夠操作java內建的類。你也可以在自己的類中實現java.lang.AutoCloseable介面,然後在try-with-resources結構裡使用這個類。

AutoClosable 介面僅僅有一個方法,介面定義如下:

public interface AutoClosable {

    public void close() throws Exception;
}

任何實現了這個介面的方法都可以在try-with-resources結構中使用。下面是一個簡單的例子:

public class MyAutoClosable implements AutoCloseable {

    public void doIt() {
        System.out.println("MyAutoClosable doing it!");
    }

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

doIt()是方法不是AutoClosable 介面中的一部分,之所以實現這個方法是因為我們想要這個類除了關閉方法外還能做點其他事。

下面是MyAutoClosable 在try-with-resources結構中使用的例子:

private static void myAutoClosable() throws Exception {

    try(MyAutoClosable myAutoClosable = new MyAutoClosable()){
        myAutoClosable.doIt();
    }
}

當方法myAutoClosable.doIt()被呼叫時,下面是列印到System.out的輸出:

MyAutoClosable doing it!
MyAutoClosable closed!

通過上面這些你可以看到,不論try-catch中使用的資源是自己創造的還是java內建的型別,try-with-resources都是一個能夠確保資源能被正確地關閉的強大方法。