天天看點

分析一道奇葩面試題:final、finally與finalize的差別

記得剛開始工作的時候見過這個面試題,明明是毫不相幹的三個關鍵詞,每次面試總是把這三者放在一起,可能是它們名字非常像吧,今天讓我們聊一聊final、finally、finalize這三個各自的使用場景。
分析一道奇葩面試題:final、finally與finalize的差別

另外本人整理了20年面試題大全,包含spring、并發、資料庫、redis、分布式、dubbo、jvm、微服務等方面總結,需要的話自行領取:騰訊文檔

在java中,final可以用來修飾類,方法和變量(成員變量或局部變量)。下面将對其詳細介紹。

1.1 修飾類

  當用final修飾類的時,表明該類不能被其他類所繼承。當我們需要讓一個類永遠不被繼承,此時就可以用final修飾,但要注意:

final類中所有的成員方法都會隐式的定義為final方法。

1.2 修飾方法

使用final方法的原因主要有兩個:

  (1) 把方法鎖定,以防止繼承類對其進行更改。

  (2) 效率,在早期的java版本中,會将final方法轉為内嵌調用。但若方法過于龐大,可能在性能上不會有多大提升。是以在最近版本中,不需要final方法進行這些優化了。

final方法意味着“最後的、最終的”含義,即此方法不能被重寫。

注意:若父類中final方法的通路權限為private,将導緻子類中不能直接繼承該方法,是以,此時可以在子類中定義相同方法名的函數,此時不會與重寫final的沖突,而是在子類中重新地定義了新方法。

1.3 修飾變量

  final成員變量表示常量,隻能被指派一次,指派後其值不再改變。類似于c++中的const。

  當final修飾一個基本資料類型時,表示該基本資料類型的值一旦在初始化後便不能發生變化;如果final修飾一個引用類型時,則在對其初始化之後便不能再讓其指向其他對象了,但該引用所指向的對象的内容是可以發生變化的。本質上是一回事,因為引用的值是一個位址,final要求值,即位址的值不發生變化。 

  final修飾一個成員變量(屬性),必須要顯示初始化。這裡有兩種初始化方式,一種是在變量聲明的時候初始化;第二種方法是在聲明變量的時候不賦初值,但是要在這個變量所在的類的所有的構造函數中對這個變量賦初值。

  當函數的參數類型聲明為final時,說明該參數是隻讀型的。即你可以讀取使用該參數,但是無法改變該參數的值。

 finally作為異常處理的一部分,它隻能用在try/catch語句中,并且附帶一個語句塊,表示這段語句最終一定會被執行(不管有沒有抛出異常),經常被用在需要釋放資源的情況下。(×)(這句話其實存在一定的問題)

finally是異常進行中的一個關鍵字,通常的結構是這樣的:

它一般用于資源釋放,比如我們可以在finally塊中關閉資料庫連接配接,在這個結構中不管異常有沒有發生finally中的代碼都會執行。

但是finally中的代碼不是一定會被執行。在以下情況下不會執行finally中的代碼:

在進入try塊之前程式發生異常。

在try塊中調用了system.exit(0)終止了虛拟機的運作。

在try塊或catch塊中程式被中斷,比如說當機。

我們再來看下面這個例子:

程式運作的結果為:1

這個例子中finally中的代碼究竟是在return前還是return後執行的呢?你可能認為是在return 前執行的,我們來看一下程式執行的過程,執行到try塊中的return num的時候num的值1會被作為傳回值存放到棧中但是這時程式并不會傳回,而是去執行num++,num的值變為2,然後程式傳回結束,此時傳回的還是棧中的num值1。

我們再來看另一個例子:

這段程式的運作結果為2

在程式執行到try塊中的return num時将num中的值1作為傳回值儲存在棧中,然後執行finally塊中的代碼,return 2,此時棧中的傳回值變為了2,是以最後的結果為2.

總結一下:

finally在try塊和catch塊return執行後,傳回前執行。

如果finally中沒有return,則其執行結果不影響try和catch中已确定的傳回值。

如果finally中有return,則其執行結果會直接傳回。

 finalize()是在java.lang.object裡定義的,也就是說每一個對象都有這麼個方法。這個方法在gc啟動,該對象被回收的時候被調用。其實gc可以回收大部分的對象(凡是new出來的對象,gc都能搞定,一般情況下我們又不會用new以外的方式去建立對象),是以一般是不需要程式員去實作finalize的。

特殊情況下,需要程式員實作finalize,當對象被回收的時候釋放一些資源,比如:一個socket連結,在對象初始化時建立,整個生命周期内有效,那麼就需要實作finalize,關閉這個連結。

  使用finalize還需要注意一個事,調用super.finalize();

  一個對象的finalize()方法隻會被調用一次,而且finalize()被調用不意味着gc會立即回收該對象,是以有可能調用finalize()後,該對象又不需要被回收了,然後到了真正要被回收的時候,因為前面調用過一次,是以不會調用finalize(),産生問題。 是以,推薦不要使用finalize()方法,它跟析構函數不一樣。

針對最近很多人都在面試,我這邊也整理了相當多的面試專題資料,也有其他大廠的面經。希望可以幫助到大家。

面試題答案都整理成文檔筆記。也還整理了一些面試資料&最新2020收集的一些大廠的面試真題(都整理成文檔,小部分截圖),有需要的可以自行領取