分析Java中的類加載問題
引用1個(gè)網(wǎng)上的經(jīng)典例子,并做稍許改動(dòng),以便大家更好地理解。
public class Animal { private int i = test(); private static int j = method(); static {System.out.println('a'); } Animal(){System.out.println('b'); } {System.out.println('c'); } public int test(){System.out.println('d');return 1; } public static int method(){System.out.println('e');return 1; }}public class Dog extends Animal{ {System.out.println('h'); } private int i = test(); static {System.out.println('f'); } private static int j = method(); Dog(){System.out.println('g'); } public int test(){System.out.println('i');return 1; } public static int method(){System.out.println('j');return 1; } public static void main(String[] args) {Dog dog = new Dog();System.out.println();Dog dog1 = new Dog(); }}
執(zhí)行這段main程序,會(huì)輸出什么?
答案是
eafjicbhigicbhig
為了方便大家一個(gè)個(gè)細(xì)節(jié)去理解, 我換一種方式去提問。
Q: 什么時(shí)候會(huì)進(jìn)行靜態(tài)變量的賦值和靜態(tài)代碼塊的執(zhí)行?
A:
第一次創(chuàng)建某個(gè)類或者某個(gè)類的子類的實(shí)例 訪問類的靜態(tài)變量、調(diào)用類的靜態(tài)方法 使用反射方法forName 調(diào)用主類的main方法(本例子的第一次靜態(tài)初始化其實(shí)屬于這個(gè)情況,調(diào)用了Dog的main方法)注: 類初始化只會(huì)進(jìn)行一次, 上面任何一種情況觸發(fā)后,之后都不會(huì)再引起類初始化操作。
Q:初始化某個(gè)子類時(shí),也會(huì)對(duì)父類做靜態(tài)初始化嗎?順序呢?
A:如果父類之前沒有被靜態(tài)初始化過,那就會(huì)進(jìn)行, 且順序是先父類再子類。 后面的非靜態(tài)成員初始化也是如此。所以會(huì)先輸出eafj。
Q: 為什么父類的method不會(huì)被子類的method重寫?
A: 靜態(tài)方法是類方法,不會(huì)被子類重寫。畢竟類方法調(diào)用時(shí),是必定帶上類名的。
Q: 為什么第一個(gè)輸出的是e而不是a?
A: 因?yàn)轭愖兞康娘@示賦值代碼和靜態(tài)代碼塊代碼按照從上到下的順序執(zhí)行。
Animal的靜態(tài)初始化過程中,method的調(diào)用在static代碼塊之前,所以先輸出e再輸出a。
而Dog的靜態(tài)初始化過程中,method的調(diào)用在static代碼塊之后,因此先輸出f,再輸出j
Q: 沒有在子類的構(gòu)造器中調(diào)用super()時(shí),也會(huì)進(jìn)行父類對(duì)象的實(shí)例化嗎?
A: 會(huì)的。會(huì)自動(dòng)調(diào)用父類的默認(rèn)構(gòu)造器。 super()主要是用于需要調(diào)用父類的特殊構(gòu)造器的情況。因此會(huì)先進(jìn)行Animal的對(duì)象實(shí)例化,再進(jìn)行Dog的對(duì)象實(shí)例化
Q: 構(gòu)造方法、成員顯示賦值、非靜態(tài)代碼塊(即輸出c和h的那2句)的順序是什么?
A:
1.成員顯示賦值、非靜態(tài)代碼塊(按定義順序)
2.構(gòu)造方法
因此Animal的實(shí)例化過程輸出icb(如果對(duì)輸出i有疑問,見下面一題)接著進(jìn)行Dog的實(shí)例化,輸出hig
Q: 為什么Animal實(shí)例化時(shí), i=test()中輸出的是i而不是d?
A:因?yàn)槟阏嬲齽?chuàng)建的是Dog子類,Dog子類中的test()方法由于簽名和父類test方法一致,因此test方法被重寫了。此時(shí)即使在父類中調(diào)用,也還是用使用子類Dog的方法。除非你new的是Animal。
Q: 同上題, 如果test方法都是private或者final屬性, 那么上題的情況會(huì)有變化嗎??
A:
因?yàn)閜rivate和final方法是不能被子類重寫的。所以Animal實(shí)例化時(shí),i=test輸出d。
總結(jié)一下順序:
1.父類靜態(tài)變量顯式賦值、父類靜態(tài)代碼塊(按定義順序)
2.子類靜態(tài)變量顯式賦值、子類靜態(tài)代碼塊(按定義順序)
3.父類非靜態(tài)變量顯式賦值(父類實(shí)例成員變量)、父類非靜態(tài)代碼塊(按定義順序)
4.父類構(gòu)造函數(shù)
5.子類非靜態(tài)變量(子類實(shí)例成員變量)、子類非靜態(tài)代碼塊(按定義順序)
6.子類構(gòu)造函數(shù)。
二、類加載過程Q:類加載的3個(gè)必經(jīng)階段是:
A:
1.加載(類加載器讀取二進(jìn)制字節(jié)流,生成java類對(duì)象)
2.鏈接(驗(yàn)證,分配靜態(tài)域初始零值)
3.初始化(前面的題目講的其實(shí)就是初始化時(shí)的順序)
更詳細(xì)的如下:
Q:new某個(gè)類的數(shù)組時(shí),會(huì)引發(fā)類初始化嗎?
像下面輸出什么
public class Test { static class A{public static int a = 1;static{ System.out.println('initA');} } public static void main(String[] args) {A[] as = new A[5]; }}
A:
new數(shù)組時(shí),不會(huì)引發(fā)類初始化。什么都不輸出。
Q:引用類的final靜態(tài)字段,會(huì)引發(fā)類初始化嗎?
像下面輸出什么?
public class Test { static class A{public static final int a = 1;static{ System.out.println('initA');} } public static void main(String[] args) {System.out.println('A.a=' + A.a); }}
A: 不會(huì)引發(fā)。
不會(huì)輸出initA。 去掉final就會(huì)引發(fā)了。(注意這里必須是基本類型常量, 如果是引用類型產(chǎn)量,則會(huì)引發(fā)類初始化)
Q:子類引用了父類的靜態(tài)成員,此時(shí)子類會(huì)做類初始化嘛?
如下會(huì)輸出什么
public class Test { static class A{public static int a = 1;static{ System.out.println('initA');} } static class B extends A{static { System.out.println('initB');} } public static void main(String[] args) {System.out.println('B.a=' + B.a); }}
A:
子類不會(huì)初始化。打印initA,卻不會(huì)打印initB。
四、類加載器雙親委派類加載時(shí)的雙親委派模型,不知道能怎么出題。。。反正就記得優(yōu)先去父類加載器中看類是否能加載。
Bootsrap不是ClassLoader的子類,他是C++編寫的。而ExtClassLoader和AppClassLoader都是繼承自ClassLoader的
Q:java中, 是否類和接口的包名和名字相同, 那么就一定是同一個(gè)類或者接口?
A:錯(cuò)誤。
1個(gè)jvm中, 類和接口的唯一性由二進(jìn)制名稱以及它的定義類加載器共同決定。因此2個(gè)不同的加載器加載出來相同的類或接口時(shí), 實(shí)際上是不同的。
以上就是分析Java中的類加載問題的詳細(xì)內(nèi)容,更多關(guān)于Java 類加載的資料請(qǐng)關(guān)注好吧啦網(wǎng)其它相關(guān)文章!
相關(guān)文章:
1. 如何對(duì)php程序中的常見漏洞進(jìn)行攻擊2. PHP循環(huán)與分支知識(shí)點(diǎn)梳理3. ThinkPHP5 通過ajax插入圖片并實(shí)時(shí)顯示(完整代碼)4. JSP之表單提交get和post的區(qū)別詳解及實(shí)例5. SXNA RSS Blog 聚合器程序6. ASP中格式化時(shí)間短日期補(bǔ)0變兩位長(zhǎng)日期的方法7. Ajax請(qǐng)求超時(shí)與網(wǎng)絡(luò)異常處理圖文詳解8. JavaWeb Servlet中url-pattern的使用9. jsp EL表達(dá)式詳解10. 利用ajax+php實(shí)現(xiàn)商品價(jià)格計(jì)算
