1. 程式人生 > >java中的final關鍵字的使用

java中的final關鍵字的使用

final關鍵字

在java中,final的含義在不同的場景下有細微的差別,但總體來說,它指的是"這是不可變的".下面,我們來講final的四種主要用法.

1.修飾資料

在編寫程式時,我們經常需要說明一個數據是不可變得,我們稱之為常量,在java中,用final關鍵字修飾的變數,只能進行一次賦值操作,並且在生命週期內不可以改變它的值.更重要的是,final會告訴編輯器,這個資料是不會修改的,那麼編譯器就可能會在編譯時期就對該資料進行替換甚至執行計算,這樣可以對我們的程式起到一點優化.不過在針對基本型別和引用型別時,final關鍵字的效果存在細微差別.

我們來看下面的例子:

class Value {
    int v;
    public Value(int v) {
        this.v = v;
    }
}

public class FinalTest {
    
    final int f1 = 1;
    final int f2;
    public FinalTest() {
        f2 = 2;
    }

    public static void main(String[] args) {
        final int value1 = 1;
        // value1 = 4;
        final double value2;
        value2 = 2.0;
        final Value value3 = new Value(1);
        value3.v = 4;
    }
}

在上面的例子中,我們先看一下main方法中的幾個final修飾的資料,在給value1賦初始值之後,我們無法對value1進行修改,final關鍵字起到了常量的作用.從value2我們可以看到,final修飾的變數可以不再宣告時賦值,即可以先宣告,後賦值.value3是一個引用變數,這裡我們可以看到final修飾引用變數時,只是限定了引用變數的引用不可以改變,既不是將value3再次引用另一個value物件,但是引用的物件的值是可以改變的,從記憶體模型中我們看的更加清晰:

從上面,final修飾的值是用粗線條的邊框表示它的值是不可改變的,我們知道引用變數的值實際是它的引用的地址,也就是說該地址的值是不可改變的,從而說明了為什麼不可以改變引用變數.而實際引用的物件實際上是不受final關鍵字的影響的,所以他的值是可以改變的.

另一方面,我們看到了用final修飾成員變數時的細微差別,因為final修飾的資料的值是不可改變的,所以我們必須確保在使用前就已經對成員變數賦值了.因此對於final修飾的成員變數,餓哦們有且只要兩個地方可以給它賦值,一個宣告該成員變數時賦值,另一個是在構造方法中賦值,在這兩個地方必須給它們賦初始值.

最後我們需要注意的一點是,同時使用static和final修飾的成員在記憶體中只佔據一段不能改變的儲存空間.

2.修飾方法引數

前面我們可以看到,如果變數是我們自己建立的,那麼使用final修飾表示我們只會給它賦值一個且不會改變變數的值.那麼如果變數是做引數傳入的,我們怎麼保證他的值不會改變呢?這就用到了final的第二種用法,即在我們編寫方法時,可以在引數前面新增fianl關鍵字,它表示在整個方法中,餓哦們不會(實際上是不能)改變引數的值:

public class FinalTest {

    /* ... */

    public void finalFunc(final int i, final Value value) {
        // i = 5; 不能改變i的值
        // v = new Value(); 不能改變v的值
        value.v = 5; // 可以改變引用物件的值
    }
}

3.修飾方法

第三種方式,即使用final關鍵字修飾方法,它表示該方法不能被覆蓋,這種使用方法主要從設計的角度考慮,即明確告訴其他繼承該類的程式設計師,不希望它們去覆蓋這個方法.這種方式我們很容易理解,然而,關於private和final關鍵字還有一些聯絡,這就是類中所有的private方法都隱示地指定為final的,由於無法在類外使用private方法,所以也就無法覆蓋它

4.修飾類

瞭解了final關鍵字的其他用法,我們很容易可以想到使用final關鍵字修飾類的作用,那就是用final修飾的類是無法被繼承的。

上面我們講解了final的四種用法,然而,對於第三種和第四種用法,我們卻甚少使用。這不是沒有道理的,從final的設計來講,這兩種用法甚至可以說是雞肋,因為對於開發人員來講,如果我們寫的類被繼承的越多,就說明我們寫的類越有價值,越成功。即使是從設計的角度來講,也沒有必要將一個類設計為不可繼承的。Java標準庫就是一個很好的反例,特別是Java 1.0/1.1中Vector類被如此廣泛的運用,如果所有的方法均未被指定為final的話,它可能會更加有用。如此有用的類,我們很容易想到去繼承和重寫他們,然而,由於final的作用,導致我們對Vector類的擴充套件受到了一些阻礙,導致了Vector並沒有完全發揮它應有的全部價值。