Java的繼承是比較重要的特性,也是比較容易出錯的地方,下面這個例子將展現若是父類構造器中調用被子類重寫的方法時會出現的狀況:java
首先是父類:函數
public class test { void fun(){ System.out.println("test fun()"); } void fun1(){ System.out.println("test fun1()"); } test(){ fun(); fun1(); } public static void main(String[] args) { test t =new test(); } }
這裏父類的構造器將調用一個fun方法,main函數的運行結果是:spa
test fun()
test fun1()code
而後是子類:對象
public class test2 extends test { int i = 2; test2(){ fun(); } void fun(){ System.out.println("test2 fun()"); System.out.println(i); } public static void main(String[] args) { test2 t = new test2(); } }
子類增長了一個字段i並初始化爲2,並重寫了fun方法,不只打印的字符串不同,還加了打印i的功能,構造器和父類同樣調用了fun方法。main函數的運行結果是:blog
test2 fun()
0
test fun1()
test2 fun()
2繼承
一般java的類進行初始化的時候,會先進行父類的初始化,因此會先調用父類的構造器,再進行子類的初始化,調用子類的構造器。字符串
一開始寫完代碼我覺得的結果是:class
test fun()
test fun1()
test2 fun()
2
我覺得就算父類的方法被重寫了,也會調用本身的方法,但事實告訴咱們,父類初始化過程當中構造器若是調用了被子類重寫的方法,會調用被子類重寫的方法。test
還有一點,若是子類重寫的方法中使用了子類才定義的字段,那這個字段的值將是該字段類型的默認值。
因此類的初始化流程總結(繼承相關)就是:
1.爲對象分配的存儲空間初始化爲二進制零。
2.調用父類的構造器,若是調用被覆蓋的方法,被覆蓋的方法將被調用,若是使用了子類中才定義的字段,該字段的值爲該字段類型的默認值。
3.調用子類的構造器。
(這裏總結的初始化流程只總結了繼承相關的,正常的static部分、初始化代碼仍是正常的樣子)
爲何會出現這種狀況呢,爲何須要先爲對象的存儲空間初始化爲二進制零呢?
1.在繼承中構造器的調用是分級的,先調用父類的,父類若是有父類就父類的。。。這一步是經過動態綁定實現的。
2.從概念上來講,構造器是用來初始化對象的,可是像上面那種狀況,子類重寫了父類的方法,使得父類將使用子類的成員,可是此時正在初始化父類,子類尚未進行初始化。
3.基於以上兩點就將爲對象分配的存儲空間初始化爲二進制零。
因此重寫父類的方法的時候須要考慮到這個特性,這種特性可能會致使父類的初始化出現問題。