最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java中類的初始化和實例化區(qū)別詳解

 更新時間:2023年08月14日 09:33:50   作者:Marvin-Fox  
這篇文章主要介紹了Java中類的初始化和實例化區(qū)別詳解,類的初始化<BR>是完成程序執(zhí)行前的準備工作,類的實例化(實例化對象)是指創(chuàng)建一個對象的過程,需要的朋友可以參考下

一、區(qū)別 

類的初始化:

是完成程序執(zhí)行前的準備工作。

在這個階段,靜態(tài)的(變量,方法,代碼塊)會被執(zhí)行。同時在會開辟一塊存儲空間用來存放靜態(tài)的數(shù)據(jù)。

初始化只在類加載的時候執(zhí)行一次

類的實例化(實例化對象):

是指創(chuàng)建一個對象的過程。這個過程中會在堆中開辟內(nèi)存,將一些非靜態(tài)的方法,變量存放在里面。在程序執(zhí)行的過程中,可以創(chuàng)建多個對象,既多次實例化。每次實例化都會開辟一塊新的內(nèi)存。(就是調(diào)用構(gòu)造函數(shù))

在每個類初始化使用前,都會先對該類進行加載(不是指類的實例化)

類加載有幾個步驟,加載->驗證->準備->解析->初始化

在編譯過程會把常量的值放入類的常量池中,在準備過程會對類變量(static修飾的變量)賦初始值,也就是零值,同時會將常量的值賦予常量;在初始化過程會按照類文件中的聲明順序執(zhí)行類變量的賦值和靜態(tài)語句塊(static{}塊),如果父類還沒有初始化會先進行父類的初始化,完成后才會進行子類的初始化。

可以看到在初始化階段就會執(zhí)行static{}塊的語句,而每一個類在運行過程中一般只會被加載一次(不是指實例化,特殊情況是使用其他類加載器對類進行加載),所以只會完成一次初始化過程,因此也就只會執(zhí)行static{}塊一次。 類的實例化和類的加載是兩個不同的概念。

1.主要區(qū)別

2.基礎(chǔ)知識

2.1 java類的生命周期:

指一個class文件從加載到卸載的全過程,類的完整生命周期包括7個部分:加載——驗證——準備——解析——初始化——使用——卸載,如下圖所示

其中,驗證——準備——解析  稱為連接階段,除了解析外,其他階段是順序發(fā)生的,而解析可以與這些階段交叉進行,因為Java支持動態(tài)綁定(晚期綁定),需要運行時才能確定具體類型;在使用階段實例化對象

2.2 類加載過程:

  • 加載:通過類名獲取類的二進制字節(jié)流是通過類加載器來完成的。其加載過程使用“雙親委派模型”
  • 驗證:當(dāng)一個類被加載之后,必須要驗證一下這個類是否合法,比如這個類是不是符合字節(jié)碼的格式、變量與方法是不是有重復(fù)、數(shù)據(jù)類型是不是有效、繼承與實現(xiàn)是否合乎標準等等??傊?,這個階段的目的就是保證加載的類是能夠被jvm所運行。
  • 準備:為類變量(靜態(tài)變量)在方法區(qū)分配內(nèi)存,并設(shè)置零值。注意:這里是類變量,不是實例變量,實例變量是對象分配到堆內(nèi)存時根據(jù)運行時動態(tài)生成的。
  • 解析:把常量池中的符號引用解析為直接引用:根據(jù)符號引用所作的描述,在內(nèi)存中找到符合描述的目標并把目標指針指針返回。
  • 初始化:類的初始化過程是這樣的:按照順序自上而下運行類中的變量賦值語句靜態(tài)語句如果有父類,則首先按照順序運行父類中的變量賦值語句和靜態(tài)語句。在類的初始化階段,只會初始化與類相關(guān)的靜態(tài)賦值語句和靜態(tài)語句,也就是有static關(guān)鍵字修飾的信息,而沒有static修飾的賦值語句和執(zhí)行語句在實例化對象的時候才會運行。執(zhí)行<clinit>()方法(clinit是class initialize的簡寫)
  • 實例化:在堆區(qū)分配內(nèi)存空間,執(zhí)行實例對象初始化,設(shè)置引用變量a指向剛分配的內(nèi)存地址

