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

Java類(lèi)初始化和實(shí)例化中的2個(gè)“雷區(qū)”

 更新時(shí)間:2016年02月14日 17:06:24   作者:Zerohuan  
這篇文章主要介紹了Java類(lèi)初始化和實(shí)例化中的2個(gè)“雷區(qū)”,大家要注意,感興趣的小伙伴們可以參考一下

在考慮類(lèi)初始化時(shí),我們都知道進(jìn)行子類(lèi)初始化時(shí),如果父類(lèi)沒(méi)有初始化要先初始化子類(lèi)。然而事情并沒(méi)有一句話(huà)這么簡(jiǎn)單。
首先看看Java中初始化觸發(fā)的條件:

(1)在使用new實(shí)例化對(duì)象,訪(fǎng)問(wèn)靜態(tài)數(shù)據(jù)和方法時(shí),也就是遇到指令:new,getstatic/putstatic和invokestatic時(shí);
(2)使用反射對(duì)類(lèi)進(jìn)行調(diào)用時(shí);
(3)當(dāng)初始化一個(gè)類(lèi)時(shí),父類(lèi)如果沒(méi)有進(jìn)行初始化,先觸發(fā)父類(lèi)的初始化;
(4)執(zhí)行入口main方法所在的類(lèi);
(5)JDK1.7動(dòng)態(tài)語(yǔ)言支持中方法句柄所在的類(lèi),如果沒(méi)有初始化觸發(fā)起初始化;

經(jīng)過(guò)編譯后生成一個(gè)<clinit>方法,類(lèi)的初始化就在這個(gè)方法中進(jìn)行,該方法只執(zhí)行,由JVM保證這一點(diǎn),并進(jìn)行同步控制;
其中條件(3),從方法調(diào)用的角度來(lái)看,是子類(lèi)的<clinit>會(huì)在開(kāi)始時(shí)遞歸的調(diào)用父類(lèi)的<clinit>,這類(lèi)似與我們?cè)谧宇?lèi)構(gòu)造器中必須首先調(diào)用父類(lèi)的構(gòu)造器;
但需要注意的是“觸發(fā)”并不是完成初始化,這意味著有可能子類(lèi)的初始化會(huì)提前于父類(lèi)初始化結(jié)束,這就是“危險(xiǎn)”的所在。

1. 一個(gè)類(lèi)初始化的例子:
這個(gè)例子我使用一個(gè)外圍類(lèi)包含2個(gè)有繼承關(guān)系的靜態(tài)成員類(lèi),因?yàn)橥鈬?lèi)的初始化和靜態(tài)成員類(lèi)沒(méi)有因果關(guān)系,因此這樣展示是安全和方便的;
父類(lèi)A和子類(lèi)B分別包含main函數(shù),由上面的觸發(fā)條件(4)可知,通過(guò)分別調(diào)用這個(gè)兩個(gè)main函數(shù)來(lái)觸發(fā)不同的類(lèi)初始化路徑;
這個(gè)例子的問(wèn)題在于父類(lèi)包含子類(lèi)的static引用并在定義處進(jìn)行初始化的問(wèn)題:

public class WrapperClass { 
  private static class A { 
    static { 
      System.out.println("類(lèi)A初始化開(kāi)始..."); 
    } 
    //父類(lèi)包含子類(lèi)的static引用 
    private static B b = new B(); 
    protected static int aInt = 9; 
 
    static { 
      System.out.println("類(lèi)A初始化結(jié)束..."); 
    } 
 
    public static void main(String[] args) { 
 
    } 
  } 
 
  private static class B extends A { 
    static { 
      System.out.println("類(lèi)B初始化開(kāi)始..."); 
    } 
    //子類(lèi)的域依賴(lài)于父類(lèi)的域 
    private static int bInt = 9 + A.aInt; 
 
    public B() { 
      //構(gòu)造器依賴(lài)類(lèi)的static域 
      System.out.println("類(lèi)B的構(gòu)造器調(diào)用 " + "bInt的值" + bInt); 
    } 
 
    static { 
      System.out.println("類(lèi)B初始化結(jié)束... " + "aInt的值:" + bInt); 
    } 
 
    public static void main(String[] args) { 
 
    } 
  } 
} 

情景一:入口為類(lèi)B的main函數(shù)時(shí)輸出結(jié)果:

/** 
   * 類(lèi)A初始化開(kāi)始... 
   * 類(lèi)B的構(gòu)造器調(diào)用 bInt的值0 
   * 類(lèi)A初始化結(jié)束... 
   * 類(lèi)B初始化開(kāi)始... 
   * 類(lèi)B初始化結(jié)束... aInt的值:18 
   */ 

分析:可以看到,main函數(shù)的調(diào)用觸發(fā)了類(lèi)B的初始化,進(jìn)入類(lèi)B的<clinit>方法,類(lèi)A作為其父類(lèi)先開(kāi)始初始化進(jìn)入了A的<clinit>方法,其中有一個(gè)語(yǔ)句new B();這時(shí)會(huì)進(jìn)行B的實(shí)例化,這是已經(jīng)在類(lèi)B的<clinit>中了,main線(xiàn)程已經(jīng)獲得鎖開(kāi)始執(zhí)行類(lèi)B的<clinit>,我們開(kāi)頭說(shuō)過(guò)JVM會(huì)保證一個(gè)類(lèi)的初始化方法只被執(zhí)行一次,JVM收到new指令后不會(huì)再進(jìn)入類(lèi)B的<clinit>方法而是直接進(jìn)行實(shí)例化,但是此時(shí)類(lèi)B還沒(méi)有完成類(lèi)初始化,所以可以看到bInt的值為0(這個(gè)0是類(lèi)加載中準(zhǔn)備階段分配方法區(qū)內(nèi)存后進(jìn)行的置零初始化);
因此,可以得出,再父類(lèi)中包含子類(lèi)類(lèi)型的static域并進(jìn)行賦值動(dòng)作,會(huì)可能導(dǎo)致子類(lèi)實(shí)例化在類(lèi)初始化完成前進(jìn)行;

情景二:入口為類(lèi)A的main函數(shù)時(shí)輸出結(jié)果:

/** 
   * 類(lèi)A初始化開(kāi)始... 
   * 類(lèi)B初始化開(kāi)始... 
   * 類(lèi)B初始化結(jié)束... aInt的值:9 
   * 類(lèi)B的構(gòu)造器調(diào)用 bInt的值9 
   * 類(lèi)A初始化結(jié)束... 
   */ 

分析:經(jīng)過(guò)情景一的分析,我們知道,由類(lèi)B的初始化觸發(fā)類(lèi)A的初始化,會(huì)導(dǎo)致類(lèi)A中類(lèi)變量b的實(shí)例化在類(lèi)B初始化完成前進(jìn)行,那如果先初始化類(lèi)A是不是就可以在類(lèi)變量實(shí)例化的時(shí)候先觸發(fā)類(lèi)B的初始化,從而使得初始化在實(shí)例化前呢?答案是肯定的,但是這仍然有問(wèn)題。
根據(jù)輸出,可以看到,類(lèi)B的初始化在類(lèi)A的初始化完成前進(jìn)行了,這導(dǎo)致了像類(lèi)變量aInt的變量在類(lèi)B初始化完成后才進(jìn)行初始化,所以類(lèi)B中的域bInt獲取到的aInt的值是“0”,而不是我們預(yù)期的“18”;

結(jié)論:綜上,可以得出,在父類(lèi)中包含子類(lèi)類(lèi)型的類(lèi)變量,并在定義出進(jìn)行實(shí)例化是非常危險(xiǎn)的行為,具體情況可能不會(huì)向例子一樣直白,調(diào)用方法在定義處賦值一樣隱含著危險(xiǎn),即使要包含子類(lèi)類(lèi)型的static域,也應(yīng)該通過(guò)static方法進(jìn)行賦值,因?yàn)镴VM可以保證在static方法調(diào)用前完成所有的初始化動(dòng)作(當(dāng)然這種保證也是你不應(yīng)該包含static B b = new B();這樣的初始化行為);

