天天看點

finally塊一定會執行嗎?

作者:海的赤子

finally是Java中一個非常常見的關鍵詞,我們經常使用這個關鍵詞來做一些後置操作,我們先來看一下finally的基本用法

finally的基本用法

// 方式一:try catch finally 捕獲異常
try {

} catch(Exception e) {

} finally {

}
 
// 方式二 不捕獲異常,
try {

} finally {

}           

我們看到了finally的用法,那麼finally被經常用作幹什麼呢?

finally的作用

  • 釋放流
public static void testReleaseStream() {
    File file = new File("D:\\logs\\common-error.log");
    FileInputStream fis = null;
    try {
        StringBuilder sb = new StringBuilder();
        fis = new FileInputStream(file);
        int len;
        byte[] buf = new byte[1024];
        while ((len = fis.read(buf)) != -1) {
            sb.append(new String(buf, 0, len));
        }
        System.out.println(sb.toString());

    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (fis != null) {
            try {
                fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}           
  • 釋放記憶體
private static Map<String, Student> studentCache = new HashMap<>();


public static void refreshCache() {
    Map<String, Student> oldCache = studentCache;
    try {
        Map<String, Student> student = getStudent();
        studentCache = student;
    } finally {
        oldCache.clear();
    }
    
}

// 模拟擷取緩存
private static Map<String, Student> getStudent() {
    return new HashMap<>();
}           
  • 移除ThreadLocal變量
private static final ThreadLocal<String> stringThreadLocal = new ThreadLocal<>();

public static void testReleaseThreadLocal() {
    try {
        stringThreadLocal.set("test");
        // 執行相關業務代碼
    } finally {
        stringThreadLocal.remove();
    }
}           

回到我們今天要看的核心問題,finally塊一定會執行麼?實際上,finally不一定會執行

  • 未進入try代碼塊
當代碼并未執行帶try的代碼塊時,finally并不會被執行
  • 執行了System.exit(0);
當執行System.exit(0);時,JVM會直接退出,是以finally代碼塊不會執行

這兩個情況我們需要關注第一種情況,因為第二種一般都不會使用。

public static void testReleaseThreadLocal() {
    stringThreadLocal.set("test");
    // 在此處執行某些代碼,并且出現異常
    try {
        
        // 執行相關業務代碼
    } finally {
        stringThreadLocal.remove();
    }
    System.exit(0);
}           
假如我們使用上面的方式來使用try finally,那麼finally代碼塊便不會執行,ThreadLocal也不會被清理,而我們采用下面的方式便不會出現問題
public static void testReleaseThreadLocal() {
    
    try {
        stringThreadLocal.set("test");
        // 在此處執行某些代碼,并且出現異常
        // 執行相關業務代碼
    } finally {
        stringThreadLocal.remove();
    }
    System.exit(0);
}           
當使用上面的方式時,即使代碼出現異常,finally代碼塊也會執行,是以ThreadLocal可以正常清理

進階

我們來簡單介紹一下關閉流的進階用法

public static void tryAdvance() {
    File file = new File("D:\\logs\\common-error.log");

    try (FileInputStream fis = new FileInputStream(file)) {
        StringBuilder sb = new StringBuilder();

        int len;
        byte[] buf = new byte[1024];
        while ((len = fis.read(buf)) != -1) {
            sb.append(new String(buf, 0, len));
        }
        System.out.println(sb.toString());

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

}           
以前我們都需要手動關閉流,每次都需要寫一堆關閉流的代碼,一點都不好看,通過這種方式,便可以實作自動關閉流,這個就是常說的try-with-resources語句