亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

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

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

一、區(qū)別 

類的初始化:

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

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

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

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

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

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

類加載有幾個(gè)步驟,加載->驗(yàn)證->準(zhǔn)備->解析->初始化

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

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

1.主要區(qū)別

2.基礎(chǔ)知識

2.1 java類的生命周期:

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

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

2.2 類加載過程:

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

二、案例詳解

1、示例1

首先來看一個(gè)最簡單的例子(無父類且無靜態(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 實(shí)例變量的初始化順序是:  w1  ,  w2  ,  w3  ,然后才是構(gòu)造函數(shù)。

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

2、示例2

接下來看一個(gè)稍微復(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() {
        //這個(gè)地方其實(shí)是調(diào)用了父類的默認(rèn)的無參構(gòu)造函數(shù),super();
        //如果父類沒有無參構(gòu)造函數(shù),則這個(gè)地方必須顯式的調(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é)果分析:這個(gè)地方是先調(diào)用了父類 SuperClass 的構(gòu)造函數(shù),然后調(diào)用子類 SubClass 的構(gòu)造函數(shù)。

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

3、示例3

繼續(xù)看一個(gè)更復(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 的實(shí)例變量(輸出new Print(1)、new Print(2))、構(gòu)造函數(shù)(輸出 new People())初始化

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

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

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

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

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

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

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

4、總結(jié)一下

這個(gè)地方把類和對象分開會更好理解一點(diǎn)。

(1)類的初始化

  • 靜態(tài)成員變量初始化發(fā)生在靜態(tài)方法之前
  • 父類的初始化必須在子類初始化之前
  • 靜態(tài)成員變量的初始化順序?yàn)槠湓诖a中出現(xiàn)的順序

(2)實(shí)例化對象

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

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

5、最后看一個(gè)比較特殊的例子

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

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

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

相關(guān)文章

  • Java在Excel中創(chuàng)建多級分組、折疊或展開分組的實(shí)現(xiàn)

    Java在Excel中創(chuàng)建多級分組、折疊或展開分組的實(shí)現(xiàn)

    這篇文章主要介紹了Java在Excel中創(chuàng)建多級分組、折疊或展開分組的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-05-05
  • 詳解Spring中接口的bean是如何注入的

    詳解Spring中接口的bean是如何注入的

    這篇文章主要介紹了詳解Spring中接口的bean是如何注入的的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用Spring具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-06-06
  • java實(shí)現(xiàn)讀取txt文件中的內(nèi)容

    java實(shí)現(xiàn)讀取txt文件中的內(nèi)容

    本文通過一個(gè)具體的例子向大家展示了如何使用java實(shí)現(xiàn)讀取TXT文件里的內(nèi)容的方法以及思路,有需要的小伙伴可以參考下
    2016-03-03
  • Jenkins之Log Parse使用方法詳解

    Jenkins之Log Parse使用方法詳解

    這篇文章主要為大家詳細(xì)介紹了Jenkins插件Log Parse使用方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-08-08
  • java常量字符串過長報(bào)錯(cuò)的解決辦法以及原因分析

    java常量字符串過長報(bào)錯(cuò)的解決辦法以及原因分析

    在IDEA中字符串長度超過65535,進(jìn)行打印,IDEA會提示java:常量字符串過長,這篇文章主要給大家介紹了關(guān)于java常量字符串過長報(bào)錯(cuò)的解決辦法以及原因分析,需要的朋友可以參考下
    2023-01-01
  • java普通類如何轉(zhuǎn)javafx程序

    java普通類如何轉(zhuǎn)javafx程序

    這篇文章主要介紹了java普通類如何轉(zhuǎn)javafx程序方式,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • Java編程實(shí)現(xiàn)軌跡壓縮算法開放窗口實(shí)例代碼

    Java編程實(shí)現(xiàn)軌跡壓縮算法開放窗口實(shí)例代碼

    這篇文章主要介紹了Java編程實(shí)現(xiàn)軌跡壓縮算法開放窗口實(shí)例代碼,具有一定借鑒價(jià)值,需要的朋友可以參考下。
    2017-11-11
  • SpringBoot整合Spring Batch示例代碼

    SpringBoot整合Spring Batch示例代碼

    這篇文章主要來和大家一起探討一下SpringBoot如何整合Spring Batch,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2023-10-10
  • 基于指針pointers和引用references的區(qū)別分析

    基于指針pointers和引用references的區(qū)別分析

    本篇文章介紹了,基于指針pointers和引用references的區(qū)別分析。需要的朋友參考下
    2013-05-05
  • IDEA2020.1構(gòu)建Spring5.2.x源碼的方法

    IDEA2020.1構(gòu)建Spring5.2.x源碼的方法

    這篇文章主要介紹了IDEA2020.1構(gòu)建Spring5.2.x源碼的方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-10-10

最新評論