Java中的final關鍵字


final關鍵字表示的不可變的。下面討論final關鍵字使用的三種場合:數據、方法以及類。 

final數據

1、final屬性

    程序中經常需要用到一些“常數”。常數主要應用於兩個方面:

  • 編譯期常數,永遠不會改變
  • 在運行期初始化一個值,不希望它發生改變。

    對於編譯期的常數,計算可以在編譯期間提前執行,可以將常數值直接用於程序中。Java中,這種常數必須是基本數據類型。前置關鍵字final聲明。定義時必須提供一個值

class Person {
    final String name;  // name未初始化,編譯出錯
}

    如果對對象句柄使用final,final會將句柄變成一個常數。進行聲明時,必須將句柄初始化到一個具體的對象,而且不能將句柄指向另一個對象。

class Person {
    String name = "張三";
}

public class FinalDemo {

    public static void main(String[] args) {
        final Person p = new Person();
        p = new Person();   // Error:無法為最終變量p分配值
    }
}

然而,對象本身是可以修改的。

class Person {
    String name = "張三";
}

public class FinalDemo {
    public static void main(String[] args) {
        final Person p = new Person();
        p.name = "蕭蕭弈寒";
    }
}

一個可能的結果:

name = fd1,i1 = 0, i2 = 6

name = fd2,i1 = 8, i2 = 6

i1,i2是在運行期間隨機產生的數據。

2、空白final

    Java1.1允許創建“空白final”,它們屬於特殊字段。盡管被聲明為final,但是卻未得到一個初始值。即便如此,空白final還是必須在使用之前得到初始化。 示例:

class Person {}

public class FinalDemo {
    final int i;
    final Person p;

    FinalDemo() {
        i = 1;
        p = new Person();
    }

    FinalDemo(int x) {
        i = x;
        p = new Person();
    }

    public static void main(String[] args) {
        FinalDemo fd = new FinalDemo();
    }
}

現在強行要求對final進行賦值處理,要么在定義字段時使用一個表達式,要么在每個構建器中。

3、用final修飾參數

    查了一些資料,很多人都說用final修飾方法參數是防止參數在調用時被修改。個人認為這種說法其實有兩種理解:一種是變量的實際值不會被修改,另一種是在方法內部不能被修改。無論是基本參數類型還是引用類型,前一種說法都是錯誤的。因為Java是值傳遞

public class FinalDemo {

    static void f(final int i) {
        i++;    // 無法為final變量賦值,編譯錯誤
    }

    public static void main(String[] args) {
        int x = 10;
        f(x);   //
    }
}

 ①處調用的f方法只是將x的值賦給了i,實際上i和x是兩個變量。

再看下面的例子:

class Person {
    String name = "張三";
}

public class FinalDemo {

    public static void main(String[] args) {
        final Person p = new Person();
        changeName(p);
        System.out.println(p.name);
    }

    static void changeName(final Person p) {
        p.name = "蕭蕭弈寒";
    }
}

【運行結果】:

蕭蕭弈寒

由此說明,final並不能阻止changeName()方法改變p的內容。接下來,我們刪除修飾參數的final,然后在changeName方法體內改變p指向的實例:

class Person {
    String name = "張三";
}

public class FinalDemo {

    public static void main(String[] args) {
        final Person p = new Person();
        p.name = "蕭蕭弈寒";
        changeName(p);
        System.out.println(p.name);
    }

    static void changeName(Person p) {
        p = new Person();
    }
}

【運行結果】:
changeName中的name:張三
蕭蕭弈寒

我們可以看出,雖然方法體內的p指向了其他對象,但是對於main方法中的p並沒有影響。原因還是Java是值傳遞的。具體的請參考Java值傳遞還是引用傳遞?

 

final方法

    final方法主要有兩個方面的作用:一種是防止任何繼承類覆蓋方法。若希望一個方法的行為在繼承期間保持不變,不可被覆蓋和改寫,就可以采取這種做法。另一種是提高程序執行的效率。將一個方法設成final后,編譯器就會忽略為執行方法調用機制而采取的常規代碼插入方法(將自變量壓入堆棧;跳至方法代碼並執行它;跳回來;清除堆棧自變量;最后對返回值進行處理)。它會用方法主體內實際代碼的一個副本來替換方法調用。這樣可以避免方法調用時的系統開銷。若方法體太大,可能效率也得不到提升。

class Human {
    public final void show() {
        //...
    }
}
public class Man extends Human{
    public void show() {}   //Cannot override the final method from Human
}

    類內所有的private方法都自動成為final。由於不能訪問一個private方法,所以它絕對不會被覆蓋。

 

final類

    如果整個類都是final,就表明這個類不允許被繼承。或者出於安全方面的理由,不希望進行子類化。除此之外,或許還考慮執行效率的問題,確保涉及這個類各對象的所有行動都要盡可能地有效。

final class Human {
}

public class Man extends Human{ // The type Man cannot subclass the final class Human
}

注意:數據成員既可以是final,也可以不是。無論類是否被定義成final,應用於final的規則同樣適用於數據成員。

將類定義成final后,結果只是禁止被繼承。由於禁止了繼承,所以一個final類中的所有方法都默認為final。 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM