我也是小白,各位倘若看到一定要帶思考的去看。
一、背景
存在類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方法中就是正在創建的對象