1. 程式人生 > >談談 final finally finalize 區別

談談 final finally finalize 區別

try phi 提問 收集 shu www. 中修改 and com

聲明

本篇所涉及的提問,正文的知識點,全都來自於楊曉峰的《Java核心技術36講》,當然,我並不會全文照搬過來,畢竟這是付費的課程,應該會涉及到侵權之類的問題。

所以,本篇正文中的知識點,是我從課程中將知識點消耗後,用個人的理解、觀念所表達出來的文字,參考了原文,但由於是個人理解,因此不保證觀點完全正確,也不代表錯誤的觀點是課程所表達的。如果這樣仍舊還是侵權了,請告知,會將發表的文章刪掉。

當然,如果你對此課程有興趣,建議你自己也購買一下,新用戶立減 30,微信掃碼訂閱時還可以返現 6 元,相當於 32 元購買 36 講的文章,每篇文章還不到 1 元,蠻劃算的了。

技術分享圖片

提問

  • 談談 final、finally、finalize 有什麽不同?
  • 為什麽局部內部類和匿名內部類只能訪問局部final變量?

正文

emmm,說實話,感覺這一講好像沒什麽實質性內容。而且,就像評論區裏有人提到的,搞不懂為啥總有人喜歡拿這三者來比較,它們有個毛的關系?僅僅就是因為單詞相近就拿來比較?

但課程小結還是要做下,梳理梳理下相關面試知識點也好,那也不說廢話了,結合原文和評論區,以及一些擴展,盡量多總結一些知識點吧。

final

final 是 java 中的關鍵字,可用於修飾類,方法,變量。

當修飾類時,表明這個類不可被繼承。Java 中有一些核心類都被 final 修飾了,比如 String,System。當考慮到安全性原因時,可以將該類設計成 final。

當修飾方法時,表明該方法不可被重寫。一般是某些流程控制不希望被修改掉時,可以將這些方法聲明成 final,比如 View 中的 measure()requestFocus()findViewById()

當修飾變量時,表明該變量為常量,不允許被重新賦值,因此聲明成 final 的變量都需要顯示的進行賦值,否則編譯會報錯。

finally

finally 是確保 try-catch 方式最後執行的一種機制,通常的用法都是在 finally 裏進行一些資源的關閉,回收。比如 IO 流的關閉等等。

建議最好不要利用 finally 來控制流程,也不要在 finally 中有返回值,否則很容易影響正常流程,導致流程結構特別雜亂。

另外,有些特殊情況下,finally 中的代碼並不會被執行到,比如:

//1.try-catch異常退出
try {
    System.exit(1)
} catch {
    ....
} finally {
    //不會執行到這裏
    Log.d("finally", "finally");
}

//2.無限循環
try {
    while(true) {
        ...
    }
} finally {
    //不會執行到這裏
    Log.d("finally", "finally");
}

//3. 線程被殺死
//當執行 try-catch 時,線程被殺死了,那麽 finally 裏的代碼也無法被執行到

總之,finally 通常情況下都會最後被執行到,所以最好不要在這裏有 return 之類的語句來影響正常流程。但在某些特殊的場景下,finally 並不會被執行到,了解一下即可。

finalize

這個是 Object 中的一個方法,方法註釋說了很多,大概就是講這個方法是由垃圾收集器即將要回收該對象時會調用該方法,用戶可在這裏做一些最後的資源釋放工作。

以上是概念定義,但說實話,沒用過該方法,而且作者也說了,不推薦使用 finalize 機制來做資源回收,並且在 JDK 9,這個方法已經被標誌為 deprecated 廢棄的方法了。

作者有提到說,因為我們無法保證 finalize 什麽時候執行,執行是否符合預期,使用不當還會影響性能,導致程序死鎖、掛起等問題。

那麽,有其他方案來替代 finalize 處理回收資源的工作麽?有,Cleaner 機制,這個我沒接觸過,作者提了這個替代方案。另外,作者也說了,回收資源最好就是資源用完後就隨手清除,或者結合 try-catch-finally 機制回收。不管是 finalize 或者 Cleaner 機制,最好都只將它看成是最後一道防線,一旦將主要的回收工作依賴於這兩個機制的話,很容易出現各種問題。

擴展

為什麽局部內部類和匿名內部類只能訪問局部final變量?

先來看這麽段代碼:

//參數 msg 必須聲明為 final 類型
public void notifyChange(final String msg) {
    mTextView.post(new Runnable() {
        @Override
        public void run() {
            mTextView.setText(msg);
        }
    })
}

在這裏,post() 方法的參數是一個匿名內部類,在內部類中如果要使用外部 notifyChange() 方法的參數 msg,那麽必須將 msg 類型聲明成 final,否則編譯器會保錯。

這種場景非常常見的吧,不管是類似上述的 Ui 場景,還有網絡訪問時也經常需要通過回調通知上層,此時也就經常出現這種場景了。

那麽,有考慮過,為什麽內部類只能訪問局部 final 變量麽?

如果懂得反編譯 class 文件的,那麽應該就很清楚了。我也不懂,理解這點是通過閱讀其他大神分析的文章,以下是我的理解:

首先,變量都是有生命周期的,成員變量的生命周期就跟隨著對象的整個生命周期,而局部變量的生命周期則是非常有限。

比如方法內部的局部變量,它的生命周期就是在這個方法執行結束就終止。同樣,方法的參數也是局部變量,它是生命周期也同樣是到該方法執行結束。

另外,內部類的執行時機有時是會在外部方法執行結束之後。就拿上述例子來說,post() 中 Runnable 的執行時機,肯定是在外部 notifyChange() 方法執行完之後的。

那麽,問題來了。內部類 Runnable 的執行需要使用到外部方法 notifyChange() 的參數,但當它執行的時候,這個參數的生命周期早已結束,已經被回收掉了。既然已經被回收了,內部類又是怎麽使用外部的這個局部變量呢?

有大神反編譯了 class 文件後,給出了結論,原來內部類使用外部的局部變量時,是通過 copy 一份過來。也就是說,其實內部類此時使用的是自己內部定義的局部變量了,只是這個變量的值是復制外部那個局部變量的而已。

這也就解釋了,為什麽外部的局部變量明明已經被回收了,內部類仍舊可以使用,因為內部類此時使用的並不是外部類的局部變量引用了。

但到這裏,新的問題就來了:既然內部類使用的局部變量本質上跟外部的局部變量是相互獨立的兩個變量,那如果在內部類中修改了這個局部變量的值會出現什麽情況?是吧,數據的不一致性。

基於此,java 編譯器就直接限定死,內部類使用外部的局部變量時,必須將其限制為 final 類型,確保該變量不允許進行更改。

這樣一來,其實也就順便理解了,為什麽成員變量可以直接在內部類中使用,因為成員變量的聲明周期很長,不存在局部變量的問題。

以上內容,是在大神的文章裏被醍醐灌頂了,感謝大神,原文鏈接放出來:

Java內部類詳解


大家好,我是 dasu,歡迎關註我的公眾號(dasuAndroidTv),如果你覺得本篇內容有幫助到你,可以轉載但記得要關註,要標明原文哦,謝謝支持~
技術分享圖片

談談 final finally finalize 區別