1. 程式人生 > >11-程式碼的執行順序

11-程式碼的執行順序

一、程式碼執行順序

1、編譯器編譯完之後,.class 檔案中就包含構造函數了。且靜態成員在編譯生成 .class 檔案時,就已經有所屬的類了(靜態成員隨著類的載入而存在)

2、程式碼執行順序:

(1)當執行 java xxx 命令時,類載入進記憶體,開闢空間。方法區中,建構函式等非靜態方法載入進方法區,靜態成員載入進方法區的靜態區。以上載入順序按照程式碼的執行順序。方法區中儲存的是方法的程式碼,無論是靜態的還是非靜態的方法,在方法區中都是共享的

注:方法區的靜態區中,除了儲存靜態方法,還儲存靜態變數。靜態變數進入方法區的靜態區後,先預設初始化,後進行指定初始化(包括在堆中建立物件,並將地址值賦給靜態變數 -- 單例模式)

(2)方法載入進方法區後,JVM呼叫main函式執行,main函式進棧

注:方法執行需要進棧。棧是執行區,方法進棧是因為區域性變數存在棧中。方法進棧,如果有區域性變數,則在對應的方法中指明。執行方法時,實際執行的程式碼還是方法區中方法的程式碼

(3)在main函式中,使用到哪個類,哪個類就開闢空間,載入進記憶體 -- 在方法區中載入方法。如果是子父類,父類先於子類載入。且子類載入進記憶體時,方法區中子類的方法就持有一個super引用指向父類空間

注:main函式是靜態方法,想要在main中呼叫非靜態的方法,只能建立物件,用物件呼叫

(4)new時,就在堆記憶體中開闢空間,分配地址值,進行預設初始化。如果是子父類,子類建立物件後,父類先於子類載入進子類物件,分空間儲存,分別初始化。父類的成員變數持有super,子類的成員變數持有this。然後建構函式進棧,執行建構函式中的方法,進行建構函式初始化,有引數就進行引數值的傳遞(不呼叫建構函式,物件初始化不成功)。之後建構函式彈棧,並將地址值賦給棧中對應的區域性變數,區域性變數指向堆中物件

注:靜態變數在方法區的靜態區中,成員變數在堆記憶體的物件中,區域性變數在棧中

      如果呼叫靜態變數,無需進棧,直接在方法區的靜態區中被使用。如果呼叫方法(靜態和非靜態方法),都需要進棧

3、子父類中子類物件的例項化過程:

(1)new Xxx()時,JVM讀取指定路徑(classpath)下的Xxx.class檔案,載入進記憶體。如果有父類,會先載入父類再載入子類(方法區中,先載入父類的方法,再載入子類的方法,且子類的方法持有super指向父類空間)

(2)在堆記憶體中開闢空間,分配地址值,進行預設初始化

(3)執行子類建構函式的第一句super(),轉去執行父類顯示初始化建構函式

(4)父類初始化完畢後,執行

成員變數的顯示初始化,之後執行子類建構函式

eg1:

class Fu {
    Fu() {
        //super();
        show();
        //return;
    }
    void show() {
        System.out.println("Fu show");
    }
}
class Zi extends Fu {
    int num = 8;
    Zi() {
        //super();
        System.out.println("Zi constructor run ... " + num);
        num = 10;
        //return;
    }
    @Override
    void show() {
        System.out.println("Zi show ... " + num);
    }
}
class ExtendsDemo {
    public static void main(String[] args) {
        Zi z = new Zi();
        z.show();
    }
}

結果:

Zi show ... 0

Zi constructor run ...8

Zi show ... 10

說明:new Zi()時,在堆中開闢空間,分配地址值,進行預設初始化,此時num=0。之後建構函式進棧,進行子類初始化。執行建構函式第一句super()時,呼叫父類建構函式,父類建構函式中呼叫show()方法,因為子父類持有的this都是子類堆中物件的地址值,所以執行的是子類的show()方法。父類建構函式執行完畢彈棧,進行子類成員變數顯示初始化,此時num=8。之後繼續執行子類的建構函式,進行建構函式初始化,num=10

eg2:

class Fu {
    Fu() {
        //super();
        System.out.println("Fu constructor run");
        show();
        //return;
    }
    void show() {
        System.out.println("Fu show");
    }
}
class Zi extends Fu {
    int num = 8;
    {
        System.out.println("constructor ... " + num);
        num = 9;
    }
    Zi() {
        //super();
        System.out.println("Zi constructor run ... " + num);
        num = 10;
        //return;
    }
    @Override
    void show() {
        System.out.println("Zi show ... " + num);
    }
}
class ExtendsDemo {
    public static void main(String[] args) {
        Zi z = new Zi();
        z.show();
    }
}

結果:

Fu constructor run

Zi show ... 0

Zi constructor ... 8

Zi constructor run ... 9

Zi show ... 10

說明:new Zi() 時,子類建構函式先載入進記憶體,分配空間,進行預設初始化。之後執行子類建構函式,在建構函式的第一行,通過super()呼叫父類建構函式進行父類初始化(顯示初始化、構造初始化)。父類初始化完畢後,子類進行顯示初始化,構造程式碼塊初始化,建構函式初始化