<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    這是一道再經典不過的面試題了,我們在各個公司的面試題中幾乎都能看到它的身影。
    final、finally和finalize雖然長得像孿生三兄弟一樣,但是它們的含義和用法卻是大相徑庭。
    這一次我們就一起來回顧一下這方面的知識。
    final關鍵字
    我們首先來說說final。它可以用于以下四個地方:
    1. 定義變量,包括靜態的和非靜態的。
    2. 定義方法的參數。
    3. 定義方法。
    4. 定義類。
    我們依次來回顧一下每種情況下final的作用。首先來看第一種情況,如果final修飾的是一
    個基本類型,就表示這個變量被賦予的值是不可變的,即它是個常量;如果final修飾的是
    一個對象,就表示這個變量被賦予的引用是不可變的,這里需要提醒大家注意的是,不可改
    變的只是這個變量所保存的引用,并不是這個引用所指向的對象。在第二種情況下,final的
    含義與第一種情況相同。實際上對于前兩種情況,有一種更貼切的表述final的含義的描述,
    那就是,如果一個變量或方法參數被final修飾,就表示它只能被賦值一次,但是JAVA虛擬
    機為變量設定的默認值不記作一次賦值。
    被final修飾的變量必須被初始化。初始化的方式有以下幾種:
    1. 在定義的時候初始化。
    2. final變量可以在初始化塊中初始化,不可以在靜態初始化塊中初始化。
    3. 靜態final變量可以在靜態初始化塊中初始化,不可以在初始化塊中初始化。
    4. 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;
    }
    我們運行上面的代碼之后出了可以發現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();
    }
    }
    這里需要特殊說明的是,具有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);
    }
    }
    運行上面的代碼試試看,結果是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語句塊");
    }
    }
    }
    運行結果說明了finally的作用:
    1. 程序拋出了異常
    2. 執行了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語句");
    }
    }
    上面這段代碼的運行結果如下:
    1. 執行了return語句
    2. 執行了finally語句
    3.
    4. 0
    5. 執行了finally語句
    6. 1
    7. 執行了finally語句
    8. 2
    9. 執行了finally語句
    10.
    11. 0
    12. 執行了finally語句
    13. 1
    14. 執行了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 { }
    眾所周知,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();
    }
    }
    運行結果如下:
    • 執行了finalize()方法
    程序調用了java.lang.System類的gc()方法,引起GC的執行,GC在清理ft對象時調用了它
    的finalize()方法,因此才有了上面的輸出結果。調用System.gc()等同于調用下面這行代碼:
    Java代碼
    Runtime.getRuntime().gc();
    調用它們的作用只是建議垃圾收集器(GC)啟動,清理無用的對象釋放內存空間,但是GC
    的啟動并不是一定的,這由JAVA虛擬機來決定。直到 JAVA虛擬機停止運行,有些對象的
    finalize()可能都沒有被運行過,那么怎樣保證所有對象的這個方法在JAVA虛擬機停止運行
    之前一定被調用呢?答案是我們可以調用System類的另一個方法:
    Java代碼
    public static void runFinalizersOnExit(boolean value) {
    //other code
    }
    給這個方法傳入true就可以保證對象的finalize()方法在JAVA虛擬機停止運行前一定被運行
    了,不過遺憾的是這個方法是不安全的,它會導致有用的對象finalize()被誤調用,因此已經
    不被贊成使用了。
    由于finalize()屬于Object類,因此所有類都有這個方法,Object的任意子類都可以重寫
    (override)該方法,在其中釋放系統資源或者做其它的清理工作,如關閉輸入輸出流。
    通過以上知識的回顧,我想大家對于final、finally、finalize的用法區別已經很清楚了。

    posts - 104, comments - 33, trackbacks - 0, articles - 0

    Copyright © 林光炎

    主站蜘蛛池模板: 久久亚洲AV成人无码电影| 国产AV无码专区亚洲Av| 免费看黄的成人APP| 免费人成在线观看网站品爱网| 91在线视频免费看| 亚洲成aⅴ人片在线影院八| 亚洲黄色免费在线观看| 亚洲日本一区二区一本一道| 一级视频免费观看| 亚洲情综合五月天| 日韩插啊免费视频在线观看 | 日韩插啊免费视频在线观看 | 亚洲欧美国产国产一区二区三区| 91免费精品国自产拍在线不卡| 亚洲色最新高清av网站| 国产人成免费视频| 亚洲国产成人精品无码一区二区| 免费精品国偷自产在线在线| 亚洲国产成人久久精品软件| 亚洲Av无码乱码在线观看性色 | 亚洲av无码一区二区三区四区 | 久久久久亚洲AV成人无码| 91av视频免费在线观看| 亚洲午夜国产精品无码老牛影视| 99在线热播精品免费99热| 激情内射亚洲一区二区三区| 毛片免费在线观看网站| 免费一级毛片在线播放放视频 | 亚洲va在线va天堂va手机| 久久青草免费91线频观看不卡| 亚洲日韩中文在线精品第一| 国内精品久久久久影院免费| 亚洲日韩欧洲乱码AV夜夜摸| 最近免费mv在线电影| 亚洲JIZZJIZZ妇女| 亚洲国产精品成人久久| 日本免费一本天堂在线| 日本黄色动图免费在线观看| 亚洲中文字幕无码爆乳app| 国产亚洲精品资源在线26u| 丝瓜app免费下载网址进入ios |