Final 關鍵字的用法
Final 關鍵字的用法
本篇部落格示例來自來自 https://github.com/BruceEckel/OnJava8-examples
一、final 在 Java中的用法
1、final類
final類不能被繼承,因此final類的成員方法沒有機會被覆蓋,預設都是final的。在設計類時候,如果這個類不需要有子類,類的實現細節不允許改變,並且確信這個類不會再被擴充套件,那麼就設計為final類。 final方法不能被子類的方法覆蓋,但可以被繼承。
2、final方法
如果一個類不允許其子類覆蓋某個方法,則可以把這個方法宣告為final方法。
使用final方法的原因有二:
第一、把方法鎖定,防止任何繼承類修改它的意義和實現。
第二、高效。編譯器在遇到呼叫final方法時候會轉入內嵌機制,大大提高執行效率。
3、final變數(常量)
用final修飾的成員變量表示常量,只能被賦值一次,賦值後值無法改變!
final修飾的變數有三種:靜態變數、例項變數和區域性變數,分別表示三種類型的常量。
另外,final變數定義的時候,可以先宣告,而不給初值,這種變數也稱為final空白,無論什麼情況,編譯器都確保空白final在使用之前必須被初始化。但是,final空白在final關鍵字final的使用上提供了更大的靈活性,為此,一個類中的final資料成員就可以實現依物件而有所不同,卻有保持其恆定不變的特徵。
4、final引數
當函式引數為final型別時,你可以讀取使用該引數,但是無法改變該引數的值。
final不能用於修飾構造方法。
注:父類的private成員方法是不能被子類方法覆蓋的,因此private型別的方法預設是final型別的。
二、final 資料
下面例子展示了 final 屬性的使用:
class Value { int i; // package access Value(int i) { this.i = i; } } public class FinalData { private static Random rand = new Random(47); private String id; public FinalData(String id) { this.id = id; } // 帶有編譯時值的final基本型別,它們都可用作編譯時常量 private final int valueOne = 9; private static final int VALUE_TWO = 99; // public意味著可以在包外訪問,static強調只有一個,final說明是一個常量。 public static final int VALUE_THREE = 39; // 不是編譯時常量,因為i4 和 INT_5 都是在執行時才能被賦值 //不同物件的 i4 值可能是不同的 private final int i4 = rand.nextInt(20); //不同物件的 INT_5 值是同一個 static final int INT_5 = rand.nextInt(20); private Value v1 = new Value(11); //對於被final修飾的引用,引用指向的物件不能進行修改,但是物件的屬性或者值可以進行修改 private final Value v2 = new Value(22); private static final Value VAL_3 = new Value(33); // 陣列也是物件,同理如上 private final int[] a = {1, 2, 3, 4, 5, 6}; @Override public String toString() { return id + ": " + "i4 = " + i4 + ", INT_5 = " + INT_5; } public static void main(String[] args) { FinalData fd1 = new FinalData("fd1"); //- fd1.valueOne++; // 不能修改被final修飾的基本資料型別 fd1.v2.i++; // 可以修改被final修飾的引用指向的物件的屬性 fd1.v1 = new Value(9); // 可以修改引用指向的物件,因為沒有被final修飾 for (int i = 0; i < fd1.a.length; i++) { fd1.a[i]++; // 原理如 fd1.v2.i++ } //- fd1.v2 = new Value(0); // 不能修改v2引用指向的物件 //- fd1.VAL_3 = new Value(1); // 如上 //- fd1.a = new int[3]; // 如上 System.out.println(fd1); System.out.println("Creating new FinalData"); FinalData fd2 = new FinalData("fd2"); System.out.println(fd1); System.out.println(fd2); } }
執行結果:
fd1: i4 = 15, INT_5 = 18
Creating new FinalData
fd1: i4 = 15, INT_5 = 18
fd2: i4 = 13, INT_5 = 18
對於基本型別,final 使數值恆定不變,而對於物件引用,final 使引用恆定不變。一旦引用被初始化指向了某個物件,它就不能改為指向其他物件。但是,物件本身是可以修改的,Java 沒有提供將任意物件設為常量的方法。(你可以自己編寫類達到使物件恆定不變的效果)這一限制同樣適用陣列,陣列也是物件。
三、空final
空白 final 指的是沒有初始化值的 final 屬性。編譯器確保空白 final 在使用前必須被初始化。這樣既能使一個類的每個物件的 final 屬性值不同,也能保持它的不變性。
class Poppet {
private int i;
Poppet(int ii) {
i = ii;
}
}
public class BlankFinal {
private final int i = 0; // 正常定義的final常量
private final int j; // 空final
private final Poppet p; // Blank final reference
// 空final必須在構造方法中進行初始化,如果未進行初始化,則編譯器會報錯
public BlankFinal() {
j = 1;
p = new Poppet(1);
}
public BlankFinal(int x) {
j = x;
p = new Poppet(x);
}
public static void main(String[] args) {
new BlankFinal();
new BlankFinal(47);
}
}
說白了,如果用final修飾資料,咱們就必須在你使用之前進行初始化,可以直接在建構函式中或者在屬性定義時進行初始化。
四、final方法和private
使用 final 方法的原因有兩個。第一個原因是給方法上鎖,防止子類通過覆寫改變方法的行為。這是出於繼承的考慮,確保方法的行為不會因繼承而改變。
class WithFinals {
// 使用private和final共同修飾,和只使用private修飾效果一樣
private final void f() {
System.out.println("WithFinals.f()");
}
// 自動添加了 final
private void g() {
System.out.println("WithFinals.g()");
}
}
class OverridingPrivate extends WithFinals {
private final void f() {
System.out.println("OverridingPrivate.f()");
}
private void g() {
System.out.println("OverridingPrivate.g()");
}
}
class OverridingPrivate2 extends OverridingPrivate {
public final void f() {
System.out.println("OverridingPrivate2.f()");
}
public void g() {
System.out.println("OverridingPrivate2.g()");
}
}
public class FinalOverridingIllusion {
public static void main(String[] args) {
OverridingPrivate2 op2 = new OverridingPrivate2();
op2.f();
op2.g();
// 可以向上轉型,但是不能呼叫op.f()或者op.g()
OverridingPrivate op = op2;
//- op.f();
//- op.g();
// Same here:
WithFinals wf = op2;
//- wf.f();
//- wf.g();
}
}
如果放開 op2.f() 的註釋,編譯器會報紅,如果進行編譯,會提示 java: f() 在 reuse.OverridingPrivate 中是 private 訪問控制
,因為如果是使用 private 或者 final 修飾的方法,它只是隱藏在類內部的程式碼。在 WithFinals 、 OverridingPrivate、 OverridingPrivate2中, **f() **和 g() 都是屬於各自的方法,他們之間不存在任何聯絡,細心的小夥伴可能會發現在 OverridingPrivate 和 **OverridingPrivate2 **沒有 @override 註解,如果強行加上 @override 註解,編譯器會報錯,由此看來,它們之間並沒有重寫父類的 private 方法,只是恰好有相同的命名而已。這也不難解釋為什麼呼叫 op.f() 編譯器會報錯。
五、final類
當說一個類是 final (final 關鍵字在類定義之前),就意味著它不能被繼承。之所以這麼做,是因為類的設計就是永遠不需要改動,或者是出於安全考慮不希望它有子類。
class SmallBrain {}
final class Dinosaur {
int i = 7;
int j = 1;
SmallBrain x = new SmallBrain();
void f() {}
}
//- class Further extends Dinosaur {}
public class Jurassic {
public static void main(String[] args) {
Dinosaur n = new Dinosaur();
n.f();
n.i = 40;
n.j++;
}
}
如果放開註釋,顯而易見的會出現error: Cannot extend final class 'Dinosaur'
錯誤,因為 Dinosaur 是不能被繼承的。當然,被 final 修飾的類,其中包含的方法都被隱式的定義為 final 。