一文解釋清楚:final?finally?finalize?


前言

這篇速讀文章來自面試的常客final、finally、finalize。說實話不大很理解為啥很多面試官喜歡把這三者放在一起。這完全不相干啊!不過既來之則安之吧。

今天讓我們聊一聊final、finally、finalize各自的場景。

正題

引子

小A:MDove,我最近發現很多文章會把final、finally、finalize放在一起比較。恕我愚鈍,我實在不知道它們有什麼關係。

MDove:它們的關係你竟然看不出來?這不就是:這三者就是卡巴斯基、巴基斯坦和小丑巴基的關係,有個基巴關係!

小A:啊?……

MDove:它們三者的確沒有什麼關係,估計大家是看它們長得像,所以弄到一起去比較吧。今天咱們也別談三者的關係了,咱們就一個個聊一聊它們的特點。先從最常用的final開始:

final

MDove:對於我們來說final是很基礎的關鍵字。final可以用來修飾類、方法、變量

1、final修飾的class,代表不可以繼承擴展。2、final的方法也是不可以重寫的。3、final修飾的變量是不可以修改的。

MDove:咱們重點提一提第3點,這裡所謂的不可修改對於基本類型來來,的確是不可以修改。而對於引用類型來說,只能說不能重新賦值。也就是不能改變引用地址。但是作為引用類型,它內部所包含的內容如果不是final則可以隨意修改咯,就像:

final List<string> arryList = new ArrayList<>();
arryList.add("AAA");
arryList.add("BBB");
List<string> unmodifiableArrayList = List.of("AAA", "BBB");
unmodifiableArrayList.add("CCC");
/<string>/<string>

MDove

:final只能約束arryList這個引用不可以被重新賦值,但是arryList對象行為不被final影響,也就是說add是沒問題的。如果我們希望對象行為不可變,我們就需要使用List.of(),這個方法創建的對象本身就是不可變的,因此最後那句add是會在運行時拋出異常的。

MDove:我們都知道final聲明的變量需要顯示的給它賦初始值的。這裡考考你,如果不想直接給它賦值,那應該怎麼做?

小A:我猜…需要在構造方法裡邊吧?

MDove:沒錯的確是這樣:

fianl int num;
final int num2 = 666;
public Test(){
num = 666;
}

MDove:前面的文章,我們有提到其實這倆種寫法,對於編譯的class文件是等價的。

MDove:關於final,還有個有趣的地方。在很多文章中,會提到在特定場景下final能夠提高性能。比如:利用final可能有助於JVM將方法進行內聯,可以改善編譯器進行條件編譯的能力等等。說實話這種假設完全沒有考慮的必要。

finally

MDove:接下來讓我們來聊一聊finally。提到finally,那麼try-catch就逃不掉了。finally 則是Java保證重點代碼一定要被執行的一種機制。最常用的地方:通過try-catch-finally來進行類似資源釋放、保證解鎖等動作。比如:

FileInputStream inputStream = null;
try {
inputStream = new FileInputStream(new File("test"));
System.out.println(inputStream.read());
} catch (IOException e) {
throw new RuntimeException(e.getMessage(), e);
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
}

MDove:這裡提一點,try-finally也是可以的。不過這裡個人不建議省略掉catch。因為前一段時間我們就踩到了這個坑。我們項目裡捕獲的異常,一般都會在catch裡通過Error的CallBack傳出去,打Log。那次我們在追一個Bug的時候,發現竟然什麼Log都沒有。後來才發現,出Bug的地方,try異常後沒有做任何處理直接finally,導致沒有辦法看到Log日誌,浪費了很多時間。

MDove:當然關於finally中釋放資源的操作,更推薦使用Java7中添加的try-with-resources語句。簡單,高效~比如:

try (FileInputStream inputStream = new FileInputStream(new File("test"))) {
System.out.println(inputStream.read());
} catch (IOException e) {
throw new RuntimeException(e.getMessage(), e);
}

這二者的寫法是等價的。當然這只是一種語法糖~

MDove:當然有些喪心病狂的題目,會問你什麼情況下finally不執行。這種情況就不執行了:

try {
System.exit(1);
} finally{
System.out.println(“程序都死了,我還執行個毛線~”);
}

MDove:可以看出,能導致程序停止的操作,finally都沒辦法執行了。說實話這真的沒有啥意義…

MDove:最後我們來聊一聊finalize。

finalize

MDove

:說實話,我們日常開發中finalize用的並不多,而且也不被推薦使用。甚至在Java9中,明確將Object.finalize()標記為deprecated!

MDove:關於finalize說白了,它設計之初的作用就是:在CG要回收某個對象時,讓這個對象有底氣的大喊一聲:“報告,我還能再搶救一下!”。但是也正是因為如此,JVM要對它進行額外處理。finalize也就成為了CG回收的阻礙者,也就會導致這個對象經過多個垃圾收集週期才能被回收。

MDove:因為我自己對finalize瞭解也不是很深,所以關於finalize的內容就暫時讓我們止步於此。如果你還是感興趣,可以搜一搜相關的分析文章。

MDove:不知道一路捋到這,你是不是多少對這三者有了一些瞭解。實話實說,他們三者瞭解瞭解就完OJBK了,知道它們設計的初衷,瞭解它們的用法。可以靈活多變的應用到業務中就哦了。過分的追求一些“奇技淫巧”,反而失去了設計本身的意義。

小A:真是學無止境,我覺得我好想選錯了行業。

劇終