一.什麼是向前引用?
有過C++編程經驗的就會知道,一個變量或者方法總是需要先聲明再使用。那麼Java裏面如下的代碼是可以的嗎?
public class GoFirst{
int m = n;//illegal forward reference,無法編譯,報錯
int n = 1;
}
也許我們可以做一些迷惑編譯器的代碼以達到前向引用的目的。
public class GoFirst{
int m = method();;//可以編譯,此時n已被初始化爲0
int n = 1;
int method() {return n; }
}
public static void main(String[] args) {
GoFirst goFirst=new GoFirst();
System.out.println(goFirst.m);//0,首先按照成員聲明順序裝載成員字段,m初始化時n未被初始化,爲默認值0
System.out.println(goFirst.method());//1,此時所有字段初始化已經完畢
System.out.println(goFirst.n);//1
}
在GoFirst類被初始化的時候,第一次的初始化:此時的GoFirst的所有成員變量均被初始化爲各種數據類型的初始值,此時的成員變量已經爲默認值(int類型的默認值爲0,此次初始值均爲編譯器給定的默認值),第二次的初始化:按照成員變量聲明的順序設置我們想要初始值。如m先被設置爲method()的返回值,再初始化n的值爲1。
下面是《Java編程思想第四版》中對象創建過程的描述,假設有一個Dog類:
1.首次創建Dog的對象時或者Dog類的靜態方法/靜態域首次被訪問時,Java解釋器必須查找類路徑,以定位Dog.class文件
2.載入Dog.class(這將會創建一個Class對象)文件,有關靜態初始化的所有動作都會執行,靜態初始化只在Class對象首次加載的時候進行一次
3.當用new Dog()創建對象的時候,首次將在堆上爲Dog對象分配足夠的存儲空間
4.將這片存儲空間清零,這就自動的將Dog對象的基本數據類型都設置爲了默認值,而引用則被設置爲了null
5.執行所有出現於字段定義處的初始化動作
6.執行構造器
二.類初始化順序
Ⅰ.單個類加載順序
/**
* 〈類的初始化順序測試〉
*
* @author 龍
* @create 2018/9/10 15:40
* @since 1.0.0
*/
public class InitOrder {
public int number=initNumber();
{
System.out.println("初始化代碼塊!!!");
}
public static int staticA=initA();
static {
System.out.println("靜態初始化代碼塊!!!");
}
public static int initA(){
System.out.println("初始化靜態字段!!!");
return 5;
}
public int initNumber(){
System.out.println("初始化成員字段!!!");
return 5;
}
public static void main(String[] args) {
System.out.println("main函數開始執行!!!");
InitOrder initOrder=new InitOrder();
}
}
運行結果如下:
初始化靜態字段!!!
靜態初始化代碼塊!!!
main函數開始執行!!!
初始化成員字段!!!
初始化代碼塊!!!
靜態字段和靜態代碼塊,初始化順序只是按照代碼順序執行,初始化成員字段和初始化代碼塊同理。由於main函數爲這個類的靜態成員,所以在main函數執行前依然需要先初始化該類的其它靜態字段和靜態代碼塊。如果在main方法裏面不新建InitOreder對象,則成員字段和代碼塊將不會被初始化。即不會打印後面兩句
Ⅰ.多個類加載順序
public class Parent {
public int numberP=initNumberP();
public static int staticP=initStaticP();
public Parent(){
System.out.println("父類無參構造函數!!!");
}
public Parent(String s){
System.out.println("父類有參構造函數!!!");
}
public static int initStaticP(){
System.out.println("父類初始化靜態字段!!!");
return 5;
}
public int initNumberP(){
System.out.println("父類初始化成員字段!!!");
return 5;
}
}
class Son extends Parent{
public int numberS=initNumberS();
public static int staticS=initStaticS();
public Son(){
super("string");//沒有該句則默認調用父類的無參構造函數
System.out.println("子類構造函數!!!");
}
public static int initStaticS(){
System.out.println("子類初始化靜態字段!!!");
return 5;
}
public int initNumberS(){
System.out.println("子類初始化成員字段!!!");
return 5;
}
public static void main(String[] args) {
System.out.println("main函數開始執行!!!");
Son son=new Son();
}
}
運行結果如下:
父類初始化靜態字段!!!
子類初始化靜態字段!!!
main函數開始執行!!!
父類初始化成員字段!!!
父類有參構造函數!!!
子類初始化成員字段!!!
子類構造函數!!!
就繼承而言,父類靜態成員>子類靜態成員>父類實例成員>子類實例成員
參考資料:
《Java編程思想第四版》