Java中的前向引用與類初始化順序

一.什麼是向前引用?
  有過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編程思想第四版》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章