Java中的final關鍵字
final關鍵字可以修飾類、方法、變數。本質作用可以理解為“ 宣告被修飾物件只讀 ”,不同用法對應不同的具體解釋。
final類
final類表示該類是完整(可理解為最終版本)的,禁止擴充套件。final標記會寫入class檔案的類定義中,在編譯期檢查,如果發現上游extends了一個final類後,則編譯失敗,從語法上保證不能extends一個final類。
final類經常用於不可變類的構造中:如果不使用final修飾,且有對子類可見的可變成員變數,則extends該“不可變類”後,子類就變成了可變類。考慮到多型的存在,這種“歧義”非常危險。詳見: ofollow,noindex" target="_blank">實現不可變類時如何禁止子類化? 。
例:String類、Interger類等:
public final class String
final方法
與final類的語義相似, final方法表示該方法是完整的,禁止重寫 。final標記會寫入class檔案的成員方法定義中,在編譯期檢查,如果發現繼承鏈上游有函式簽名相同的final方法(重寫),則編譯失敗,從語法上保證不能重寫一個final方法;同時,在編譯期完成對final方法的解析( 靜態繫結
)。
例:com.sun.org.apache.bcel.internal.classfile.Signature中的dump方法:
public final void dump(DataOutputStream file)throws IOException { super.dump(file); file.writeShort(signature_index); }
final變數
final成員變數
final成員變數體現了最直觀的final語義: final成員變量表示該成員變數只讀 (在第一次賦值後就不能再修改)。final標記會寫入class檔案的成員變數定義中,在編譯器和執行期都會進行檢查,如果發現第二次修改就編譯失敗或丟擲異常。
如果想實現不可變類,通常建議儘量使用final修飾每一個成員變數(延遲初始化等會違背這一建議)。
如果final成員變數的初始化被收集到該類的構造方法中,則final成員變數初始化之前的變數通常具有記憶體可見性,但這一性質並不容易應用,不建議利用。詳見:一文解決記憶體屏障。
例:Integer#value:
private final int value; public Integer(int value){ this.value = value; }
顯式的用構造方法初始化final成員變數。
常量
如果在成員變數宣告時完成初始化(第一次賦值),則該成員變數被JVM視作常量。
對於編譯期能確定初始值的常量(如 final int a = 1;
),通常能通過 常量摺疊
、 常量傳播
等技術,在編譯期完成常量的優化和解析。
例: Integer.SIZE
和 Integer.BYTES
:
@Native public static final int SIZE = 32; public static final int BYTES = SIZE / Byte.SIZE;
final區域性變數
final區域性變數也表示該區域性變數只讀,但與final成員變數不同,final區域性變數只是java中的語法糖, 區域性變數上的final修飾並不會寫入class檔案,更無法出現在執行期 。如果區域性變數被final修飾,則 編譯器在編譯時會檢查該變數是否有可能發生第二次修改 ,有可能就編譯失敗;否則,消除final修飾。
例: