Java核心 -- final + finally + finalize
摘要:
修飾類
,代表不可以繼承擴充套件
修飾變數
,代表變數不可以修改
修飾方法
,代表方法不可以重寫
實踐
推薦使用fin...
- 修飾類 ,代表不可以繼承擴充套件
- 修飾變數 ,代表變數不可以修改
- 修飾方法 ,代表方法不可以重寫
實踐
-
推薦使用
final
關鍵字來明確表示 程式碼的語義和邏輯意圖 -
將方法或類宣告為
final
,明確表示不允許重寫或繼承 -
使用
final
修飾引數或變數,能夠避免意外賦值而導致的程式設計錯誤 -
final
變數產生了某種程度的不可變 (immutable)的效果,可以用於保護只讀資料 -
現在
JVM
足夠智慧,final
對效能的影響,在大部分情況下,都沒有必要考慮 ,應用程式更應該關注的是語義
final != immutable
Java目前沒有原生的immutable支援
// final只能約束strList這個引用不可以被賦值,但strList物件本身的行為是不受影響的 final List<String> strList = new ArrayList<>(); strList.add("Hello"); strList.add("World"); // since 9 List<String> unmodifiableStrList = List.of("Hello", "world"); // throw java.lang.UnsupportedOperationException unmodifiableStrList.add("again");
immutable類
-
final class
-
所以成員變數定義為
private final
,並且不要實現setter
方法 -
構造物件時,成員變數使用深度拷貝
來初始化
- 防禦程式設計:因為無法確保輸入物件不會被其它執行緒修改
-
如果需要實現
getter
,使用copy-on-write
原則
finally
- 保證重點程式碼一定要被執行 的一種機制
-
try-finally
和try-catch-finally
-
try-with-resources
(JDK 7引入)
try { System.exit(-1); } finally { // 不會執行 System.out.println("Print from finally"); }
finalize
-
java.lang.Object
中的一個protected
方法 - 設計目標: 保證物件在被GC前完成特定資源的回收
-
不推薦使用,在
JDK 9
中已經被標記為@Deprecated(since="9")
-
無法保證
finalize()
何時會執行,執行的結果是否符合預期- 如果使用不當會影響效能,導致程式死鎖、掛起等問題
-
一旦實現類非空的
finalize
方法,會導致物件回收呈現數量級 上的變慢(40~50倍) -
實現了
finalize
方法的物件是特殊公民 ,JVM需要對它們進行額外的處理-
finalize
本質上成為了快速回收的阻礙者 - 可能導致物件經過多個GC週期 才能被回收
-
-
System.runFinalization()
同樣是不可預測的 -
實踐中,
finalize
會拖慢GC,導致大量物件堆積 ,有可能導致OOM
-
對於消耗非常高頻的資源,不要指望
finalize
去承擔釋放資源的主要職責- 推薦做法:資源用完即顯式釋放 ,或者利用資源池 來複用
-
另外,
finalize
會掩蓋資源回收時的出錯資訊
java.lang.ref.Finalizer
// Throwable被生吞 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
-
Java平臺逐漸使用
java.lang.ref.Cleaner
替換掉原有的finalize
實現 -
Cleaner
的實現利用了幻象引用 (Phantom Reference)- 利用幻象引用 和引用佇列 ,保證物件被銷燬之前 做一些類似資源回收的工作
-
Cleaner
比finalize
更加輕量 ,更加可靠 -
每個
Cleaner
的操作都是獨立 的,都有自己的執行執行緒 ,可以避免意外死鎖 等問題 -
從可預測
的角度來判斷,
Cleaner
或者幻象引用 改善的程度依然是有限的- 由於種種原因導致幻象引用堆積 ,同樣會出現問題
-
Cleaner
適合作為最後的保證手段 ,而不能完全依賴Cleaner
進行資源回收
public class CleaningExample implements AutoCloseable { // A cleaner, preferably one shared within a library private static final Cleaner cleaner = Cleaner.create(); // State定義為static,為了避免由於普通的內部類隱含對外部物件的強引用,而導致外部物件無法進入幻象可達的狀態 static class State implements Runnable { State() { // initialize State needed for cleaning action } @Override public void run() { // cleanup action accessing State, executed at most once } } private final State state; private final Cleaner.Cleanable cleanable; public CleaningExample() { this.state = new State(); this.cleanable = cleaner.register(this, state); } @Override public void close() { cleanable.clean(); } }