二、案例詳解

1、示例1

首先來看一個最簡單的例子(無父類且無靜態(tài)成員變量)

public class OrderOfInitialization1 {
    public static void main(String[] args) {
        House house = new House();
        house.f();
    }
}
class Window {
    Window(int market) {
        System.out.println("Window(" + market + ")");
    }
}
class House {
    Window w1 = new Window(1);// before constructor
    House() {
        // show we're in constructor
        System.out.println("House()");
        w3 = new Window(33);//reinitialize w3
    }
    Window w2 = new Window(2);// after constructor
    void f() {
        System.out.println("f()");
    }
    Window w3 = new Window(3);//at end
}

output:
    Window(1)
    Window(2)
    Window(3)
    House()
    Window(33)
    f()

從輸出結(jié)果分析,House 實例變量的初始化順序是:  w1  ,  w2  ,  w3  ,然后才是構(gòu)造函數(shù)。

即:實例變量按照其在代碼中出現(xiàn)的順序執(zhí)行初始化,然后執(zhí)行構(gòu)造函數(shù)里面的初始化。

2、示例2

接下來看一個稍微復(fù)雜一些的例子(有父類但無靜態(tài)成員變量)

public class OrderOfInitialization2 {
    public static void main(String[] args) {
        SubClass subClass = new SubClass();
    }
}
class Print {
    Print(int i) {
        System.out.println("new Print(" + i + ")");
    }
}
class SuperClass {
    Print print1 = new Print(1);
    public SuperClass() {
        System.out.println("new SuperClass()");
    }
    Print print2 = new Print(2);
}
class SubClass extends SuperClass {
    Print print3 = new Print(3);
    public SubClass() {
        //這個地方其實是調(diào)用了父類的默認的無參構(gòu)造函數(shù),super();
        //如果父類沒有無參構(gòu)造函數(shù),則這個地方必須顯式的調(diào)用父類的構(gòu)造函數(shù),否則編譯不通過
        System.out.println("new SubClass()");
    }
    Print print4 = new Print(4);
}

output:
    new Print(1)
    new Print(2)
    new SuperClass()
    new Print(3)
    new Print(4)
    new SubClass()

從輸出結(jié)果分析:這個地方是先調(diào)用了父類 SuperClass 的構(gòu)造函數(shù),然后調(diào)用子類 SubClass 的構(gòu)造函數(shù)。

即:如果一個類有父類,在實例化子類的時候,會先執(zhí)行父類的構(gòu)造函數(shù),然后執(zhí)行子類的構(gòu)造函數(shù)。

3、示例3

繼續(xù)看一個更復(fù)雜一些的例子(有父類且有靜態(tài)成員變量)

public class OrderOfInitialization3 {
    public static void main(String[] args) {
        Man man = new Man();
        Man man1 = new Man();
    }
    static Print1 print0 = new Print1(0);
}
class Print1 {
    Print1(int i) {
        System.out.println("new Print1(" + i + ")");
    }
}
class People {
    Print1 print1 = new Print1(1);
    public People() {
        System.out.println("new People()");
    }
    Print1 print2 = new Print1(2);
    static Print1 print5 = new Print1(5);
}
class Man extends People {
    Print1 print3 = new Print1(3);
    public Man() {
        System.out.println("new Man()");
    }
    Print1 print4 = new Print1(4);
    static Print1 print6 = new Print1(6);
}

output:
       new Print(0)
       new Print(5)
       new Print(6)
       new Print(1)
       new Print(2)
       new People()
       new Print(3)
       new Print(4)
       new Man()
       new Print(1)
       new Print(2)
       new People()
       new Print(3)
       new Print(4)
       new Man()

從輸出結(jié)果分析:這里首先執(zhí)行了 OrderOfInitialization3 類的靜態(tài)變量 print0 的初始化(輸出 new Print(0)),然后執(zhí)行靜態(tài)方法 main;

緊接著是執(zhí)行 People 類的靜態(tài)成員變量 print5 的初始化(輸出 new Print(5)),再接著是 Man 類的靜態(tài)成員變量 print6 的初始化(輸出 new Print(6));

之后是 People 的實例變量(輸出new Print(1)、new Print(2))、構(gòu)造函數(shù)(輸出 new People())初始化

最后才是 Man 實例變量(輸出 new Print(3)、new Print(4))、構(gòu)造函數(shù)(輸出 new Man())的初始化。

在第二次實例化一個 Man 的時候,所有的靜態(tài)成員變量都沒有相應(yīng)的輸出,即靜態(tài)成員變量只初始化了一次。

所以這個地方執(zhí)行的順序是:首先執(zhí)行 main 所在類的靜態(tài)成員變量的初始化,然后是 Man 的父類的靜態(tài)成員變量的初始化,然后是子類的靜態(tài)成員的初始化;

接著是父類的構(gòu)造函數(shù),最后才是子類的構(gòu)造函數(shù)。

這個地方 Man 實例化了兩次,但是其父類和本身的靜態(tài)成員變量只初始化了一次。

為什么靜態(tài)成員變量只會初始化一次呢?

  • 實際上,靜態(tài)成員變量初始化的過程本質(zhì)上就是一個類的加載和初始化的過程,虛擬機保證了在同一個類加載器下,一個類型只會初始化一次。

4、總結(jié)一下

這個地方把類和對象分開會更好理解一點。

(1)類的初始化

  • 靜態(tài)成員變量初始化發(fā)生在靜態(tài)方法之前
  • 父類的初始化必須在子類初始化之前
  • 靜態(tài)成員變量的初始化順序為其在代碼中出現(xiàn)的順序

(2)實例化對象

  • 如果有父類,先執(zhí)行父類的實例化
  • 成員變量初始化發(fā)生在構(gòu)造函數(shù)之前
  • 成員變量的初始化順序為其在代碼中出現(xiàn)的順序

(3)實例化對象之前如果該類沒有初始化,必須先執(zhí)行該類的初始化。

5、最后看一個比較特殊的例子

public class NestedInitialization {
    public static void main(String[] args) {
        staticFunction();
    }
    static NestedInitialization st = new NestedInitialization();
    static {
        System.out.println("1");
    }
    {
        System.out.println("2");
    }
    NestedInitialization() {
        System.out.println("3");
        System.out.println("a=" + a + ",b=" + b);
    }
    public static void staticFunction() {
        System.out.println("4");
    }
    int a = 110;
    static int b = 112;
}

output:
    2
    3
    a=110,b=0
    1
    4

這個例子的特殊性在于,**該類還沒有完成初始化,就去實例化一個該類的對象。**我們從類的生命周期來分析,一個類會經(jīng)歷加載、驗證、準備、解析、初始化、使用和卸載七個階段,在執(zhí)行到  static NestedInitialization st = new NestedInitialization();  這一步時,已經(jīng)是類的初始化階段了,此時,NestedInitialization 類里面的靜態(tài)成員的值都還是準備階段設(shè)置的初始零值,即  static NestedInitialization st = null , static int b = 0; ,然后這個地方需要實例化 NestedInitialization,所以是以此時的狀態(tài)去實例化 NestedInitialization 類的,執(zhí)行類的實例化,然后在繼續(xù)類的初始化。所以才會出現(xiàn)上面的輸出結(jié)果。

