您好,登錄后才能下訂單哦!
本篇內(nèi)容主要講解“Java類的加載時機是什么時候”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學(xué)習(xí)“Java類的加載時機是什么時候”吧!
有四種情況類是必須要進行初始化的,對于這四種情況原文描述如下:
但是對于初始化階段,虛擬機規(guī)范則是嚴格規(guī)定了有且只有4種情況必須立即對類進行初始化,而加載、驗證、準(zhǔn)備自然需要在此之前開始。
1:遇到new、getstatic、putstatic或invokestatic這4條字節(jié)碼指令時,如果類沒有進行過初始化,則需要先觸發(fā)其初始化。生成這4條指令最常見的java代碼場景是:使用new關(guān)鍵字實例化對象的時候、讀取或設(shè)置一個類的靜態(tài)字段(被final修飾、已在編譯期把結(jié)果放入常量池的靜態(tài)字段除外)的時候,以及調(diào)用一個類的靜態(tài)方法的時候。
2:使用java.lang.reflect包的方法對類進行反射調(diào)用的時候,如果類沒有進行過初始化,則需要先觸發(fā)其初始化。
3:當(dāng)初始化一個類的時候,如果發(fā)現(xiàn)其父類還沒有進行過初始化,則需要先觸發(fā)其父類的初始化。
4:當(dāng)虛擬機啟動時,用戶需要指定一個要執(zhí)行的主類(包含main()方法的那個類),虛擬機會先初始化這個主類。
以上四點我們一一用代碼來驗證,第一點里面說到了四種初始化的場景,分別是:
①用new關(guān)鍵字實例化對象
②讀取類靜態(tài)字段
③設(shè)置類的靜態(tài)字段
④調(diào)用一個類的靜態(tài)方法
在驗證之前需要達成一個共識:虛擬機在初始化類時會執(zhí)行static語句塊中的操作,因此我們可以根據(jù)靜態(tài)語句塊中的代碼是否執(zhí)行了來判斷類是否加載。為此我創(chuàng)建了一個SubClass類
package com.test.jvm.classloading; /** * @author fc */ public class SubClass { static{ System.out.println("子類初始化"); } public static int a = 10; public static int getA(){ return a; } }
在main方法中分別執(zhí)行(每次執(zhí)行一條)以下四條代碼來模擬上面四個場景
package com.test.jvm.classloading; /** * @author fc */ public class Main { public static void main(String[] args) { SubClass subClass = new SubClass(); System.out.println(SubClass.a); SubClass.getA(); SubClass.a = 30; } }
結(jié)果不出所料,輸出結(jié)果都包含"子類初始化",說明以上四種方式確實可以會觸發(fā)類的初始化。
接下來看第二點,對類進行反射調(diào)用時會觸發(fā)類的初始化
package com.test.jvm.classloading; /** * @author fc */ public class Main { public static void main(String[] args) throws ClassNotFoundException { Class.forName("com.test.jvm.classloading.SubClass"); } }
以上的反射調(diào)用同樣正常輸出了"子類初始化"。
第三點如果父類沒有進行初始化,則要先觸發(fā)父類的初始化
,再創(chuàng)建一個父類,并且讓之前的子類繼承父類
package com.test.jvm.classloading; /** * @author fc */ public class SuperClass { static { System.out.println("父類初始化"); } public static int b = 20; }
package com.test.jvm.classloading; /** * @author fc */ public class SubClass extends SuperClass { static{ System.out.println("子類初始化"); } public static int a = 10; public static int getA(){ return a; } }
這時我們再次執(zhí)行上面的main方法里面的任意一條測試語句,這時發(fā)現(xiàn)在原來的輸出"子類初始化"前輸出了"父類初始化",說明了兩點:①父類同樣會初始化;②父類會先于子類初始化。
第四點虛擬機會先初始化包含main方法的主類
,這時我們在主類中加入靜態(tài)代碼塊
package com.test.jvm.classloading; /** * @author fc */ public class Main { static { System.out.println("初始化主類"); } public static void main(String[] args) throws ClassNotFoundException { SubClass subClass = new SubClass(); } }
可以看到輸出結(jié)果如下,完全印證了第四點。
而對于不會主動進行初始化的情況在該書中也有以下幾種情況
第一種是通過子類類名調(diào)用父類靜態(tài)代碼(包括靜態(tài)方法和靜態(tài)變量)不會進行初始化
,以下也通過代碼進行說明
package com.test.jvm.classloading; /** * @author fc */ public class Main { public static void main(String[] args) throws ClassNotFoundException { System.out.println(SubClass.b); } }
輸出如下,可以看到只初始化了父類而沒有初始化子類。
第二種是通過數(shù)組來創(chuàng)建對象不會觸發(fā)此類的初始化
package com.test.jvm.classloading; /** * @author fc */ public class Main { public static void main(String[] args) throws ClassNotFoundException { SuperClass[] supers = new SuperClass[10]; } }
輸出為空。
第三種是調(diào)用final修飾的常量不會觸發(fā)類的初始化
,為此我在父類中加了一個常量
package com.test.jvm.classloading; /** * @author fc */ public class SuperClass { static { System.out.println("父類初始化"); } public static int b = 20; public final static String STATE = "常量"; }
package com.test.jvm.classloading; /** * @author fc */ public class Main { public static void main(String[] args) { System.out.println(SuperClass.STATE); } }
可以看到輸出結(jié)果只是打印了常量的值,并沒有初始化這個類。
到這里對于書中描述的類的加載時機都已經(jīng)用例子說明了,接下來展示一個在博主Boblim那看到的一個例子
/** * @author fc */ class SingleTon { private static SingleTon singleTon = new SingleTon(); public static int count1; public static int count2 = 0; private SingleTon() { count1++; count2++; } public static SingleTon getInstance() { return singleTon; } } public class Test { public static void main(String[] args) { SingleTon.getInstance(); System.out.println("count1=" + SingleTon.count1); System.out.println("count2=" + SingleTon.count2); } }
輸出count1=1,count2=0,關(guān)于為什么會輸出這個結(jié)果在那篇鏈接的博客已經(jīng)做了詳細的說明,同時這個輸出結(jié)果也很好地佐證了下面這句話
類構(gòu)造器<clinit>()方法是由編譯器自動收集類中的所有類變量的賦值動作和靜態(tài)語句塊(static塊)中的語句合并產(chǎn)生的,編譯器收集的順序是由語句在源文件中出現(xiàn)的順序所決定的。
正是給類變量賦值時是按照順序進行的,所以上面count2又會被重新賦值為0,才導(dǎo)致這個輸出結(jié)果。
到此,相信大家對“Java類的加載時機是什么時候”有了更深的了解,不妨來實際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進入相關(guān)頻道進行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。