1. 程式人生 > >Java關鍵字之final詳解

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); 
        } 
}