到此這篇關(guān)于Java中類的初始化和實例化區(qū)別詳解的文章就介紹到這了,更多相關(guān)Java初始化和實例化內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java中使用Files類的copy()方法實現(xiàn)復(fù)制文件

    Java中使用Files類的copy()方法實現(xiàn)復(fù)制文件

    這篇文章主要介紹了Java中使用Files類的copy()方法實現(xiàn)復(fù)制文件,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2025-05-05
  • 關(guān)于@ConfigurationProperties注解全解析

    關(guān)于@ConfigurationProperties注解全解析

    通過@ConfigurationProperties和@ConditionalOnProperty注解,可以實現(xiàn)基于配置的條件加載Bean,以此優(yōu)化Spring Boot應(yīng)用的啟動速度,在application.yml中設(shè)置配置項,如是否加載特定的Bean(以swagger配置為例)
    2024-11-11
  • springboot @Value實現(xiàn)獲取計算機中絕對路徑文件的內(nèi)容

    springboot @Value實現(xiàn)獲取計算機中絕對路徑文件的內(nèi)容

    這篇文章主要介紹了springboot @Value實現(xiàn)獲取計算機中絕對路徑文件的內(nèi)容,具有很好的參考價值,希望對大家有所幫助。
    2021-09-09
  • Spring配置動態(tài)數(shù)據(jù)源實現(xiàn)讀寫分離的方法

    Spring配置動態(tài)數(shù)據(jù)源實現(xiàn)讀寫分離的方法

    這篇文章主要介紹了利用Spring配置動態(tài)數(shù)據(jù)源實現(xiàn)讀寫分離的方法,文中通過示例代碼介紹的很詳細,相信對大家的理解和學(xué)習(xí)具有一定的參考借鑒價值,藕需要的朋友可以一起學(xué)習(xí)學(xué)習(xí)。
    2017-01-01
  • SpringBoot實現(xiàn)整合微信支付方法詳解

    SpringBoot實現(xiàn)整合微信支付方法詳解

    這篇文章主要介紹了SpringBoot實現(xiàn)整合微信支付的過程詳解,文中的示例代碼對我們的工作或?qū)W習(xí)有一定的幫助,感興趣的小伙伴可以跟隨小編學(xué)習(xí)一下
    2021-12-12
  • java null轉(zhuǎn)換為字符串的三種方法

    java null轉(zhuǎn)換為字符串的三種方法

    在Java開發(fā)中,正確處理null值至關(guān)重要,以避免空指針異常,本文介紹了三種常見的null值轉(zhuǎn)字符串方法:三元運算符、Objects.toString方法、String.valueOf方法,感興趣的可以了解一下
    2024-10-10
  • Spring中@Validated和@Valid區(qū)別淺析

    Spring中@Validated和@Valid區(qū)別淺析

    @Valid是javax.validation里的,?@Validated是@Valid?的一次封裝,是Spring提供的校驗機制使用,下面這篇文章主要給大家介紹了關(guān)于Spring中@Validated和@Valid區(qū)別的相關(guān)資料,需要的朋友可以參考下
    2022-04-04
  • Java日志框架打印應(yīng)用程序日志代碼的執(zhí)行情況分析

    Java日志框架打印應(yīng)用程序日志代碼的執(zhí)行情況分析

    在配置INFO日志級別時,日志器(logger)中debug級的日志代碼仍會被執(zhí)行,只是是否輸出取決于配置的日志級別,本文基于Java 1.8、SLF4J 1.7.25和Log4j 2.20.0進行實驗,詳述了日志框架處理日志代碼的機制,感興趣的朋友一起看看吧
    2024-10-10
  • SpringSecurity認證授權(quán)全流程解讀

    SpringSecurity認證授權(quán)全流程解讀

    這篇文章主要介紹了SpringSecurity認證授權(quán)全流程,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2025-06-06
  • SpringBoot中的CSRF攻擊及預(yù)防方法

    SpringBoot中的CSRF攻擊及預(yù)防方法

    CSRF攻擊是一種常見的網(wǎng)絡(luò)攻擊方式,可以通過欺騙用戶來執(zhí)行惡意操作,在Spring Boot應(yīng)用程序中,我們可以采取多種措施來預(yù)防CSRF攻擊,本文將給大家介紹一下CSRF攻擊以及如何預(yù)防攻擊,需要的朋友可以參考下
    2023-07-07

最新評論

上思县| 嘉峪关市| 桂平市| 宾川县| 和平县| 德安县| 芒康县| 奉新县| 石棉县| 伊川县| 广丰县| 子长县| 麻江县| 云和县| 永嘉县| 上犹县| 青川县| 西乌珠穆沁旗| 东乌| 青神县| 白城市| 金华市| 永福县| 辽源市| 莲花县| 鄂伦春自治旗| 遵义市| 古丈县| 拉萨市| 德昌县| 武山县| 常熟市| 军事| 汾西县| 比如县| 德格县| 申扎县| 监利县| 六枝特区| 乐平市| 游戏|