真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

java中static關(guān)鍵字如何使用

java中static關(guān)鍵字如何使用,相信很多沒有經(jīng)驗的人對此束手無策,為此本文總結(jié)了問題出現(xiàn)的原因和解決方法,通過這篇文章希望你能解決這個問題。

成都創(chuàng)新互聯(lián)長期為上千客戶提供的網(wǎng)站建設(shè)服務(wù),團(tuán)隊從業(yè)經(jīng)驗10年,關(guān)注不同地域、不同群體,并針對不同對象提供差異化的產(chǎn)品和服務(wù);打造開放共贏平臺,與合作伙伴共同營造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為達(dá)拉特企業(yè)提供專業(yè)的做網(wǎng)站、成都網(wǎng)站制作,達(dá)拉特網(wǎng)站改版等技術(shù)服務(wù)。擁有10年豐富建站經(jīng)驗和眾多成功案例,為您定制開發(fā)。

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");
    }
}

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

相信有不少同學(xué)遇到過這類問題,可能查過資料之后接著就忘了,再次遇到還是答不對。接下來課代表通過4個步驟,帶大家拆解一下這段代碼的執(zhí)行順序,并借此總結(jié)規(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");
    }
}

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

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

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

2.static 有啥作用?

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

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

  • 使用 new 關(guān)鍵字實例化對象的時候

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

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

  • 使用反射調(diào)用類的時候

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

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

  • 當(dāng)初次調(diào)用 MethodHandle 實例時,初始化該 MethodHandle 指向的方法所在的類。

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

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

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

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

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

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

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

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

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

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

答案是:雙親委派模型。

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

應(yīng)用程序類加載器 → 擴(kuò)展類加載器 → 啟動類加載器

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

該過程的實現(xiàn)也很簡單,下面展示關(guā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) {
            // 如果父類無法加載,轉(zhuǎn)由子類加載
            c = findClass(name);
        }
    }
    if (resolve) {
        resolveClass(c);
    }
    return c;
}

結(jié)合注釋相信大家很容易看懂。

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

4.

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

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

所以,當(dāng)我們new 一個類時,如果JVM未加載該類,則先對其進(jìn)行初始化,再進(jìn)行實例化。

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

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

5. 規(guī)律實踐

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

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

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

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

父類 > 子類 > 父類 > 子類

對應(yīng)到開篇的問題,我們來實踐一下:

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

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

看完上述內(nèi)容,你們掌握java中static關(guān)鍵字如何使用的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝各位的閱讀!


名稱欄目:java中static關(guān)鍵字如何使用
分享網(wǎng)址:http://weahome.cn/article/gcccsh.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部