Java關鍵字之final詳解
在我們編寫Java程式時總會根據需求將變數、方法、類設定成static(靜態)或final(最終),熟練掌握final用法是必須的,現在我們就來詳細瞭解final關鍵字!
一、final概述
概念:由字面可以瞭解,final有最終態,無法改變的意思。
使用目的:為了阻止改變與提高效率。
高效原因:Java內嵌機制,final方法會在編譯的過程中利用內嵌機制進行inline優化。
inline優化是指:在編譯的時候直接呼叫方法程式碼替換,也就是內嵌,而不是在執行時呼叫方法。
inline需要在編譯的時候就知道最後要用哪個方法。
顯然,非final是不行的。
非final方法可能在子類中被重寫,由於可能出現多型的情況,編譯器在編譯階段
並不能確定將來呼叫方法的物件的真正型別,也就無法確定到底呼叫哪個方法。)
修飾物件:
1、非抽象類,由於被final修飾的類是不能被繼承的,而抽象類必須被繼承才有意義。
2、非抽象方法,由於被final修飾的方法是不能被重寫的,而抽象方法必須被重寫才有意義。
3、變數或常量。
注意:
1、final不能修飾構造方法。
2、父類的private方法是不能被子類重寫的,因為private方法預設是final的。
二、具體用法
1、修飾類
final類不能被繼承,所有其成員與方法自然沒有機會被覆蓋,預設是final的。所以在將類設計成final類的時候,一定要特別慎重考慮,確定這個類不需要有子類,類實現不能被改變,類不能被擴充套件的時候才能用final修飾。
注:Java中String類就是一個final類。
2、修飾方法
如果一個類不允許其子類覆蓋某個方法,則可以把這個方法用final修飾。
這樣做的目的可在《Java程式設計思想》中查到:
“使用final方法的原因有兩個。第一個原因是把方法鎖定,以防任何繼承類修改它的含義;第二個原因是效率。在早期的Java實現版本中,會將final方法轉為內嵌呼叫。但是如果方法過於龐大,可能看不到內嵌呼叫帶來的任何效能提升。在最近的Java版本中,不需要使用final方法進行這些優化了。”
public class Test1 { public static void main(String[] args) { // TODO 自動生成方法存根 } public void f1() { System.out.println("f1"); } //無法被子類覆蓋的方法 public final void f2() { System.out.println("f2"); } public void f3() { System.out.println("f3"); } private void f4() { System.out.println("f4"); } } public class Test2 extends Test1 { public void f1(){ System.out.println("Test1父類方法f1被覆蓋!"); } public static void main(String[] args) { Test2 t=new Test2(); t.f1(); t.f2(); //呼叫從父類繼承過來的final方法 t.f3(); //呼叫從父類繼承過來的方法 //t.f4(); //呼叫失敗,無法從父類繼承獲得 } }
3、修飾變數(重點)
1、final修飾基本資料型別變數,則其數值一旦被初始化就不能被更改。
2、final修飾引用變數(如ObjectName obj = new ObjectName();中obj就是一個引用變數,指向堆記憶體中物件空間的首地址),則其初始化後就不能修改引用指向另一個物件。
public class Test2 {
private final String S = "final例項變數S";
private final int A = 100;
public final int B = 90;
public static final int C = 80;
private static final int D = 70;
public final int E; //final空白,必須在初始化物件的時候賦初值
public Test3(int x) {
E = x;
}
/**
* @param args
*/
public static void main(String[] args) {
Test3 t = new Test3(2);
//t.A=101; //出錯,final變數的值一旦給定就無法改變
//t.B=91; //出錯,final變數的值一旦給定就無法改變
//t.C=81; //出錯,final變數的值一旦給定就無法改變
//t.D=71; //出錯,final變數的值一旦給定就無法改變
System.out.println(t.A);
System.out.println(t.B);
System.out.println(t.C); //不推薦用物件方式訪問靜態欄位
System.out.println(t.D); //不推薦用物件方式訪問靜態欄位
System.out.println(Test3.C);
System.out.println(Test3.D);
//System.out.println(Test3.E); //出錯,因為E為final空白,依據不同物件值有所不同.
System.out.println(t.E);
Test3 t1 = new Test3(3);
System.out.println(t1.E); //final空白變數E依據物件的不同而不同
}
private void test() {
System.out.println(new Test3(1).A);
System.out.println(Test3.C);
System.out.println(Test3.D);
}
public void test2() {
final int a; //final空白,在需要的時候才賦值
final int b = 4; //區域性常量--final用於區域性變數的情形
final int c; //final空白,一直沒有給賦值.
a = 3;
//a=4; 出錯,已經給賦過值了.
//b=2; 出錯,已經給賦過值了.
}
}
四、修飾引數
當函式引數為final型別時,你可以讀取使用該引數,但是無法改變該引數的值。
public class Test3 {
public static void main(String[] args) {
new Test4().f1(2);
}
public void f1(final int i) {
//i++; //i是final型別的,值不允許改變的.
System.out.print(i);
}
}