Java入門系列之final

Jeffcky發表於2020-07-08

前言

在C#經典面試中摻雜過Java的final關鍵字,主要用於類不能被繼承,在C#則是利用關鍵字seal修飾類為密封類,而在Java中的final關鍵字的具體用法包含C#中const、readonly、seal三者使用之和,本節我們來聊聊Java的關鍵字final。

關鍵字final

通過final關鍵字修飾意為無法改變,在Java早期版本通過final可以改善效能或者提高效率,但現如今版本final已經無所謂效能,我們只關注於在設計時應用final的具體場景,final關鍵字包含兩層含義:其一,編譯時常量不變,其二,執行時初始化值而後不被改變。這兩者其實就是C#中的const和readonly關鍵字的使用。 final主要用於三種情況,修飾資料、方法、類,但本質上都是表示不可變,至於“不可變”的含義就看對應哪種場景。當然加上static修飾符只是表明在記憶體中只儲存一份資料,這裡我們就不做過多探討。

資料

編譯時常量和C#中概念一致,只不過C#用const修飾,同時對於變數的命名都是採用大寫方式,單詞與單詞之間用下劃線隔開,比如如下編譯時常量

public final int VALUE_ONE = 1;

上述已經說過,final不僅僅只是對於編譯時必須為常量,還可以用於執行時初始化時的值,比如通過隨機函式生成值

public  static Random random = new Random();

public final int VALUE_ONE = random.nextInt(20);

這種執行時初始化值方式就是C#中的readonly,如下:

public static Random random = new Random();

public readonly int VALUE_ONE = random.Next(20);

對於C#中的readonly初始值,我們知道除了在定義時賦值,也可以在建構函式中初始化值,若是再加上static,很顯然必須在靜態建構函式中初始化

class Program
{
    public readonly int VALUE_ONE;

    public Program()
    {
        VALUE_ONE = 1;
    }
}

final關鍵字同樣也能達到上述使用readonly的效果,只不過在Java中稱其為“空白final”,所謂空白final是指被宣告為final但未給定初始值,但是無論何種情況,編譯器必須確保在使用資料之前必須進行初始化

public class Main {

    public final int VALUE_ONE;

    public Main() {
        VALUE_ONE = 1;
    }
}

引數

無論是作為方法引數還是作為初始化變數的修飾,我們可以更改該引數裡面的值,但是無法更改引數引用所指向的物件。如下示例為修飾初始化類變數,作為方法引數同理,這裡不再闡述。

class Value {
    int i;

    public Value(int i) {
        this.i = i;
    }
}

public class FinalDemo {
    public final Value value = new Value(1);
}
public class Main {

    public static void main(String[] args) {

        FinalDemo finalDemo = new FinalDemo();

        //可以更改final引數裡的值
        finalDemo.value.i = 2;

        //不能更改引數所指向的物件(編譯報錯)
        finalDemo.value = new Value(2);
    }
}

方法

final修飾方法是將方法進行鎖定,以防止任何繼承類修改它本身的含義,此意為用以標記該方法不能被覆蓋(對於物件範圍內)或隱藏,這為後續開發人員建立其方法所在類的子類無法覆蓋其行為提供有力的保證。比如如下計數器則是一個很好的例子

public class Counter {
    private int counter = 0;

    public final int count() {
        return ++counter;
    }

    public final int reset() {
        return (counter = 0);
    }
}

但是我們去掉上述count方法的修飾符,進行如下操作,很顯然將破壞原始方法的行為,列印出結果為2

Counter c = new Counter() {
            public int count() {
                super.count();
                return super.count();
            }
        };
System.out.println(c.count());

若將整個類定義為final時,這說明此類將不會被繼承和C#中seal修飾符如出一轍,或出於安全考慮,或出於設計已確定該類不會再做任何變動,不希望該類有子類

public final class Counter {
}

//編譯發生錯誤
class SubCounter extends Counter {
}

總結

本節我們詳細講解了final關鍵字,該關鍵字是C#中const、readonly、seal三者使用的組合,Java程式設計思想雖然將其劃分為資料、方法、類,這裡我認為劃分為變數(包含資料和引數)、方法、類更合理。其實語法都大同小異,還是那句話,學習的本質在於歸納總結並舉一反三從而內化。 

相關文章