Java核心 談談final、finally、 finalize有什麼不同?
問題
談談 final、finally、 finalize 有什麼不同?
解析
這三個簡單來講就是卡巴斯基和巴基斯坦,有個基巴關係。。。
final 修飾的元素都不可改變,類不能繼承,方法不能重寫,變數不能修改。
finally 在異常捕獲中保證了除程式/執行緒關閉的情況外,塊內的程式碼必須執行,通常用於關閉 JDBC 連線,unlock 鎖等。
finalize 是 Object 類的一個方法,將目標物件標記為特殊回收物件。因其不確定性(不能確定什麼時候執行 GC)和降低 GC 效率(標記為特殊回收物件反而會阻礙 GC 流程),已在 JDK 中被標記為 deprecated。
擴充套件
final 和 immutable 和區別
final List<String> strList = new ArrayList<>();
strList.add("Hello");
strList.add("world");
List<String> unmodifiableStrList = List.of("hello","world");
unmodifiableStrList.add("again");
複製程式碼
strList是 final,但可以改變其內部元素內容,因為 final 保證了引用不更改,但引用的元素可以更改;unmodifiableStrList 是 immutable,不僅建立了不可更改的引用,其元素內容也不可更改,所以在 呼叫 add 方法會拋異常。
immutable
immutable 一旦建立即不可改變,保證了執行緒安全,不用 synchroinzed 可以在多執行緒中使用,增加效率;缺點是在呼叫 get 方法後會 copy 一份,增加記憶體和 GC 負擔。
如何建立一個 immutable 物件?
- 將 class 宣告 final,限制擴充套件。
- 將所有成員變數宣告 final 和 private,不要實現 setter 方法。
- 構建物件時用深拷貝,因無法確定輸入物件不被修改。
- getter 方法使用 copy on write。
深拷貝和淺拷貝
基本資料型別賦的是值,物件賦值時用的是引用地址。淺拷貝只複製了一層,深拷貝複製了所有引用。copy on write
在修改源資料 A 的時候額外複製一份源資料 B,在 B 上修改再將指標指向 B。
finalize 和 cleaner
finalize 被設計為在被 GC 前呼叫,反而會阻礙 GC 處理,導致效率降低。
System.runFinalization()也不能確保 GC 什麼時候執行。
資源用完即顯式釋放,或者利用資源池來儘量重用。
java.lang.ref.Finalizer 會 swallow 異常。
private void runFinalizer(JavaLangAccess jla) {
// ... 省略部分程式碼
try {
Object finalizee = this.get();
if (finalizee != null && !(finalizee instanceof java.lang.Enum)) {
jla.invokeFinalize(finalizee);
// Clear stack slot containing this variable,to decrease
// the chances of false retention with a conservative GC
finalizee = null;
}
} catch (Throwable x) { }
super.clear();
}
複製程式碼
Cleaner 的實現利用了幻象引用(PhantomReference),使用 post-mortem 清理機制,更可靠和輕量。它有自己的執行執行緒,所以可以避免意外死鎖等問題。(下期詳解各種引用)
每一次成長,都想與你分享。