2. 一個(gè)實(shí)例化的例子:
首先需要知道對(duì)象創(chuàng)建的過(guò)程:
(1)遇到new指令,檢查類(lèi)是否完成了加載,驗(yàn)證,準(zhǔn)備,解析,初始化(解析過(guò)程就是符號(hào)引用解析成直接引用,比如方法名就是一個(gè)符號(hào)引用,可以在初始化完成后使用這個(gè)符號(hào)引用的時(shí)候進(jìn)行,正是為了支持動(dòng)態(tài)綁定),沒(méi)有完成先進(jìn)行這些過(guò)程;
(2)分配內(nèi)存,采用空閑列表或者指針碰撞的方法,并將新分配的內(nèi)存“置零”,因此所有的實(shí)例變量在此環(huán)節(jié)都進(jìn)行了一次默認(rèn)初始化為0(引用為null)的過(guò)程;
(3)執(zhí)行<init>方法,包括檢查調(diào)用父類(lèi)的<init>方法(構(gòu)造器),實(shí)例變量定義出的賦值動(dòng)作,實(shí)例化器順序執(zhí)行,最后調(diào)用構(gòu)造器中的動(dòng)作。

這個(gè)例子可能更為大家所熟知,也就是它違反了“不要在構(gòu)造器,clone方法和readObject方法中調(diào)用可被覆蓋的方法”。其原因就在于Java中的多態(tài),也就是動(dòng)態(tài)綁定。
父類(lèi)A的構(gòu)造器中包含一個(gè)protected方法,類(lèi)B是其子類(lèi)。

public class WrongInstantiation { 
  private static class A { 
    public A() { 
      doSomething(); 
    } 
 
    protected void doSomething() { 
      System.out.println("A's doSomething"); 
    } 
  } 
 
  private static class B extends A { 
    private int bInt = 9; 
 
    @Override 
    protected void doSomething() { 
      System.out.println("B's doSomething, bInt: " + bInt); 
    } 
  } 
 
  public static void main(String[] args) { 
    B b = new B(); 
  } 
} 

輸出結(jié)果:

/** 
   * B's doSomething, bInt: 0 
   */ 

分析:首先需要知道,在沒(méi)有顯示提供構(gòu)造器時(shí)Java編譯器會(huì)生成默認(rèn)構(gòu)造器,并在開(kāi)始處調(diào)用父類(lèi)的構(gòu)造器,因此類(lèi)B的構(gòu)造器開(kāi)始會(huì)先調(diào)用類(lèi)A的構(gòu)造器。
類(lèi)A中調(diào)用了protected方法doSomething,從輸出結(jié)果中我們看到實(shí)際上調(diào)用的是子類(lèi)的方法實(shí)現(xiàn),而此時(shí)子類(lèi)的實(shí)例化還未開(kāi)始,因此bInt并沒(méi)有如“預(yù)期”那樣是9,而是0;
這就是由于動(dòng)態(tài)綁定,doSomething是一個(gè)protected方法,因此它是通過(guò)invokevirtual指令調(diào)用的,該指令根據(jù)對(duì)象實(shí)例的類(lèi)型找到對(duì)應(yīng)的方法實(shí)現(xiàn)(這里就是B的實(shí)例對(duì)象,對(duì)應(yīng)方法就是類(lèi)B的方法實(shí)現(xiàn))執(zhí)行,故而有此結(jié)果。

結(jié)論:正如前面說(shuō)的“不要在構(gòu)造器,clone方法和readObject方法中調(diào)用可被覆蓋的方法”。

以上就是為大家介紹的Java類(lèi)初始化和實(shí)例化中的2個(gè)“雷區(qū)”,希望對(duì)大家的學(xué)習(xí)有所幫助。

