Java中的類初始化和實例初始化


我也是小白,各位倘若看到一定要帶思考的去看。

一、背景

存在類Father和類Son,其中類Son繼承了Father類。

1.父類: Father

/**
 * @Author Yiang37
 * @Date 2020/4/9 23:44
 * Description:
 */
public class Father {

    private final int i = test();
    private static final int j = method();
    
    static {
        System.out.print("(1)");
    }

    {
        System.out.print("(3)");
    }

    Father() {
        System.out.print("(2)");
    }

    public int test() {
        System.out.print("(4)");
        return 1;
    }

    public static int method() {
        System.out.print("(5)");
        return 1;
    }

}

2.子類: Son

package cn.yang37.classloading.loadstep;

/**
 * @Author Yiang37
 * @Date 2020/4/9 23:44
 * Description:
 */
public class Son extends Father {

    private final int i = test();
    private static final int j = method();

    static {
        System.out.print("(6)");
    }

    {
        System.out.print("(8)");
    }

    Son() {
        System.out.print("(7)");
    }


    @Override
    public int test() {
        System.out.print("(9)");
        return 1;
    }

    public static int method() {
        System.out.print("(10)");
        return 1;
    }


    public static void main(String[] args) {
//        Son s1 = new Son();
//        System.out.println();
//        Son s2 = new Son();
    }
}

二、初始化

初始化包括?

成員變量賦初值、代碼塊、構造器

注意方法是被調用的,有人調用它它才執行相應的東西。

三、類初始化

在一開始,注釋掉son類main方法中代碼

    public static void main(String[] args) {
//        Son s1 = new Son();
//        System.out.println();
//        Son s2 = new Son();
    }

執行結果如下:

(5)(1)(10)(6)

1.規則

1.1 實例初始化之前先要進行類的初始化(加載類),即執行一些static相關的操作.

所以你執行空的main()也會輸出結果,因為在進行類的初始化.

1.2 子類初始化要先初始化其父類

即先執行的Father的,再執行Son的.

1.3 類初始化即是執行clinit(ClassInit)方法

  • 加載靜態變量/靜態代碼塊
  • 按順序加載(上到下的順序執行)
  • 這個clinit方法只執行一次,即類只初始化一次即可.

2.擴展

2.1 將父類中的static代碼塊放到靜態變量之前,可以看到結果按順序改變。

package cn.yang37.classloading.loadstep;

/**
 * @Author Yiang37
 * @Date 2020/4/9 23:44
 * Description:
 */
public class Father {

    private final int i = test();

    static {
        System.out.print("(1)");
    }
    
    private static final int j = method();
    
    {
        System.out.print("(3)");
    }

    Father() {
        System.out.print("(2)");
    }

    public int test() {
        System.out.print("(4)");
        return 1;
    }

    public static int method() {
        System.out.print("(5)");
        return 1;
    }

}

先輸出1再輸出5,即按順序加載.

(1)(5)(10)(6)

四、實例初始化

取消Son類中main方法的第一句注釋,即:

進行new操作,即實例的初始化.

    public static void main(String[] args) {
        Son s1 = new Son();
//        System.out.println();
//        Son s2 = new Son();
    }

此時將會先進行類初始化,再進行實例初始化,結果如下。

(5)(1)(10)(6)(9)(3)(2)(9)(8)(7)

1.規則

1.1 實例初始化即是執行init方法

Java文件在編譯后會在字節碼文件中生成init方法,該方法被稱之為實例構造器。init方法是在對象實例化時執行的。

1.2 方法中調用幾個構造器就執行幾個init方法,每次創建對象都會執行相應的init方法。

1.3 init方法由非靜態實例變量顯式賦值、非靜態代碼塊和相應的構造器組成。

1.4 非靜態實例變量賦值與非靜態代碼塊按上到下的順序執行,構造器在最后執行。

構造器嘛,要用到變量之類的,肯定最后階段啦.

1.5 init方法的首行是super()或者帶參數的super(),即父類init方法。

2.擴展

2.1 父類中的test()方法輸出的是(4),為何父類中結果為932?

非靜態方法前有一個默認的對象this

this在構造器(或者init方法)表示的是正在創建的對象。

這里是在創建Son類的對象,所以執行的是重寫后的代碼。

2.1 取消main中的所有注釋

    public static void main(String[] args) {
        Son s1 = new Son();
        System.out.println();
        Son s2 = new Son();
    }

運行結果:

(5)(1)(10)(6)(9)(3)(2)(9)(8)(7)
(9)(3)(2)(9)(8)(7)

可以看到(9)(3)(2)(9)(8)(7)執行了兩次,因為new Son()執行了兩次.

五、總結

初始化操作包括成員變量、靜態/非靜態代碼塊和構造器。

1.先類初始化,再實例初始化。

2.都是先父類再子類。

3.類初始化關鍵詞"靜態"。

4.實例初始化關鍵詞"非靜態",構造器在最后。

5.注意構造器和實例初始化init方法中的this對象。

六、補充

1.哪些方法不能被重寫?

final方法
靜態方法
private等子類中不可見方法

2.多態性的體現?

子類若重寫了父類的方法,則通過子類對象調用的一定是子類重寫過的方法
非靜態方法默認的調用對象是this
this對象在構造器或者init方法中就是正在創建的對象


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM