final可以用來修飾數據、方法、類。
final數據
1、final修飾變量
final修飾基本數據類型的變量時,必須賦予初始值且不能被改變。
static final修飾的是常量,常量分爲編譯時常量與運行時常量,定義爲static,強調只有一份。
比如static final int a = 10;就是編譯時常量,只要是該常量帶入的語句,在編譯過後都會替換。
這段代碼
在反編譯後變成
而且只要這種常量在的算式,在編譯時期就會被計算。(只有基本數據類型和String)
反編譯後
很明顯了。
關於運行時常量:static final int a = “123”.length();就是一個運行時常量,它的賦值會引起類的初始化。
編譯器常量有一個風險。就比如A類定義了常量,B類使用了常量。但現在需要修改A類源碼的常量值,對A類重新編譯,但沒對B類重新編譯,這就導致A類與B類的常量值不一樣。
2、final修飾引用
對於對象引用,final使引用恆定不變。就是一旦引用指向一個對象,就無法把他再指向另一個對象,但對象是可以修改的。數組也一樣,畢竟數組也是對象。
3、空白final
所謂空白final就是被聲明爲final但未賦初值的域,但在使用前,必須保證賦初值,這樣就可以靈活的爲其進行賦值,但是又保持了其不可更改的特性 。
必須在域定義處(final修飾局部變量)或每個構造器裏(final修飾成員變量)用表達式對final進行賦值。
public class Tests {
private final int j;
public Tests(){
j = 0;
}
public Tests(int x){
j = x;
}
public static void main(String[] args) {
new Tests(12);
final int k;
k = 2;
System.out.println(k);
}
}
3、final參數
被final修飾的參數稱位final參數。意思是你無法更改參數引用所指向的變量,可以讀參數,但不可修改參數。主要用於向內部類傳遞數據。
4、final方法
被final修飾的方法,只能被繼承,不能被覆蓋
final與private關鍵字:類中所有private方法都被隱式指定是final的,但如果在子類定義一個與父類方法名、參數列表、返回值都相同的private方法也可以,但調用的時候,還是按照private的法則,private方法只在本類中使用,而且用@Override也會報錯,也就是說父類與子類的相同的private方法沒有任何關係。
5、final類
當類被final修飾時,表示類是不可被繼承的。final類中的所有方法都是隱式的指定爲final的,無法被覆蓋。但成員變量不是final,你可以人爲指定某個成員變量是final。
繼承與初始化
如下代碼:
public class Battle extends Insect {
private int k = printInit("Battle.k initialized");
public Battle(){
System.out.println("k = " + k);
System.out.println("j = " + j);
}
private static int x2 = printInit("static Battle.x2 initialized");
public static void main(String[] args) {
System.out.println("Battle constructor");
Battle t = new Battle();
}
}
class Insect{
private int i = 9;
protected int j;
Insect(){
System.out.println("i = " + i + ", j = " + j);
j = 39;
}
private static int x1 = printInit("static Insect.x1 initialized");
static int printInit(String s){
System.out.println(s);
return 47;
}
}
輸出
在Battle上運行Java時,程序想要訪問Battle類的main方法,這時加載器就要找出Battle的.class文件,對它進行加載。加載過程中,發現它有一個父類,於是就要對父類進行加載,如果發現父類還有父類,那就要加載“爺爺類”,不要認爲加載父類是由於本類調用了父類的靜態方法,實際上去掉調用父類的成員變量,父類還是先加載。
父類的static初始化完後,就輪到自己的static執行,執行完後,也就輸出了前兩行。
執行到這,說明必要的類已經加載完畢了。進入main方法,輸出Battle constructor。開始創建對象。先把對象內所有的基本類型設置爲默認值,引用類型設置爲null(通過將對象內存設爲二進制零值一舉生成的),然後,構造器開始調用,我們知道,子類構造器會默認在第一行添加super(),先調用父類構造器,輸出i = 9, j = 0;,然後子類構造器執行。