詳解Java關鍵字final

一、常見問題

  • 所有的final修飾的字段都是編譯期常量嗎?
  • 如何理解private所修飾的方法是隱式的final?
  • 說說final類型的類如何拓展?比如String是final類型,我們想寫個MyString復用所有String中方法,同時增加一個新的toMyString()的方法,應該如何做?
  • final方法可以被重載嗎?可以
  • 父類的final方法能不能夠被子類重寫?不可以
  • 說說final域重排序規則?
  • 說說final的原理?
  • 使用 final 的限制條件和局限性?

二、final修飾類

當某個類的整體定義為final時,就表明瞭你不能打算繼承該類,而且也不允許別人這麼做。即這個類是不能有子類的。
註意:final類中的所有方法都隱式為final,因為無法覆蓋他們,所以在final類中給任何方法添加final關鍵字是沒有任何意義的。

三、final修飾方法

類中所有private方法都隱式地指定為final的,由於無法取用private方法,所以也就不能覆蓋它。可以對private方法增添final關鍵字,但這樣做並沒有什麼好處。

public class Base {
    private void test() {
    }
}

public class Son extends Base{
    public void test() {
    }
    public static void main(String[] args) {
        Son son = new Son();
        Base father = son;
        //father.test();
    }
}
  

Base和Son都有方法test(),但是這並不是一種覆蓋,因為private所修飾的方法是隱式的final,也就是無法被繼承,所以更不用說是覆蓋瞭,在Son中的test()方法不過是屬於Son的新成員罷瞭,Son進行向上轉型得到father,但是father.test()是不可執行的,因為Base中的test方法是private的,無法被訪問到。

四、final方法是可以被重載的

我們知道父類的final方法是不能夠被子類重寫的,那麼final方法可以被重載嗎? 答案是可以的,下面代碼是正確的。

public class FinalExampleParent {
    public final void test() {
    }

    public final void test(String str) {
    }
}

修飾參數
Java允許在參數列表中以聲明的方式將參數指明為final,這意味這你無法在方法中更改參數引用所指向的對象。這個特性主要用來向匿名內部類傳遞數據。

五、修飾變量

public class Test {
    //編譯期常量
    final int i = 1;
    final static int J = 1;
    final int[] a = {1,2,3,4};
    //非編譯期常量
    Random r = new Random();
    final int k = r.nextInt();

    public static void main(String[] args) {

    }
}

k的值由隨機數對象決定,所以不是所有的final修飾的字段都是編譯期常量,隻是k的值在被初始化後無法被更改。

六、static final

一個既是static又是final 的字段隻占據一段不能改變的存儲空間,它必須在定義的時候進行賦值,否則編譯器將不予通過。

public class finaltest
{
    //Random對象r
    static Random r = new Random();
    //生成隨機數k
    final int k = r.nextInt(10);
    //生成隨機數k2
    static final int k2 = r.nextInt(10);
    
    public static void main(String[] args) {
        
        finaltest t1 = new finaltest();
        
        System.out.println("k="+t1.k+" k2="+t1.k2);
        
        finaltest t2 = new finaltest();
        
        System.out.println("k="+t2.k+" k2="+t2.k2);
    }
}
  

k=2 k2=7
k=8 k2=7
我們可以發現對於不同的對象k的值是不同的,但是k2的值卻是相同的,這是為什麼呢? 因為static關鍵字所修飾的字段並不屬於一個對象,而是屬於這個類的。也可簡單的理解為static final所修飾的字段僅占據內存的一個一份空間,一旦被初始化之後便不會被更改。

七、black final

Java允許生成空白final,也就是說被聲明為final但又沒有給出定值的字段,但是必須在該字段被使用之前被賦值,這給予我們兩種選擇:

  • 在定義處進行賦值(這不叫空白final)
  • 在構造器中進行賦值,保證瞭該值在被使用前賦值。

這增強瞭final的靈活性。

final int i1 = 1;

    final int i2;//空白final

    public finaltest() {
        i2 = 1;
    }
    public finaltest(int x) {
        this.i2 = x;
    }

可以看到i2的賦值更為靈活。但是請註意,如果字段由static和final修飾,僅能在定義處賦值,因為該字段不屬於對象,屬於這個類。

八、final域重排序規則

上面我們聊的final使用,應該屬於Java基礎層面的,當理解這些後我們就真的算是掌握瞭final嗎? 有考慮過final在多線程並發的情況嗎? 在java內存模型中我們知道java內存模型為瞭能讓處理器和編譯器底層發揮他們的最大優勢,對底層的約束就很少,也就是說針對底層來說java內存模型就是一弱內存數據模型。同時,處理器和編譯為瞭性能優化會對指令序列有編譯器和處理器重排序。那麼,在多線程情況下,final會進行怎樣的重排序? 會導致線程安全的問題嗎? 下面,就來看看final的重排序。

到此這篇關於詳解Java關鍵字final的文章就介紹到這瞭,更多相關Java關鍵字final內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: