<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          讀Java虛擬機(jī)類加載引發(fā)的血案

          共 8737字,需瀏覽 18分鐘

           ·

          2020-12-30 17:00

          來自:花前月下的細(xì)說?|?責(zé)編:樂樂

          鏈接:jianshu.com/u/ef2b368f8b29


          ??? ?

          ? ?正文? ?



          /? ?前言? ?/


          最近在看 Java 虛擬機(jī)類加載的知識(shí)點(diǎn),結(jié)果讓我發(fā)現(xiàn)了自己一個(gè)曾經(jīng)一直糾結(jié),又沒徹底弄懂的類加載黑洞,從而引發(fā)下面一系列的測(cè)試血案。


          相信面試過的你們也會(huì)見過類似下面測(cè)試的這幾道題。不過,答案你真的理解了么?話不多說,直接 GKD??上也皇谴罄校?..哈哈哈 GKD 吧!下面就是測(cè)試過程種發(fā)現(xiàn)的一些疑惑點(diǎn),趕緊記錄一波...


          /? ?正文? ?/


          測(cè)試開始,先思考下下面代碼輸出什么:


          class?Singleton?{

          ????public?Singleton()?{
          ????????System.out.println("Singleton?new?instance");
          ????}

          ????static?{
          ????????System.out.println("Singleton?static?block");
          ????}

          ????{
          ????????System.out.println("Singleton block ?。。?);
          ????}

          }

          public?class?NewTest?{
          ????public?static?void?main(String?args[]){
          ????????Singleton?singleton?=?new?Singleton();
          ????}
          }


          輸出結(jié)果:


          Singleton?static?block
          Singleton block ?。?!
          Singleton?new?instance


          當(dāng)然,大佬們應(yīng)該都能知道答案...畢竟,新手入門級(jí)的野怪,誰都打得過。這個(gè)對(duì)我這小菜雞也算還比較容易理解;加載連接過程,沒有需要處理的 static。new Singleton()?直接開始類的初始化了,所以輸出直接按照類的初始化順序來就好了


          類的初始化的執(zhí)行順序


          沒有父類的情況:


          1. 類的靜態(tài)屬性

          2. 類的靜態(tài)代碼塊

          3. 類的非靜態(tài)屬性

          4. 類的非靜態(tài)代碼塊

          5. 構(gòu)造方法


          有父類的情況:


          1. 父類的靜態(tài)屬性

          2. 父類的靜態(tài)代碼塊

          3. 子類的靜態(tài)屬性

          4. 子類的靜態(tài)代碼塊

          5. 父類的非靜態(tài)屬性

          6. 父類的非靜態(tài)代碼塊

          7. 父類構(gòu)造方法

          8. 子類非靜態(tài)屬性

          9. 子類非靜態(tài)代碼塊

          10. 子類構(gòu)造方法


          這里有個(gè)小誤區(qū),是我自己的誤區(qū)~~比如下面這個(gè)例子:


          class?ParentSingleton{

          public?static?int?value?=?100;

          public?ParentSingleton(){
          ????System.out.println("ParentSingleton?new?instance");
          }

          static?{
          ????System.out.println("ParentSingleton?static?block");
          }

          {
          ????System.out.println("ParentSingleton block ?。。?");
          }

          }


          當(dāng)要初始化上面這個(gè)類的時(shí)候,會(huì)輸出什么?


          如果這時(shí)候,我們只看上面的初始化順序,會(huì)覺得這樣輸出,根據(jù)順序來嘛~


          ParentSingleton?static?block
          ParentSingleton block ?。?!
          ParentSingleton?new?instance
          ???


          OMG,錯(cuò)了,這里的順序不是說,只要初始化,就要全部按照順序一一執(zhí)行...不是這樣的。實(shí)際上只會(huì)輸出:


          ParentSingleton?static?block


          如果有創(chuàng)建這個(gè)類的實(shí)例,比如 new ParentSingleton(),才會(huì):


          ParentSingleton block !??!?
          ParentSingleton?new?instance


          是的,這里的誤區(qū),我曾經(jīng)一度搞錯(cuò)了...尷尬。那再看這個(gè)測(cè)試:


          class?Singleton?{
          private?static?Singleton?singleton?=?new?Singleton();

          private?Singleton()?{
          ????System.out.println("Singleton?new?instance");
          }

          public?static?void?forTest()?{

          }

          static?{
          ????System.out.println("Singleton?static?block");
          }

          {
          ????System.out.println("Singleton block ?。?!?");
          }

          }

          public?class?TestSingleton?{
          public?static?void?main(String?args[]){
          ????Singleton.forTest();
          }
          }


          看完資料的我,逐漸膨脹,畢竟100多斤的胖子,我想的輸出應(yīng)該是:


          在公眾號(hào)程序員小樂回復(fù)“offer”,獲取算法面試題和答案驚喜禮包。


          Singleton?static?block
          Singleton block ?。?!
          Singleton?new?instance


          然后運(yùn)行一看,懵逼了,結(jié)果是:


          Singleton block !??!?
          Singleton?new?instance
          Singleton?static?block


          咋回事啊,小老弟,結(jié)果亂套了...為什么不是先執(zhí)行 static 代碼塊先了。認(rèn)真想了一波,也不知道對(duì)不對(duì),只能瘋狂測(cè)試這樣子...


          經(jīng)過一番測(cè)試,查看資料...最終...我覺得是這樣子的。整個(gè)的流程詳解應(yīng)該是執(zhí)行的第一步:Singleton.forTest();這時(shí)候,對(duì)Singleton類進(jìn)行加載和連接,所以首先需要對(duì)它進(jìn)行加載和連接操作。在連接-準(zhǔn)備階段,要講給靜態(tài)變量賦予默認(rèn)初始值,這里還沒到執(zhí)行 forTest;初始值是 singleton = null。加載和連接完畢之后,再進(jìn)行初始化工作:


          private?static?Singleton?singleton?=?new?Singleton();


          所以執(zhí)行去到了 new Singleton();??這里因?yàn)?new 會(huì)引起 Singleton 的初始化。需要執(zhí)行 Singleton構(gòu)造函數(shù)里面的內(nèi)容。但是又因?yàn)榉莝tatic初始化塊,這里面的代碼在創(chuàng)建java對(duì)象實(shí)例時(shí)執(zhí)行,而且在構(gòu)造器之前?。。。【褪沁@東西...所以輸出應(yīng)該是:


          Singleton block ?。?!?
          Singleton?new?instance


          而根據(jù)類的初始化順序,要執(zhí)行 static 代碼塊,應(yīng)該輸出:


          Singleton?static?block


          完成初始化后。接下來就到真正調(diào)用 forTest 方法了,方法什么都不做,沒輸出。所以,總的答案就是:


          Singleton block ?。?!?
          Singleton?new?instance
          Singleton?static?block


          這里最大的原因就是,連接加載的時(shí)候,要給屬性初始化,而這里的初始化又剛好是 創(chuàng)建java 實(shí)例,需要執(zhí)行構(gòu)造,執(zhí)行構(gòu)造的前面又必須先執(zhí)行 {} 大括號(hào)非 static 塊。而不是和第一個(gè)測(cè)試?yán)幽菢樱瑂tatic 屬性不需要初始化,所以....


          IG 永不加班,但我需要哇,繼續(xù)測(cè)試吧...繼續(xù)測(cè)試驗(yàn)證:


          class?Singleton?{
          private?static?Singleton?singleton?=?new?Singleton();

          private?Singleton()?{
          ????System.out.println("Singleton?new?instance");
          }

          public?static?Singleton?getSingleton()?{
          ????return?new?Singleton();
          }

          static?{
          ????System.out.println("Singleton?static?block");
          }

          {
          ????System.out.println("Singleton block ?。?!?");
          }

          }

          public?class?TestSingleton?{
          public?static?void?main(String?args[]){
          ????Singleton?singleton?=?Singleton.getSingleton();
          }
          }


          輸出結(jié)果如下所示。emm, 再次根據(jù)上面自己的理解,走一遍,應(yīng)該是:


          Singleton block ?。。?
          Singleton?new?instance
          Singleton?static?block
          Singleton block !??!?
          Singleton?new?instance


          這里后面第二次 new 為啥不引起第二次?類的初始化???因?yàn)橐粋€(gè)類只能初始化一次啊!new 只是創(chuàng)建實(shí)例,不再初始化了。所以在調(diào)用 getSingleton 的時(shí)候,只創(chuàng)建實(shí)例就好了,而創(chuàng)建實(shí)例就是:


          Singleton block ?。?!?
          Singleton?new?instance


          在同一個(gè)類加載器下面只能初始化類一次,如果已經(jīng)初始化了就不必要初始化了。為什么只初始化一次呢?類加載的最終結(jié)果就是在堆中存有唯一一個(gè)Class對(duì)象,我們通過Class對(duì)象找到的那個(gè)唯一的。噢?運(yùn)行看一手,丟,對(duì)了..還有存在 final 的時(shí)候,和存在父類的時(shí)候,下面慢慢再測(cè)試驗(yàn)證....繼續(xù)測(cè)試:


          class?Singleton?extends?ParentSingleton?{

          public?Singleton()?{
          ????System.out.println("Singleton?new?instance");
          }

          static?{
          ????System.out.println("Singleton?static?block");
          }

          {
          ????System.out.println("Singleton block !??!?");
          }

          }

          class?ParentSingleton{

          public?ParentSingleton(){
          ????System.out.println("ParentSingleton?new?instance");
          }

          static?{
          ????System.out.println("ParentSingleton?static?block");
          }

          {
          ????System.out.println("ParentSingleton block ?。?!?");
          }

          }

          public?class?TestSingleton?{
          public?static?void?main(String?args[]){
          ????Singleton?singleton?=?new?Singleton();
          }
          }


          輸出結(jié)果如下所示。這個(gè),很明了,還是按照上面的類的初始化,有父類的情況按順序調(diào)用,輸出如下:


          ParentSingleton?static?block
          Singleton?static?block
          ParentSingleton block ?。。?
          ParentSingleton?new?instance
          Singleton block ?。。?
          Singleton?new?instance


          繼續(xù)測(cè)試如下所示。那個(gè)人,又來了...改成和上面沒有父類一樣的情況:


          class?Singleton?extends?ParentSingleton?{
          private?static?Singleton?singleton?=?new?Singleton();

          private?Singleton()?{
          ????System.out.println("Singleton?new?instance");
          }

          public?static?Singleton?getSingleton()?{
          ????return?singleton;
          }

          static?{
          ????System.out.println("Singleton?static?block");
          }

          {
          ????System.out.println("Singleton block !?。?");
          }

          }

          class?ParentSingleton{

          public?ParentSingleton(){
          ????System.out.println("ParentSingleton?new?instance");
          }

          static?{
          ????System.out.println("ParentSingleton?static?block");
          }

          {
          ????System.out.println("ParentSingleton block ?。?!?");
          }

          }

          public?class?TestSingleton?{
          public?static?void?main(String?args[]){
          ????Singleton?singleton?=?Singleton.getSingleton();
          }
          }


          輸出結(jié)果如下所示。這里,就開始懵了...有點(diǎn)。先看結(jié)果:


          ParentSingleton?static?block
          ParentSingleton block !?。?
          ParentSingleton?new?instance
          Singleton block ?。?!?
          Singleton?new?instance
          Singleton?static?block


          其實(shí),很容易看清了,現(xiàn)在,再走一遍流程吧!執(zhí)行到 Singleton.getSingleton() 時(shí),先加載 Singleton ?,這時(shí)因?yàn)?Singleton 有父類,需要需要加載父類先,加載父類 ParentSingleton,根據(jù)加載流程,在連接-準(zhǔn)備階段,要講給靜態(tài)變量賦予默認(rèn)初始值,但父類沒有 static 屬性需要賦值初始化什么的,但是根據(jù)順序,需要初始化static 代碼塊:


          ParentSingleton?static?block


          這時(shí)候回到子類的加載流程。根據(jù)連接-準(zhǔn)備階段,子類有需要處理的屬性 private static Singleton singleton = new Singleton();賦值默認(rèn)值先,singleton = null;然后初始化 singleton = new Singleton();根據(jù)上面的經(jīng)驗(yàn),這里是創(chuàng)建實(shí)例 ,并引起初始化,正常應(yīng)該是:


          Singleton block !??!?
          Singleton?new?instance
          Singleton?static?block


          但是,重點(diǎn)來了 ??!類實(shí)例創(chuàng)建過程:按照父子繼承關(guān)系進(jìn)行初始化,首先執(zhí)行父類的初始化塊部分。然后是父類的構(gòu)造方法;再執(zhí)行本類繼承的子類的初始化塊,最后是子類的構(gòu)造方法,也就是:


          在公眾號(hào)程序員小樂回復(fù)“Java”,獲取Java面試題和答案驚喜禮包。


          ParentSingleton block ?。?!?
          ParentSingleton?new?instance


          同時(shí)子類的初始化,因?yàn)槌跏蓟宇愃懈割悾孕枰瘸跏蓟割悾ǖ沁@里因?yàn)楦割愐呀?jīng)初始化了,就不再初始化了)。所以結(jié)果是:


          ParentSingleton?static?block
          ParentSingleton block ?。?!?
          ParentSingleton?new?instance
          Singleton block ?。?!?
          Singleton?new?instance
          Singleton?static?block


          最終測(cè)試如下所示:


          class?Singleton?extends?ParentSingleton?{
          private?static?Singleton?singleton?=?new?Singleton();

          private?Singleton()?{
          ????System.out.println("Singleton?new?instance");
          }

          public?static?Singleton?getSingleton()?{
          ????return?singleton;
          }

          static?{
          ????System.out.println("Singleton?static?block");
          }

          {
          ????System.out.println("Singleton block ?。?!?");
          }

          }

          class?ParentSingleton{

          private?static?ParentSingleton?parentSingleton?=?new?ParentSingleton();???

          public?ParentSingleton(){

          ????System.out.println("ParentSingleton?new?instance");
          }

          static?{
          ????System.out.println("ParentSingleton?static?block");
          }

          {
          ????System.out.println("ParentSingleton block !??!?");
          }

          }

          public?class?TestSingleton?{
          public?static?void?main(String?args[]){
          ????Singleton?singleton?=?Singleton.getSingleton();
          }
          }


          測(cè)試結(jié)果如下所示:


          ParentSingleton block ?。?!?
          ParentSingleton?new?instance
          ParentSingleton?static?block
          ParentSingleton block ?。。?
          ParentSingleton?new?instance
          Singleton block ?。?!?
          Singleton?new?instance
          Singleton?static?block


          加載一個(gè)類時(shí),先加載父類。按照先加載,創(chuàng)建實(shí)例,初始化,這個(gè)順序就發(fā)現(xiàn)很通順的寫出答案了。哈哈哈哈哈,終于清楚了。所以一切的一切,都是創(chuàng)建實(shí)例這個(gè)東西。搞得我頭暈。


          部分特殊不引起類初始化記錄,先記錄下吧。


          1. 通過子類引用父類的靜態(tài)字段,不會(huì)導(dǎo)致子類初始化,對(duì)于靜態(tài)字段,只有直接定義這個(gè)字段的類才會(huì)被初始化

          2. 通過數(shù)組定義來引用類,不會(huì)觸發(fā)此類的初始化

          3. 常量在編譯階段會(huì)存入調(diào)用類的常量池中,本質(zhì)上并沒有直接引用到定義常量的類,因此不會(huì)觸發(fā)定義常量的類的初始化

          4. public?static?final?int?x?=6/3;??能夠在編譯時(shí)期確定的,叫做編譯常量,不會(huì)引起類的初始化!!!

          5. public?static?final?int?x?=new?Random().nextInt(100);?運(yùn)行時(shí)才能確定下來的,叫做運(yùn)行時(shí)常量,運(yùn)行常量會(huì)引起類的初始化!!!


          在虛擬機(jī)規(guī)范中使用了一個(gè)很強(qiáng)烈的限定語:“有且僅有”,這5種場景中的行為稱為對(duì)類進(jìn)行主動(dòng)引用。除此之外,所有引用類的方式都不會(huì)觸發(fā)初始化,稱為被動(dòng)引用。


          5種必須初始化的場景如下


          1.????遇到new、getstatic、putstatic或invokestatic這4條字節(jié)碼指令時(shí),如果類沒有初始化,則需要先觸發(fā)其初始化


          這4條指令對(duì)應(yīng)的的常見場景分別是:使用new關(guān)鍵字實(shí)例化對(duì)象、讀取或設(shè)置一個(gè)類的靜態(tài)字段(被final修飾、已在編譯期把結(jié)果放入常量池的靜態(tài)字段除外)的時(shí)候,以及調(diào)用一個(gè)類的靜態(tài)方法的時(shí)候。


          注:靜態(tài)內(nèi)容是跟類關(guān)聯(lián)的而不是類的對(duì)象。


          2. ???使用java.lang.reflect包的方法對(duì)類進(jìn)行反射調(diào)用的時(shí)候,如果類沒有進(jìn)行過初始化,則需要先觸發(fā)其初始化。


          注:反射機(jī)制是在運(yùn)行狀態(tài)中,對(duì)于任意一個(gè)類,都能夠知道這個(gè)類的所有屬性和方法


          對(duì)于任意一個(gè)對(duì)象,都能夠調(diào)用它的任意一個(gè)方法和屬性


          這種動(dòng)態(tài)獲取的信息以及動(dòng)態(tài)調(diào)用對(duì)象的方法的功能稱為java語言的反射機(jī)制,這相對(duì)好理解為什么需要初始化類。


          3. ???當(dāng)初始化一個(gè)類的時(shí)候,如果發(fā)現(xiàn)其父類還沒有進(jìn)行過初始化,則需要先觸發(fā)其父類的初始化。


          注:子類執(zhí)行構(gòu)造函數(shù)前需先執(zhí)行父類構(gòu)造函數(shù)


          4. ???當(dāng)虛擬機(jī)啟動(dòng)時(shí),用戶需要指定一個(gè)要執(zhí)行的主類(包含main()方法的那個(gè)類),虛擬機(jī)會(huì)先初始化這個(gè)主類。


          注:main方法是程序的執(zhí)行入口


          5. ???當(dāng)使用JDK1.7的動(dòng)態(tài)語言支持時(shí),如果一個(gè)java.lang.invoke.MethodHandle實(shí)例最后的解析結(jié)果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且這個(gè)方法句柄所對(duì)應(yīng)的類沒有進(jìn)行過初始化。則需要先觸發(fā)其初始化。


          注:JDK1.7的一種新增的反射機(jī)制,都是對(duì)類的一種動(dòng)態(tài)操作


          這回,以后看代碼的時(shí)候,就不會(huì)再被這些執(zhí)行加載順序弄混了,對(duì)優(yōu)化代碼可能還是有幫助的吧。


          再不說,也能再讓我看到這些測(cè)試題,或者問我加載的過程,怎么也能處理回答個(gè)7788了吧。


          可能其中個(gè)人理解有部分紕漏,還請(qǐng)大佬們指出~~蟹蟹鴨!



          PS:歡迎在留言區(qū)留下你的觀點(diǎn),一起討論提高。如果今天的文章讓你有新的啟發(fā),歡迎轉(zhuǎn)發(fā)分享給更多人。


          瀏覽 61
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲日韩Av无码中文字幕美国 | 激情丁香五月天 | 激情黄色五月天 | 国产成人黄色在线观看 | g国产欧美一区二区精品性色超碰 |