【Java基礎】初始化時代碼塊執行順序

本文講下類在初始化加載時,靜態代碼塊、構造代碼塊、構造函數以及有父類時的執行順序。

1、無父類時

看個例子就一目瞭然了。

public class TempTest {
    private static final Logger LOGGER = LoggerFactory.getLogger(TempTest.class);

    static {
        LOGGER.info("靜態代碼塊1");
    }

    static {
        LOGGER.info("靜態代碼塊2");
    }

    {
        LOGGER.info("構造代碼塊1");
    }

    {
        LOGGER.info("構造代碼塊2");
    }

    TempTest() {
        LOGGER.info("無參構造器");
    }

    TempTest(String text) {
        LOGGER.info("有參構造器");
    }

    public static void main(String[] args) {
        TempTest tempTest1 = new TempTest();
        TempTest tempTest2 = new TempTest("有參");
    }
}

輸入結果如下:

[main] 靜態代碼塊1
[main] 靜態代碼塊2
[main] 構造代碼塊1
[main] 構造代碼塊2
[main] 無參構造器
[main] 構造代碼塊1
[main] 構造代碼塊2
[main] 有參構造器

可以看到執行順序是靜態代碼塊->構造代碼塊->構造器靜態代碼塊、構造代碼塊分別按照編寫的順序依次執行。日誌是在第一個聲明的,所以後面調用都是正常的,如果你把日誌聲明放在了後面,直接就會報錯。

從日誌也可以看到,靜態代碼塊只在類初始化加載時執行一次,而構造代碼塊只要new了一個對象就會執行一次,不管你調用哪個構造器,都會執行。

反編譯看下:

public class TempTest {
    private static final Logger LOGGER = LoggerFactory.getLogger(TempTest.class);

    TempTest() {
        LOGGER.info("構造代碼塊1");
        LOGGER.info("構造代碼塊2");
        LOGGER.info("無參構造器");
    }

    TempTest(String text) {
        LOGGER.info("構造代碼塊1");
        LOGGER.info("構造代碼塊2");
        LOGGER.info("有參構造器");
    }

    public static void main(String[] args) {
        new TempTest();
        new TempTest("有參");
    }

    static {
        LOGGER.info("靜態代碼塊1");
        LOGGER.info("靜態代碼塊2");
    }
}

可以看到,編譯之後會把靜態代碼塊合併在一起,順序是編寫的先後順序,而構造代碼塊被依次放在了各個構造器中,且位於你自己編寫的代碼前面。利用這些特性,一般靜態塊中可以進行一些配置、連接等的初始化,只要執行一次的那種。而構造代碼塊在每創建一個對象時都會執行一次,可以做創建對象的計數。

2、有父類時

看個例子:

public class BaseTest {
    private static final Logger LOGGER = LoggerFactory.getLogger(BaseTest.class);

    static {
        LOGGER.info("父類靜態代碼塊1");
    }

    static {
        LOGGER.info("父類靜態代碼塊2");
    }

    {
        LOGGER.info("父類構造代碼塊1");
    }

    {
        LOGGER.info("父類構造代碼塊2");
    }

    BaseTest() {
        LOGGER.info("父類無參構造器");
    }

    BaseTest(String text) {
        LOGGER.info("父類有參構造器");
    }
}

public class TempTest extends BaseTest{
    private static final Logger LOGGER = LoggerFactory.getLogger(TempTest.class);

    static {
        LOGGER.info("子類靜態代碼塊1");
    }

    static {
        LOGGER.info("子類靜態代碼塊2");
    }

    {
        LOGGER.info("子類構造代碼塊1");
    }

    {
        LOGGER.info("子類構造代碼塊2");
    }

    TempTest() {
        super();
        LOGGER.info("子類無參構造器");
    }

    TempTest(String text) {
        super(text);
        LOGGER.info("子類有參構造器");
    }

    public static void main(String[] args) {
        TempTest tempTest1 = new TempTest();
        TempTest tempTest2 = new TempTest("有參");
    }
}

輸出如下:

BaseTest][main] 父類靜態代碼塊1
BaseTest][main] 父類靜態代碼塊2
TempTest][main] 子類靜態代碼塊1
TempTest][main] 子類靜態代碼塊2
BaseTest][main] 父類構造代碼塊1
BaseTest][main] 父類構造代碼塊2
BaseTest][main] 父類無參構造器
TempTest][main] 子類構造代碼塊1
TempTest][main] 子類構造代碼塊2
TempTest][main] 子類無參構造器
BaseTest][main] 父類構造代碼塊1
BaseTest][main] 父類構造代碼塊2
BaseTest][main] 父類有參構造器
TempTest][main] 子類構造代碼塊1
TempTest][main] 子類構造代碼塊2
TempTest][main] 子類有參構造器

可以看到執行順序爲父類靜態代碼塊->子類靜態代碼塊->父類構造代碼塊->父類構造器->子類構造代碼塊->子類構造器

從第一部分的反編譯知道,構造代碼塊是被編譯進各個構造器的,這就是爲什麼父類構造代碼塊執行之後執行的是父類構造器而不是子類構造代碼塊了。知道了構造代碼塊是被編譯進各個構造器這一點,就不難理解了。

 

 

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