Java中final關鍵字的使用
《Java程式設計思想》
final是Java的一個非訪問控制修飾符(non-access modifier),可以用於修飾變數、方法和類,有著“不可變”的作用。下面我們簡單看一下final有哪些用法。
1 final變數
當一個變數被final修飾時,它分為兩種情況:
當這個變數是一個基本資料型別時,這時就意味著該變數是可讀的、不可更改的常量(constant),對於常量,有兩個方面的應用:
(1)編譯時常數,它永遠不會改變,對於編譯期的常數,編譯器可將常數值“封裝”到需要的計算過程裡。也就是說,計算可在編譯期間提前執行,從而節省執行時的一些開銷;
(2)在執行期初始化的一個值,我們不希望它發生變化;
而當這個變數是一個物件的引用時,它表示的則是引用只能被賦值一次(引用不可變,但物件本身可變)。
final int MY_CONSTANT_INT = 1;//編譯時常數 // MY_CONSTANT_INT = 2; //編譯錯誤 final int MY_RUNTIME_CONSTANT_INT = (int) (Math.random() * 10);//執行時常數 // MY_RUNTIME_CONSTANT_INT = 3;//編譯錯誤 final List<String> MY_CONSTANT_LIST = new ArrayList<>(); // MY_CONSTANT_LIST = new ArrayList<>();//編譯錯誤 MY_CONSTANT_LIST.add("aa");
2 final方法
當一個方法被宣告為final時,意味著該方法不能被子類覆蓋重寫(overridden)。
class BaseClazz{
public final void method() {
}
}
class ChildClazz extends BaseClazz{
//編輯錯誤
// public final void method() {
//
// }
}
同時,在執行上,final方法比final方法有著更高的效率,編譯器會把對final方法的呼叫轉換為內聯(inline)方法呼叫,即忽略為執行方法呼叫機制(將自變數壓入堆疊;跳至方法程式碼並執行它;跳回來;清除堆疊自變數;最後對返回值進行處理)而採取的常規程式碼嵌入方法。相反,它會用方法主體內實際程式碼的一個副本來替換方法呼叫。這樣做可避免方法呼叫時的系統開銷。當然,若方法體積太大,那麼程式也會變得雍腫,可能感受不到嵌入程式碼所帶來的任何效能提升。因為任何提升都被花在方法內部的時間抵消了。 Java 編譯器能自動偵測這些情況,並頗為“明智”地決定是否嵌入一個 final 方法。
另外,類的 private 方法都自動成為 final方法。
3 final類
如果一個類被final修飾的話,則表明這個類是不可繼承的,我們常用的String就是一個final修飾的類:
final class BaseClazz{
public final void method() {
}
}
//錯誤
//class ChildClazz extends BaseClazz{
//
//}
另外,final類的方法都自動成為 final方法。
4 空白final
空白final是指,已經宣告但並未進行初始化的final變數,但編譯器會保證該變數在實際使用前得到正確的初始化。
final int BLANK_FINAL_INT;//空白final
BLANK_FINAL_INT = 2;
但如果空白final作為成員變量出現,那麼如何保證它的初始化呢,兩種方式:
public class BlankFinalTest {
final int BLANK_FINAL_MEM_INT1;
//程式碼塊初始化
{
BLANK_FINAL_MEM_INT1 = 1;
}
final int BLANK_FINAL_MEM_INT2;
//建構函式初始化
public BlankFinalTest(int i) {
BLANK_FINAL_MEM_INT2 = i;
}
}
使用空白final建立一個不可變(內容不可變)的物件:
class ImmutableClazz{
public final int CONSTAIN_II;
public ImmutableClazz(int i) {
CONSTAIN_II = i;
}
}
//...
public static void main(String[] args) {
ImmutableClazz immutableClazz = new ImmutableClazz(11);
// immutableClazz.CONSTAIN_II = 2;//編譯錯誤
}
5 final和static的區別
final和static修飾符常成對出現,用於宣告常量,但final和static有著什麼區別呢?
static成員變量表示只儲存一份副本,並且所有物件共享該變數;而final的作用是用來保證變數不可變。
final class BaseClazz{
public final double FINAL_DD = Math.random();
public static double STATIC_DD = Math.random();
public final void method() {
}
public static void main(String[] args) {
BaseClazz b1 = new BaseClazz();
BaseClazz b2 = new BaseClazz();
System.out.println(b1.FINAL_DD);
System.out.println(b2.FINAL_DD);
System.out.println(b1.STATIC_DD);
System.out.println(b2.STATIC_DD);
}
}
輸出:
0.17103733059790327
0.237554219342485
0.7814031755793921
0.7814031755793921