使用 finally


老實說,要抓還是要拋? 撰寫的FileUtil範例並不是很正確,之後在學習輸入輸出時會談到,如果建構FileInputStream實例就會開啟檔案,不使用時,應該呼叫close()關閉檔案。FileUtil中是透過Scanner搭配FileInputStream來讀取檔案,實際上Scanner物件有個close()方法,可以關閉Scanner相關資源與搭配的FileInputStream

那麼要何時關閉資源呢?如下撰寫並不是很正確:

...
public static String readFile(String name) throws FileNotFoundException {
    StringBuilder builder = new StringBuilder();
    Scanner scanner = new Scanner(new FileInputStream(name));
    while (scanner.hasNext()) {
        builder.append(scanner.nextLine());
        builder.append('\n');
    }
    scanner.close();
    return builder.toString();
}
...

如果scanner.close()前發生了任何例外,執行流程就會中斷,因此scanner.close()就可能不會執行,因此Scanner及搭配的FileInputStream就不會被關閉。

你想要的是無論如何,最後一定要執行關閉資源的動作,trycatch語法還可以搭配finally,無論try區塊中有無發生例外,若撰寫有finally區塊,則finally區塊一定會被執行。例如:

package cc.openhome;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.Scanner;

public class FileUtil {
    public static String readFile(String name) throws FileNotFoundException {
        StringBuilder builder = new StringBuilder();
        Scanner scanner = null;
        try {
            scanner = new Scanner(new FileInputStream(name));
            while (scanner.hasNext()) {
                builder.append(scanner.nextLine());
                builder.append('\n');
            }
        } finally {
            if(scanner != null) {
                scanner.close();
            }
        }
        return builder.toString();
    }
}

由於finally區塊一定會被執行,這個範例中scanner原先是null,若FileInputStream建構失敗,則scanner就有可能還是null,因此在finally區塊中必須先檢查scanner是否有參考物件,有的話才進一步呼叫close()方法,否則scanner參考至null又打算呼叫close()方法,反而會拋出NullPointerException

如果程式撰寫的流程中先return了,而且也有寫finally區塊,那finally區塊會先執行完後,再將值傳回。例如,下面這個範例會先顯示「finally...」再顯示「1」:

package cc.openhome;
public class Main {    
    public static void main(String[] args) {
        System.out.println(test(true));
    }

    static int test(boolean flag) {
        try {
            if(flag) {
                return 1;
            }
        } finally {
            System.out.println("finally...");
        }
        return 0;
    }
}