站長資訊網(wǎng)
        最全最豐富的資訊網(wǎng)站

        一次性帶你弄懂java中的static關鍵字

        相信有不少同學遇到過這類問題,可能查過資料之后接著就忘了,再次遇到還是答不對。接下來通過4個步驟,帶大家拆解一下這段代碼的執(zhí)行順序,并借此總結規(guī)律。

        開篇一道題,考察代碼執(zhí)行順序:

        public class Parent {     static {         System.out.println("Parent static initial block");     }      {         System.out.println("Parent initial block");     }      public Parent() {         System.out.println("Parent constructor block");      } }  public class Child extends Parent {     static {         System.out.println("Child static initial block");     }      {         System.out.println("Child initial block");     }          private Hobby hobby = new Hobby();      public Child() {         System.out.println("Child constructor block");     } }  public class Hobby {     static{         System.out.println("Hobby static initial block");     }      public Hobby() {         System.out.println("hobby constructor block");     } }

        當執(zhí)行new Child()時,上述代碼輸出什么?

        相信有不少同學遇到過這類問題,可能查過資料之后接著就忘了,再次遇到還是答不對。接下來課代表通過4個步驟,帶大家拆解一下這段代碼的執(zhí)行順序,并借此總結規(guī)律。

        1.編譯器優(yōu)化了啥?

        下面兩段代碼對比一下編譯前后的變化:

        編譯前的Child.java

        public class Child extends Parent {     static {         System.out.println("Child static initial block");     }     {         System.out.println("Child initial block");     }          private Hobby hobby = new Hobby();          public Child() {         System.out.println("Child constructor block");     } }

        編譯后的Child.class

        public class Child extends Parent {     private Hobby hobby;      public Child() {         System.out.println("Child initial block");         this.hobby = new Hobby();         System.out.println("Child constructor block");     }      static {         System.out.println("Child static initial block");     } }

        通過對比可以看到,編譯器把初始化塊和實例字段的賦值操作,移動到了構造函數(shù)代碼之前,并且保留了相關代碼的先后順序。事實上,如果構造函數(shù)有多個,初始化代碼也會被復制多份移動過去。

        據(jù)此可以得出第一條優(yōu)先級順序:

        • 初始化代碼 > 構造函數(shù)代碼

        2.static 有啥作用?

        類的加載過程可粗略分為三個階段:加載 -> 鏈接 -> 初始化

        初始化階段可被8種情況周志明》P359 "觸發(fā)類初始化的8種情況")觸發(fā):

        • 使用 new 關鍵字實例化對象的時候

        • 讀取或設置一個類型的靜態(tài)字段(常量")除外)

        • 調用一個類型的靜態(tài)方法

        • 使用反射調用類的時候

        • 當初始化類的時候,如果發(fā)現(xiàn)父類還沒有進行過初始化,則先觸發(fā)其父類初始化

        • 虛擬機啟動時,會先初始化主類(包含main()方法的那個類)

        • 當初次調用 MethodHandle 實例時,初始化該 MethodHandle 指向的方法所在的類。

        • 如果接口中定義了默認方法(default 修飾的接口方法),該接口的實現(xiàn)類發(fā)生了初始化,則該接口要在其之前被初始化

        其中的2,3條目是被static代碼觸發(fā)的。

        其實初始化階段就是執(zhí)行類構造器<clinit> 方法的過程,這個方法是編譯器自動生成的,里面收集了static修飾的所有類變量的賦值動作和靜態(tài)語句塊(static{} 塊),并且保留這些代碼出現(xiàn)的先后順序。

        根據(jù)條目5,JVM 會保證在子類的<clinit>方法執(zhí)行前,父類的<clinit>方法已經(jīng)執(zhí)行完畢。

        小結一下:訪問類變量或靜態(tài)方法,會觸發(fā)類的初始化,而類的初始化就是執(zhí)行<clinit>,也就是執(zhí)行 static 修飾的賦值動作和static{}塊,并且 JVM 保證先執(zhí)行父類初始化,再執(zhí)行子類初始化。

        由此得出第二條優(yōu)先級順序:

        • 父類的static代碼 > 子類的static代碼

        3.static 代碼只執(zhí)行一次

        我們都知道,static代碼(靜態(tài)方法除外)只執(zhí)行一次。

        你有沒有想過,這個機制是如何保證的呢?

        答案是:雙親委派模型。

        JDK8 及之前的雙親委派模型是:

        應用程序類加載器 → 擴展類加載器 → 啟動類加載器

        平時開發(fā)中寫的類,默認都是由 應用程序類加載器加載,它會委派給其父類:擴展類加載器。而擴展類加載器又會委派給其父類:啟動類加載器。只有當父類加載器反饋無法完成這個加載請求時,子加載器才會嘗試自己去完成加載,這個過程就是雙親委派。三者的父子關系并不是通過繼承,而是通過組合模式實現(xiàn)的。

        該過程的實現(xiàn)也很簡單,下面展示關鍵實現(xiàn)代碼:

        protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {     // 首先檢查該類是否被加載過     // 如果加載過,直接返回該類     Class<?> c = findLoadedClass(name);     if (c == null) {         try {             if (parent != null) {                 c = parent.loadClass(name, false);             } else {                 c = findBootstrapClassOrNull(name);             }         } catch (ClassNotFoundException e) {             // 如果父類拋出ClassNotFoundException             // 說明父類無法完成加載請求         }          if (c == null) {             // 如果父類無法加載,轉由子類加載             c = findClass(name);         }     }     if (resolve) {         resolveClass(c);     }     return c; }

        結合注釋相信大家很容易看懂。

        由雙親委派的代碼可知,同一個類加載器下,一個類只能被加載一次,也就限定了它只能被初始化一次。所以類中的 static代碼(靜態(tài)方法除外)只在類初始化時執(zhí)行一次

        4. <init>和<clinit>

        前面已經(jīng)介紹了編譯器自動生成的類構造器:<clinit>方法,它會收集static修飾的所有類變量的賦值動作和靜態(tài)語句塊(static{} 塊)并保留代碼的出現(xiàn)順序,它會在類初始化時執(zhí)行

        相應的,編譯器還會生成一個<init>方法,它會收集實例字段的賦值動作、初始化語句塊({}塊)和構造器(Constructor)中的代碼,并保留代碼的出現(xiàn)順序,它會在 new 指令之后接著執(zhí)行

        所以,當我們new 一個類時,如果JVM未加載該類,則先對其進行初始化,再進行實例化。

        至此,第三條優(yōu)先級規(guī)則也就呼之欲出了:

        • 靜態(tài)代碼(static{}塊、靜態(tài)字段賦值語句) > 初始化代碼({}塊、實例字段賦值語句)

        5. 規(guī)律實踐

        將前文的三條規(guī)則合并,總結出如下兩條:

        1.靜態(tài)代碼(static{}塊、靜態(tài)字段賦值語句) > 初始化代碼({}塊、實例字段賦值語句) > 構造函數(shù)代碼

        2.父類的static代碼 > 子類的static代碼

        根據(jù)前文總結,初始化代碼和構造函數(shù)代碼被編譯器收集到了<init>中,靜態(tài)代碼被收集到了<clinit>中,所以再次對上述規(guī)律做合并:

        父類<clinit> > 子類<clinit> > 父類 <init> > 子類 <init>

        對應到開篇的問題,我們來實踐一下:

        當執(zhí)行new Child()時,new關鍵字觸發(fā)了 Child 類的初始化 ,JVM 發(fā)現(xiàn)其有父類,則先初始化 Parent 類,開始執(zhí)行Parent類的<clinit>方法,然后執(zhí)行 Child 類的<clinit>方法(還記得<clinit>里面收集了什么嗎?)。

        然后開始實例化 一個Child類的對象,此時準備執(zhí)行 Child 的<init>方法,發(fā)現(xiàn)它有父類,優(yōu)先執(zhí)行父類的<init>方法,然后再執(zhí)行子類的<init>(還記得<init>里面收集了什么嗎?)。

        相信看到這里,各位心中已經(jīng)對開篇的問題有答案了,不妨先手寫一下輸出順序,然后寫代碼親自驗證一下。

        結束語

        平時開發(fā)中經(jīng)常用到static,每次寫的時候,心里總會打兩個問號,我為什么要用static?不用行不行?

        通過本文可以看出,static的應用遠遠不止類變量,靜態(tài)方法那么簡單。在經(jīng)典的單例模式中,你將看到static的各種用法,下一篇就寫如何花式編寫單例模式。

        贊(0)
        分享到: 更多 (0)
        網(wǎng)站地圖   滬ICP備18035694號-2    滬公網(wǎng)安備31011702889846號
        主站蜘蛛池模板: 亚洲精品无码专区2| 亚洲一区精品伊人久久伊人 | 老湿亚洲永久精品ww47香蕉图片| 久久精品黄AA片一区二区三区| 精品久久久久久无码人妻蜜桃| 久久99国产精品99久久| 国产欧美日韩精品a在线观看| 日批日出水久久亚洲精品tv| 欧美精品免费观看二区| 精品国产第一国产综合精品| 亚洲性日韩精品一区二区三区| 久久99精品久久久久久野外| 国产成人99久久亚洲综合精品| 久久精品成人免费看| 国产精品免费一区二区三区四区| 亚洲AV日韩精品久久久久| 色哟哟国产精品免费观看| 国产精品亚洲mnbav网站| 亚洲国产精品国自产拍电影 | 蜜芽亚洲av无码精品色午夜| 亚洲国产精品第一区二区三区| 国产午夜精品一区二区三区小说 | 94久久国产乱子伦精品免费| 8x福利精品第一导航| 西瓜精品国产自在现线| 久久伊人精品青青草原日本| 精品无码人妻久久久久久| 亚洲愉拍自拍欧美精品| 久久精品国产只有精品2020| 国产成人精品免费视频网页大全| 精品久久久久久国产潘金莲| 久久精品无码午夜福利理论片| 亚洲日韩中文在线精品第一 | 四虎国产精品永久地址99新强| 国产精品视频第一页| 国产成人久久精品激情| 日本一卡精品视频免费| 四虎成人精品永久免费AV| 无码精品国产VA在线观看DVD| 日韩精品无码一区二区三区| 久久亚洲美女精品国产精品|