日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

JAVA面试题解惑系列(四)——final、finally和finalize的区别

發布時間:2024/4/14 编程问答 41 豆豆
生活随笔 收集整理的這篇文章主要介紹了 JAVA面试题解惑系列(四)——final、finally和finalize的区别 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
final、finally和finalize的區別是什么?

這是一道再經典不過的面試題了,我們在各個公司的面試題中幾乎都能看到它的身影。final、finally和finalize雖然長得像孿生三兄弟一樣,但是它們的含義和用法卻是大相徑庭。這一次我們就一起來回顧一下這方面的知識。

final關鍵字

我們首先來說說final。它可以用于以下四個地方:
  • 定義變量,包括靜態的和非靜態的。
  • 定義方法的參數。
  • 定義方法。
  • 定義類。

  • 我們依次來回顧一下每種情況下final的作用。首先來看第一種情況,如果final修飾的是一個基本類型,就表示這個變量被賦予的值是不可變的,即它是個常量;如果final修飾的是一個對象,就表示這個變量被賦予的引用是不可變的,這里需要提醒大家注意的是,不可改變的只是這個變量所保存的引用,并不是這個引用所指向的對象。在第二種情況下,final的含義與第一種情況相同。實際上對于前兩種情況,有一種更貼切的表述final的含義的描述,那就是,如果一個變量或方法參數被final修飾,就表示它只能被賦值一次,但是JAVA虛擬機為變量設定的默認值不記作一次賦值。

    被final修飾的變量必須被初始化。初始化的方式有以下幾種:
  • 在定義的時候初始化。
  • final變量可以在初始化塊中初始化,不可以在靜態初始化塊中初始化。
  • 靜態final變量可以在靜態初始化塊中初始化,不可以在初始化塊中初始化。
  • final變量還可以在類的構造器中初始化,但是靜態final變量不可以。

  • 通過下面的代碼可以驗證以上的觀點:
    Java代碼
  • public?class?FinalTest?{ ??
  • ????//?在定義時初始化 ??
  • ????public?final?int?A?=?10; ??
  • ??
  • ????public?final?int?B; ??
  • ????//?在初始化塊中初始化 ??
  • ????{ ??
  • ????????B?=?20; ??
  • ????} ??
  • ??
  • ????//?非靜態final變量不能在靜態初始化塊中初始化 ??
  • ????//?public?final?int?C; ??
  • ????//?static?{ ??
  • ????//?C?=?30; ??
  • ????//?} ??
  • ??
  • ????//?靜態常量,在定義時初始化 ??
  • ????public?static?final?int?STATIC_D?=?40; ??
  • ??
  • ????public?static?final?int?STATIC_E; ??
  • ????//?靜態常量,在靜態初始化塊中初始化 ??
  • ????static?{ ??
  • ????????STATIC_E?=?50; ??
  • ????} ??
  • ??
  • ????//?靜態變量不能在初始化塊中初始化 ??
  • ????//?public?static?final?int?STATIC_F; ??
  • ????//?{ ??
  • ????//?STATIC_F?=?60; ??
  • ????//?} ??
  • ??
  • ????public?final?int?G; ??
  • ??
  • ????//?靜態final變量不可以在構造器中初始化 ??
  • ????//?public?static?final?int?STATIC_H; ??
  • ??
  • ????//?在構造器中初始化 ??
  • ????public?FinalTest()?{ ??
  • ????????G?=?70; ??
  • ????????//?靜態final變量不可以在構造器中初始化 ??
  • ????????//?STATIC_H?=?80; ??
  • ??
  • ????????//?給final的變量第二次賦值時,編譯會報錯 ??
  • ????????//?A?=?99; ??
  • ????????//?STATIC_D?=?99; ??
  • ????} ??
  • ??
  • ????//?final變量未被初始化,編譯時就會報錯 ??
  • ????//?public?final?int?I; ??
  • ??
  • ????//?靜態final變量未被初始化,編譯時就會報錯 ??
  • ????//?public?static?final?int?STATIC_J; ??
  • }??
  • public class FinalTest {// 在定義時初始化public final int A = 10;public final int B;// 在初始化塊中初始化{B = 20;}// 非靜態final變量不能在靜態初始化塊中初始化// public final int C;// static {// C = 30;// }// 靜態常量,在定義時初始化public static final int STATIC_D = 40;public static final int STATIC_E;// 靜態常量,在靜態初始化塊中初始化static {STATIC_E = 50;}// 靜態變量不能在初始化塊中初始化// public static final int STATIC_F;// {// STATIC_F = 60;// }public final int G;// 靜態final變量不可以在構造器中初始化// public static final int STATIC_H;// 在構造器中初始化public FinalTest() {G = 70;// 靜態final變量不可以在構造器中初始化// STATIC_H = 80;// 給final的變量第二次賦值時,編譯會報錯// A = 99;// STATIC_D = 99;}// final變量未被初始化,編譯時就會報錯// public final int I;// 靜態final變量未被初始化,編譯時就會報錯// public static final int STATIC_J; }
    我們運行上面的代碼之后出了可以發現final變量(常量)和靜態final變量(靜態常量)未被初始化時,編譯會報錯。

    用final修飾的變量(常量)比非final的變量(普通變量)擁有更高的效率,因此我們在實際編程中應該盡可能多的用常量來代替普通變量,這也是一個很好的編程習慣。

    當final用來定義一個方法時,會有什么效果呢?正如大家所知,它表示這個方法不可以被子類重寫,但是它這不影響它被子類繼承。我們寫段代碼來驗證一下:
    Java代碼
  • class?ParentClass?{ ??
  • ????public?final?void?TestFinal()?{ ??
  • ????????System.out.println("父類--這是一個final方法"); ??
  • ????} ??
  • } ??
  • ??
  • public?class?SubClass?extends?ParentClass?{ ??
  • ????/** ?
  • ?????*?子類無法重寫(override)父類的final方法,否則編譯時會報錯 ?
  • ?????*/??
  • ????//?public?void?TestFinal()?{ ??
  • ????//?System.out.println("子類--重寫final方法"); ??
  • ????//?} ??
  • ???? ??
  • ????public?static?void?main(String[]?args)?{ ??
  • ????????SubClass?sc?=?new?SubClass(); ??
  • ????????sc.TestFinal(); ??
  • ????} ??
  • }??
  • class ParentClass {public final void TestFinal() {System.out.println("父類--這是一個final方法");} } public class SubClass extends ParentClass {/*** 子類無法重寫(override)父類的final方法,否則編譯時會報錯*/// public void TestFinal() {// System.out.println("子類--重寫final方法");// }public static void main(String[] args) {SubClass sc = new SubClass();sc.TestFinal();} }
    這里需要特殊說明的是,具有private訪問權限的方法也可以增加final修飾,但是由于子類無法繼承private方法,因此也無法重寫它。編譯器在處理private方法時,是按照final方法來對待的,這樣可以提高該方法被調用時的效率。不過子類仍然可以定義同父類中的private方法具有同樣結構的方法,但是這并不會產生重寫的效果,而且它們之間也不存在必然聯系。

    最后我們再來回顧一下final用于類的情況。這個大家應該也很熟悉了,因為我們最常用的String類就是final的。由于final類不允許被繼承,編譯器在處理時把它的所有方法都當作final的,因此final類比普通類擁有更高的效率。而由關鍵字abstract定義的抽象類含有必須由繼承自它的子類重載實現的抽象方法,因此無法同時用final和abstract來修飾同一個類。同樣的道理,final也不能用來修飾接口。final的類的所有方法都不能被重寫,但這并不表示final的類的屬性(變量)值也是不可改變的,要想做到final類的屬性值不可改變,必須給它增加final修飾,請看下面的例子:
    Java代碼
  • public?final?class?FinalTest?{ ??
  • ??
  • ????int?i?=?10; ??
  • ??
  • ????public?static?void?main(String[]?args)?{ ??
  • ????????FinalTest?ft?=?new?FinalTest(); ??
  • ????????ft.i?=?99; ??
  • ????????System.out.println(ft.i); ??
  • ????} ??
  • }??
  • public final class FinalTest {int i = 10;public static void main(String[] args) {FinalTest ft = new FinalTest();ft.i = 99;System.out.println(ft.i);} }
    運行上面的代碼試試看,結果是99,而不是初始化時的10。

    finally語句

    接下來我們一起回顧一下finally的用法。這個就比較簡單了,它只能用在try/catch語句中,并且附帶著一個語句塊,表示這段語句最終總是被執行。請看下面的代碼:
    Java代碼
  • public?final?class?FinallyTest?{ ??
  • ????public?static?void?main(String[]?args)?{ ??
  • ????????try?{ ??
  • ????????????throw?new?NullPointerException(); ??
  • ????????}?catch?(NullPointerException?e)?{ ??
  • ????????????System.out.println("程序拋出了異常"); ??
  • ????????}?finally?{ ??
  • ????????????System.out.println("執行了finally語句塊"); ??
  • ????????} ??
  • ????} ??
  • }??
  • public final class FinallyTest {public static void main(String[] args) {try {throw new NullPointerException();} catch (NullPointerException e) {System.out.println("程序拋出了異常");} finally {System.out.println("執行了finally語句塊");}} }
    運行結果說明了finally的作用:
  • 程序拋出了異常
  • 執行了finally語句塊

  • 請大家注意,捕獲程序拋出的異常之后,既不加處理,也不繼續向上拋出異常,并不是良好的編程習慣,它掩蓋了程序執行中發生的錯誤,這里只是方便演示,請不要學習。

    那么,有沒有一種情況使finally語句塊得不到執行呢?大家可能想到了return、continue、break這三個可以打亂代碼順序執行語句的規律。那我們就來試試看,這三個語句是否能影響finally語句塊的執行:
    Java代碼
  • public?final?class?FinallyTest?{ ??
  • ??
  • ????//?測試return語句 ??
  • ????public?ReturnClass?testReturn()?{ ??
  • ????????try?{ ??
  • ????????????return?new?ReturnClass(); ??
  • ????????}?catch?(Exception?e)?{ ??
  • ????????????e.printStackTrace(); ??
  • ????????}?finally?{ ??
  • ????????????System.out.println("執行了finally語句"); ??
  • ????????} ??
  • ????????return?null; ??
  • ????} ??
  • ??
  • ????//?測試continue語句 ??
  • ????public?void?testContinue()?{ ??
  • ????????for?(int?i?=?0;?i?<?3;?i++)?{ ??
  • ????????????try?{ ??
  • ????????????????System.out.println(i); ??
  • ????????????????if?(i?==?1)?{ ??
  • ????????????????????continue; ??
  • ????????????????} ??
  • ????????????}?catch?(Exception?e)?{ ??
  • ????????????????e.printStackTrace(); ??
  • ????????????}?finally?{ ??
  • ????????????????System.out.println("執行了finally語句"); ??
  • ????????????} ??
  • ????????} ??
  • ????} ??
  • ??
  • ????//?測試break語句 ??
  • ????public?void?testBreak()?{ ??
  • ????????for?(int?i?=?0;?i?<?3;?i++)?{ ??
  • ????????????try?{ ??
  • ????????????????System.out.println(i); ??
  • ????????????????if?(i?==?1)?{ ??
  • ????????????????????break; ??
  • ????????????????} ??
  • ????????????}?catch?(Exception?e)?{ ??
  • ????????????????e.printStackTrace(); ??
  • ????????????}?finally?{ ??
  • ????????????????System.out.println("執行了finally語句"); ??
  • ????????????} ??
  • ????????} ??
  • ????} ??
  • ??
  • ????public?static?void?main(String[]?args)?{ ??
  • ????????FinallyTest?ft?=?new?FinallyTest(); ??
  • ????????//?測試return語句 ??
  • ????????ft.testReturn(); ??
  • ????????System.out.println(); ??
  • ????????//?測試continue語句 ??
  • ????????ft.testContinue(); ??
  • ????????System.out.println(); ??
  • ????????//?測試break語句 ??
  • ????????ft.testBreak(); ??
  • ????} ??
  • } ??
  • ??
  • class?ReturnClass?{ ??
  • ????public?ReturnClass()?{ ??
  • ????????System.out.println("執行了return語句"); ??
  • ????} ??
  • }??
  • public final class FinallyTest {// 測試return語句public ReturnClass testReturn() {try {return new ReturnClass();} catch (Exception e) {e.printStackTrace();} finally {System.out.println("執行了finally語句");}return null;}// 測試continue語句public void testContinue() {for (int i = 0; i < 3; i++) {try {System.out.println(i);if (i == 1) {continue;}} catch (Exception e) {e.printStackTrace();} finally {System.out.println("執行了finally語句");}}}// 測試break語句public void testBreak() {for (int i = 0; i < 3; i++) {try {System.out.println(i);if (i == 1) {break;}} catch (Exception e) {e.printStackTrace();} finally {System.out.println("執行了finally語句");}}}public static void main(String[] args) {FinallyTest ft = new FinallyTest();// 測試return語句ft.testReturn();System.out.println();// 測試continue語句ft.testContinue();System.out.println();// 測試break語句ft.testBreak();} } class ReturnClass {public ReturnClass() {System.out.println("執行了return語句");} }
    上面這段代碼的運行結果如下:
  • 執行了return語句
  • 執行了finally語句
  • 0
  • 執行了finally語句
  • 1
  • 執行了finally語句
  • 2
  • 執行了finally語句
  • 0
  • 執行了finally語句
  • 1
  • 執行了finally語句

  • 很明顯,return、continue和break都沒能阻止finally語句塊的執行。從輸出的結果來看,return語句似乎在finally語句塊之前執行了,事實真的如此嗎?我們來想想看,return語句的作用是什么呢?是退出當前的方法,并將值或對象返回。如果finally語句塊是在return語句之后執行的,那么return語句被執行后就已經退出當前方法了,finally語句塊又如何能被執行呢?因此,正確的執行順序應該是這樣的:編譯器在編譯return new ReturnClass();時,將它分成了兩個步驟,new ReturnClass()和return,前一個創建對象的語句是在finally語句塊之前被執行的,而后一個return語句是在finally語句塊之后執行的,也就是說finally語句塊是在程序退出方法之前被執行的。同樣,finally語句塊是在循環被跳過(continue)和中斷(break)之前被執行的。

    finalize方法

    最后,我們再來看看finalize,它是一個方法,屬于java.lang.Object類,它的定義如下:
    Java代碼
  • protected?void?finalize()?throws?Throwable?{?}??
  • protected void finalize() throws Throwable { }
    眾所周知,finalize()方法是GC(garbage collector)運行機制的一部分,關于GC的知識我們將在后續的章節中來回顧。

    在此我們只說說finalize()方法的作用是什么呢?

    finalize()方法是在GC清理它所從屬的對象時被調用的,如果執行它的過程中拋出了無法捕獲的異常(uncaught exception),GC將終止對改對象的清理,并且該異常會被忽略;直到下一次GC開始清理這個對象時,它的finalize()會被再次調用。

    請看下面的示例:
    Java代碼
  • public?final?class?FinallyTest?{ ??
  • ????//?重寫finalize()方法 ??
  • ????protected?void?finalize()?throws?Throwable?{ ??
  • ????????System.out.println("執行了finalize()方法"); ??
  • ????} ??
  • ??
  • ????public?static?void?main(String[]?args)?{ ??
  • ????????FinallyTest?ft?=?new?FinallyTest(); ??
  • ????????ft?=?null; ??
  • ????????System.gc(); ??
  • ????} ??
  • }??
  • public final class FinallyTest {// 重寫finalize()方法protected void finalize() throws Throwable {System.out.println("執行了finalize()方法");}public static void main(String[] args) {FinallyTest ft = new FinallyTest();ft = null;System.gc();} }
    運行結果如下:
    • 執行了finalize()方法

    程序調用了java.lang.System類的gc()方法,引起GC的執行,GC在清理ft對象時調用了它的finalize()方法,因此才有了上面的輸出結果。調用System.gc()等同于調用下面這行代碼:
    Java代碼
  • Runtime.getRuntime().gc();??
  • Runtime.getRuntime().gc();
    調用它們的作用只是建議垃圾收集器(GC)啟動,清理無用的對象釋放內存空間,但是GC的啟動并不是一定的,這由JAVA虛擬機來決定。直到JAVA虛擬機停止運行,有些對象的finalize()可能都沒有被運行過,那么怎樣保證所有對象的這個方法在JAVA虛擬機停止運行之前一定被調用呢?答案是我們可以調用System類的另一個方法:
    Java代碼
  • public?static?void?runFinalizersOnExit(boolean?value)?{ ??
  • ????//other?code ??
  • }??
  • public static void runFinalizersOnExit(boolean value) {//other code }
    給這個方法傳入true就可以保證對象的finalize()方法在JAVA虛擬機停止運行前一定被運行了,不過遺憾的是這個方法是不安全的,它會導致有用的對象finalize()被誤調用,因此已經不被贊成使用了。

    由于finalize()屬于Object類,因此所有類都有這個方法,Object的任意子類都可以重寫(override)該方法,在其中釋放系統資源或者做其它的清理工作,如關閉輸入輸出流。

    通過以上知識的回顧,我想大家對于final、finally、finalize的用法區別已經很清楚了。

    轉載于:https://blog.51cto.com/zangweiren/94395

    總結

    以上是生活随笔為你收集整理的JAVA面试题解惑系列(四)——final、finally和finalize的区别的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。