相關(guān)文章

  • Java 文創(chuàng)商城系統(tǒng)的實(shí)現(xiàn)流程

    Java 文創(chuàng)商城系統(tǒng)的實(shí)現(xiàn)流程

    讀萬(wàn)卷書(shū)不如行萬(wàn)里路,只學(xué)書(shū)上的理論是遠(yuǎn)遠(yuǎn)不夠的,只有在實(shí)戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用java+SSM+mysql+maven+tomcat實(shí)現(xiàn)一個(gè)文創(chuàng)商城系統(tǒng),大家可以在過(guò)程中查缺補(bǔ)漏,提升水平
    2021-11-11
  • 使用maven方式創(chuàng)建springboot項(xiàng)目的方式

    使用maven方式創(chuàng)建springboot項(xiàng)目的方式

    使用Spring Initializr創(chuàng)建spring boot項(xiàng)目,因?yàn)橥饩W(wǎng)問(wèn)題導(dǎo)致很難成功,所以只能使用maven方式,這里介紹下使用maven方式創(chuàng)建springboot項(xiàng)目的方法,感興趣的朋友一起看看吧
    2022-09-09
  • Spring MVC實(shí)現(xiàn)文件上傳和下載

    Spring MVC實(shí)現(xiàn)文件上傳和下載

    這篇文章主要為大家詳細(xì)介紹了Spring MVC實(shí)現(xiàn)文件上傳和下載,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-04-04
  • Java編程一道多線(xiàn)程問(wèn)題實(shí)例代碼

    Java編程一道多線(xiàn)程問(wèn)題實(shí)例代碼

    這篇文章主要介紹了Java編程一道多線(xiàn)程問(wèn)題實(shí)例代碼,分享了相關(guān)代碼示例,小編覺(jué)得還是挺不錯(cuò)的,具有一定借鑒價(jià)值,需要的朋友可以參考下
    2018-02-02
  • Java基于命令模式實(shí)現(xiàn)郵局發(fā)信功能詳解

    Java基于命令模式實(shí)現(xiàn)郵局發(fā)信功能詳解

    這篇文章主要介紹了Java基于命令模式實(shí)現(xiàn)郵局發(fā)信功能,較為詳細(xì)的分析了命令行模式的概念、原理并結(jié)合實(shí)例形式分析了Java使用命令行模式實(shí)現(xiàn)郵局發(fā)信功能的相關(guān)操作技巧與注意事項(xiàng),需要的朋友可以參考下
    2018-04-04
  • SpringBoot自定義錯(cuò)誤處理邏輯詳解

    SpringBoot自定義錯(cuò)誤處理邏輯詳解

    這篇文章主要介紹了SpringBoot自定義錯(cuò)誤處理邏輯,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧
    2022-10-10
  • Java將文件夾保留目錄打包為 ZIP 壓縮包并下載的教程詳解

    Java將文件夾保留目錄打包為 ZIP 壓縮包并下載的教程詳解

    這篇文章主要介紹了Java將文件夾保留目錄打包為 ZIP 壓縮包并下載的教程詳解,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-08-08
  • Java設(shè)計(jì)模式之適配器模式的示例詳解

    Java設(shè)計(jì)模式之適配器模式的示例詳解

    適配器模式,即將某個(gè)類(lèi)的接口轉(zhuǎn)換成客戶(hù)端期望的另一個(gè)接口的表示,主要目的是實(shí)現(xiàn)兼容性,讓原本因?yàn)榻涌诓黄ヅ?,沒(méi)辦法一起工作的兩個(gè)類(lèi),可以協(xié)同工作。本文將通過(guò)示例詳細(xì)介紹適配器模式,需要的可以參考一下
    2022-08-08
  • SpringMVC整合SpringSession 實(shí)現(xiàn)sessiong

    SpringMVC整合SpringSession 實(shí)現(xiàn)sessiong

    這篇文章主要介紹了SpringMVC整合SpringSession 實(shí)現(xiàn)session的實(shí)例代碼,本文通過(guò)實(shí)例相結(jié)合的形式給大家介紹的非常詳細(xì),需要的朋友參考下吧
    2018-04-04
  • 詳解Mybatis內(nèi)的mapper方法為何不能重載

    詳解Mybatis內(nèi)的mapper方法為何不能重載

    這篇文章主要介紹了詳解Mybatis內(nèi)的mapper方法為何不能重載,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-12-12

最新評(píng)論