final,finally,finalize的區別與finally鮮爲人知的細節拆解

 

Final關鍵字

在java類中,final可以用來修飾類,方法和變量。

 

  • 修飾類:

當final修飾類時,表明該類不能被其他類所繼承。

當我們需要讓一個類永遠不被繼承,此時就可以用final修飾,但要注意:

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

 

  • 修飾方法:

final修飾方法的目的有兩個:

1.鎖定方法,防止繼承類對其進行更改,即方法不能被重寫。

注意:若父類中final方法的訪問權限爲private,將導致子類中不能直接繼承該方法,因此,此時可以在子類中定義相同方法名的函數,此時不會與重寫final方法,而是在子類中重新地定義了新方法。

2.效率,在早期的java版本中,會將final方法轉爲內嵌調用。但若方法過於龐大,可能在性能上不會有多大提升。因此在最近版本中,不需要final方法進行這些優化了。此處不再贅述。

 

  • 修飾變量

        final成員變量表示常量,只能被賦值一次,賦值後其值不再改變。

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

 

注意:final修飾一個成員變量(屬性),必須要初始化。

不進行初始化將不能通過編譯。

 

兩種初始化方式:

1.在變量聲明的時候初始化;

 

2.聲明變量的時候不賦初值,但是要在這個變量所在的類的所有的構造函數中對這個變量賦初值。

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

 

【在java中,String被設計成final類,那爲什麼平時使用時,String的值可以被改變?】

  字符串常量池是java堆內存中一個特殊的存儲區域,當我們建立一個String對象時,假設常量池不存在該字符串,則創建一個,若存在則直接引用已經存在的字符串。當我們對String對象值改變的時候,例如 String str="A"; str="B" 。str是String對象的一個引用(我們這裏所說的String對象其實是指字符串常量),當str=“B”執行時,並不是原本String對象("A")發生改變,而是創建一個新的對象("B"),令str引用它。

 

 

Finally

   finally作爲異常處理的一部分,它只能用在try/catch語句中,並且附帶一個語句塊,表示這段語句最終一定會被執行(不管有沒有拋出異常),經常被用在需要釋放資源的情況下。在try未執行或者虛擬機關閉時存在特例

 

*但此處存在特例:

1.finally執行的前提是,只有與finally對應的try語句塊得到執行的情況下,finally語句塊纔會執行。如果在執行try語句塊之前已經返回或拋出異常,所以try對應的finally語句就不會被執行。

例如:

在try之前拋出異常 了,finally不會被執行。

 

2.使用System.exit (0) 語句,終止了 Java 虛擬機的運行,則finally不能被執行。

   System.exit(0)用來結束當前正在運行中的java虛擬機。如果status是非零參數,那麼表示是非正常退出。 
   System.exit(0)是正常退出,即將你的整個虛擬機裏的內容都停掉。無論如何,JVM虛擬機關閉,意味內存釋放。

   即finally將無法被執行。(斷電同理)

 

finally對於return的表達方式:

例如這段代碼,返回的是什麼呢?

返回結果是2,即finally‘替換’掉了trycase中的返回值。

  • 以實際編譯源碼來說,一旦finally裏有return,將把所有return全部替換爲finally的return值。

舉例來說:

 

  • 但另一種情況則不同:如果finally不直接return,而是修改其變量,return將直接返回return時設置的值。

如果try語句裏有return,根據jvm的官方說明,代碼的行爲如下:
1.如果有返回值,就把返回值保存到局部變量中
2.執行jsr指令跳到finally語句裏執行
3.執行完finally語句後,返回之前保存在局部變量表裏的值

例1:

例2:

 

到底返回值變不變可以簡單的這麼記憶:當finally調用的任何可變API,會修改返回值;當finally調用任何的不可變API,對返回值沒有影響。

總結一下:其實return與finally並沒有明顯的誰強誰弱。在執行時,是return語句先把返回值寫入但內存中,然後停下來等待finally語句塊執行完,return再執行後面的一段。

 

Finalize

當垃圾回收器確定不存在對該對象的更多引用時,由對象的垃圾回收器調用此方法。

protected void finalize()
                 throws Throwable

當垃圾回收器確定不存在對該對象的更多引用時,由對象的垃圾回收器調用此方法。子類重寫 finalize 方法,以配置系統資源或執行其他清除。

finalize 的常規協定是:當 JavaTM 虛擬機已確定尚未終止的任何線程無法再通過任何方法訪問此對象時,將調用此方法,除非由於準備終止的其他某個對象或類的終結操作執行了某個操作。finalize 方法可以採取任何操作,其中包括再次使此對象對其他線程可用;不過,finalize 的主要目的是在不可撤消地丟棄對象之前執行清除操作。例如,表示輸入/輸出連接的對象的 finalize 方法可執行顯式 I/O 事務,以便在永久丟棄對象之前中斷連接。

Object 類的 finalize 方法執行非特殊性操作;它僅執行一些常規返回。Object 的子類可以重寫此定義。

Java 編程語言不保證哪個線程將調用某個給定對象的 finalize 方法。但可以保證在調用 finalize 時,調用 finalize 的線程將不會持有任何用戶可見的同步鎖定。如果 finalize 方法拋出未捕獲的異常,那麼該異常將被忽略,並且該對象的終結操作將終止。

在啓用某個對象的 finalize 方法後,將不會執行進一步操作,直到 Java 虛擬機再次確定尚未終止的任何線程無法再通過任何方法訪問此對象,其中包括由準備終止的其他對象或類執行的可能操作,在執行該操作時,對象可能被丟棄。

對於任何給定對象,Java 虛擬機最多隻調用一次 finalize 方法。

finalize 方法拋出的任何異常都會導致此對象的終結操作停止,但可以通過其他方法忽略它。

 

  • 終結方法(finalize)通常是不可預測的,也是很危險的,一般情況下是不必要的。會導致行爲不穩定,降低性能以及可移植性問題,根據經驗,應該避免使用終結方法。(EffectiveJava 創建銷燬對象P24)

終結方法finalize()的缺點在於不能保證會被及時的執行,從一個對象變得不可到達開始到它的終結方法被執行,所花費的時間是任意長的。

及時地執行終結方法是垃圾回收算法的一個重要功能,這種算法在不同JVM的實現會大相徑庭。一個程序在你的測試用JVM上運行的非常好,在最重要的客戶JVM上根本無法運行,是完全有可能的。

JAVA語言規範不僅不保證終結方法會及時的執行,甚至根本不保證它們會被執行。總之,除非作爲安全網,或者是爲了終止非關鍵的本地資源,否則不要使用終結方法。

 

有關finalize()的執行,可以移步:javaGC與內存分配策略 ->對象的生存與死亡 查看爲何finalize是不可預期的,儘量避免使用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章