<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>

          從 static 關(guān)鍵字深入理解 java對(duì)象初始化順序

          共 3770字,需瀏覽 8分鐘

           ·

          2020-11-24 14:07

          點(diǎn)擊上方藍(lán)色?猿芯”?關(guān)注,輸入1024,你懂的

          前言

          最近在閱讀 ThreadLocal 源碼的時(shí)候,發(fā)現(xiàn)一段很有意思的代碼,代碼片段如下:

          private?final?int?threadLocalHashCode?=?nextHashCode();?
          private?static?AtomicInteger?nextHashCode?=?new?AtomicInteger();
          private?static?final?int?HASH_INCREMENT?=?0x61c88647;

          private?static?int?nextHashCode()?{
          ????return?nextHashCode.getAndAdd(HASH_INCREMENT);
          }

          以上代碼片段主要是 ThradLocal 生成哈希值(threadLocalHashCode)的邏輯,通過靜態(tài)的原子整型變量 nextHashCode 以及靜態(tài)方法 nextHashCode (),為每個(gè)線程持有的 ThreadLocal 本地變量生成唯一 的 hashCode

          注:ThreadLocalhashCode 選擇 HASH_INCREMENT 變量值:0x61c88647 很有意思,里面涉及到斐波那契數(shù)列黃金分割法,感興趣的同學(xué)可以自行了解下。

          當(dāng)然本文的重點(diǎn)不是 ThreadLocal 原理分析上,而是分析 static 關(guān)鍵字修飾的靜態(tài)域(靜態(tài)變量、靜態(tài)塊)順序加載問題。

          這段代碼總共四行,除了第一行都是用 static 關(guān)鍵字修飾的,這里我們?cè)O(shè)想一個(gè)問題,當(dāng)類初始化的時(shí)候,這四行代碼是從上往下執(zhí)行的嗎?

          答案是:”否“。

          靜態(tài)變量

          為了方便 debug 調(diào)試,我們把上面的代碼稍微做了下調(diào)整,代碼片段如下:

          public?class?Static01?{
          ????private?final?int?threadLocalHashCode?=?nextHashCode();

          ????private?static?AtomicInteger?nextHashCode?=?new?AtomicInteger();

          ????private?static?final?int?HASH_INCREMENT?=?getIncr();

          ????public?Static01(){
          ????????System.out.println("threadLocalHashCode::"?+?threadLocalHashCode);
          ????}

          ????private?static?int?getIncr()?{
          ????????return?0x61c88647;
          ????}

          ????private?static?int?nextHashCode()?{
          ????????return?nextHashCode.getAndAdd(HASH_INCREMENT);
          ????}

          ????public?static?void?main(String[]?args)?{
          ????????new?Static01();
          ????}

          }

          上面的代碼片段用 debug 模式啟動(dòng),通過為每行代碼打斷點(diǎn),發(fā)現(xiàn)當(dāng)真正實(shí)例化 Static01 類時(shí),代碼運(yùn)行順序并非是按照逐行執(zhí)行,而是如下圖紅色標(biāo)記順序進(jìn)行的。

          其執(zhí)行流程是:

          • 第一步、用 new 關(guān)鍵字初始化 Static01 類的構(gòu)造方法
          • 第二步、初始化靜態(tài)變量 nextHashCode
          • 第三步、初始化靜態(tài)變量 HASH_INCREMENT
          • 第四步、初始化成員變量 threadLocalHashCode
          • 最后 、在 Static01 構(gòu)造方法打印 threadLocalHashCode 變量的 hash

          對(duì)象實(shí)例化

          就是執(zhí)行類中構(gòu)造函數(shù)的內(nèi)容,如果該類存在父類,會(huì)通過顯示或者隱示的方式(super方法)先執(zhí)行父類的構(gòu)造函數(shù),在堆內(nèi)存中為父類的實(shí)例變量開辟空間,并賦予默認(rèn)的初始值,然后在根據(jù)構(gòu)造函數(shù)的代碼內(nèi)容將真正的值賦予實(shí)例變量本身,然后,引用變量獲取對(duì)象的首地址,通過操作對(duì)象來調(diào)用實(shí)例變量和方法

          從上面代碼執(zhí)行流程可以看出

          • 在對(duì)象實(shí)例化之前必須先初始化 static 修飾的靜態(tài)變量,并且靜態(tài)變量也是有加載順序的;
          • 類的成員變量的初始化在構(gòu)造方法里面進(jìn)行,加載順序優(yōu)先于構(gòu)造方法體的執(zhí)行語句。
          • 如果某類繼承了父類,那么必須先初始化父類的構(gòu)造方法以及成員變量以及構(gòu)造方法的執(zhí)行語句,然后才是子類的成員變量以及構(gòu)造方法的執(zhí)行語句。
          public?Static01()?{
          ????super();
          ????System.out.println("threadLocalHashCode::"?+?threadLocalHashCode);
          }

          另外,靜態(tài)語句塊中只能訪問到定義在靜態(tài)塊之前的變量,在靜態(tài)塊里可以給該變量賦值,但是不能訪問,否則編譯器會(huì)提示 “Illegal forward reference” 錯(cuò)誤,如下圖

          靜態(tài)塊

          靜態(tài)塊主要用于類的初始化,不是指對(duì)象的實(shí)例化。它只會(huì)執(zhí)行一次,靜態(tài)塊只能訪問類的靜態(tài)成員屬性和方法,不能在靜態(tài)塊使用 this

          我們先把上面的代碼稍加改造下,增加 “靜態(tài)塊1”和“靜態(tài)塊2” 靜態(tài)塊代碼

          private?final?int?threadLocalHashCode?=?nextHashCode();
          private?static?AtomicInteger?nextHashCode?=?new?AtomicInteger();

          static{
          ????System.out.println("靜態(tài)塊1");
          }

          private?static?final?int?HASH_INCREMENT?=?getIncr();

          static{
          ????System.out.println("靜態(tài)塊2");
          }

          運(yùn)行結(jié)果如下:

          發(fā)現(xiàn)不管是靜態(tài)塊還是靜態(tài)變量,它們之間都是按順序執(zhí)行的。那為什么是靜態(tài)塊、靜態(tài)變量的初始化是有順序的呢?

          通過查看 Static01 類的 class 編譯文件,發(fā)現(xiàn)編譯器會(huì)把 static 塊的代碼放在同一 static 花括號(hào){}內(nèi)。

          代碼順序是按照之前編碼的順序整合,這么看來是編譯器在作怪吧。

          static?{
          ????System.out.println("靜態(tài)塊1");
          ????HASH_INCREMENT?=?getIncr();
          ????System.out.println("靜態(tài)塊2");
          }

          類加載中,靜態(tài)域的加載時(shí)機(jī)

          從《深入理解Java虛擬機(jī):JVM高級(jí)特性與最佳實(shí)踐(第2版)》這本書講的類加載機(jī)制原理可知:

          當(dāng)遇到newgetstaticputstaticinvokestatic4條字節(jié)碼指令時(shí),如果類沒有進(jìn)行過初始化,則需要先觸發(fā)其初始化。

          這就解釋了為什么在對(duì)象未實(shí)例化前,可以通過 “類名.靜態(tài)屬性變量、類名.靜態(tài)方法” 的方式訪問靜態(tài)變量和靜態(tài)方法了。

          類加載的時(shí)機(jī)

          對(duì)于初始化階段,虛擬機(jī)規(guī)范規(guī)定了有且只有 5 種情況必須立即對(duì)類進(jìn)行“初始化”(而加載、驗(yàn)證、準(zhǔn)備自然需要在此之前開始):

          1. 遇到new、getstatic 和 putstatic 或 invokestatic 這4條字節(jié)碼指令時(shí),如果類沒有進(jìn)行過初始化,則需要先觸發(fā)其初始化。對(duì)應(yīng)場(chǎng)景是:使用 new 實(shí)例化對(duì)象、讀取或設(shè)置一個(gè)類的靜態(tài)字段(被 final 修飾、已在編譯期把結(jié)果放入常量池的靜態(tài)字段除外)、以及調(diào)用一個(gè)類的靜態(tài)方法。
          2. 對(duì)類進(jìn)行反射調(diào)用的時(shí)候,如果類沒有進(jìn)行過初始化,則需要先觸發(fā)其初始化。
          3. 當(dāng)初始化類的父類還沒有進(jìn)行過初始化,則需要先觸發(fā)其父類的初始化。(而一個(gè)接口在初始化時(shí),并不要求其父接口全部都完成了初始化)
          4. 虛擬機(jī)啟動(dòng)時(shí),用戶需要指定一個(gè)要執(zhí)行的主類(包含 main() 方法的那個(gè)類),虛擬機(jī)會(huì)先初始化這個(gè)主類。
          5. 當(dāng)使用 JDK 1.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ā)其初始化。

          總結(jié)

          1、靜態(tài)域(靜態(tài)變量、靜態(tài)塊)是按逐行順序加載的,并且靜態(tài)域只會(huì)加載一次。

          2、當(dāng)實(shí)例化對(duì)象之前(構(gòu)造方法調(diào)用),會(huì)先去初始化靜態(tài)域,再去調(diào)用構(gòu)造函數(shù)實(shí)例化對(duì)象。

          3、一般對(duì)象初始化順序如下:父類的靜態(tài)域順序加載–>子類靜態(tài)域順序加載–>父類非靜態(tài)域初始化->父類構(gòu)造函數(shù)初始化–>子類非靜態(tài)域初始化->子類構(gòu)造函數(shù)初始化。

          參考

          • https://blog.csdn.net/qq_36522306/article/details/80584595
          • https://www.cnblogs.com/cxiang/p/10082160.html
          瀏覽 57
          點(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>
                  欧美成人电影在线 | www.操逼.com | 婷婷在线干在线色 | 黄色一级片在线看 | 国产少妇美女下海在线黄 |