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

Java類繼承關系中的初始化順序實例詳解

 更新時間:2019年09月15日 10:55:16   作者:Andy奧  
這篇文章主要介紹了Java類繼承關系中的初始化順序,結合實例形式詳細對比分析了Java非繼承關系中的初始化與繼承關系中的初始化相關原理與操作技巧,需要的朋友可以參考下

本文實例講述了Java類繼承關系中的初始化順序。分享給大家供大家參考,具體如下:

Java類初始化的順序經常讓人犯迷糊,現(xiàn)在本文嘗試著從JVM的角度,對Java非繼承和繼承關系中類的初始化順序進行試驗,嘗試給出JVM角度的解釋。

非繼承關系中的初始化順序

對于非繼承關系,主類InitialOrderWithoutExtend中包含了靜態(tài)成員變量(類變量)SampleClass 類的一個實例,普通成員變量SampleClass 類的2個實例(在程序中的順序不一樣)以及一個靜態(tài)代碼塊,其中靜態(tài)代碼塊中如果靜態(tài)成員變量sam不為空,則改變sam的引用。main()方法中創(chuàng)建了2個主類對象,打印2個主類對象的靜態(tài)成員sam的屬性s。

代碼1

package com.j2se;

public class InitialOrderWithoutExtend {
 static SampleClass sam = new SampleClass("靜態(tài)成員sam初始化");
 SampleClass sam1 = new SampleClass("普通成員sam1初始化");
 static {
  System.out.println("static塊執(zhí)行");
  if (sam == null)
   System.out.println("sam is null");
  sam = new SampleClass("靜態(tài)塊內初始化sam成員變量");
 }

 SampleClass sam2 = new SampleClass("普通成員sam2初始化");

 InitialOrderWithoutExtend() {
  System.out.println("InitialOrderWithoutExtend默認構造函數(shù)被調用");
 }

 public static void main(String[] args) {
  // 創(chuàng)建第1個主類對象
  System.out.println("第1個主類對象:");
  InitialOrderWithoutExtend ts = new InitialOrderWithoutExtend();

  // 創(chuàng)建第2個主類對象
  System.out.println("第2個主類對象:");
  InitialOrderWithoutExtend ts2 = new InitialOrderWithoutExtend();

  // 查看兩個主類對象的靜態(tài)成員:
  System.out.println("2個主類對象的靜態(tài)對象:");
  System.out.println("第1個主類對象, 靜態(tài)成員sam.s: " + ts.sam);
  System.out.println("第2個主類對象, 靜態(tài)成員sam.s: " + ts2.sam);
 }
}

class SampleClass {
 // SampleClass 不能包含任何主類InitialOrderWithoutExtend的成員變量
 // 否則導致循環(huán)引用,循環(huán)初始化,調用棧深度過大
 // 拋出 StackOverFlow 異常
 // static InitialOrderWithoutExtend iniClass1 = new InitialOrderWithoutExtend("靜態(tài)成員iniClass1初始化");
 // InitialOrderWithoutExtend iniClass2 = new InitialOrderWithoutExtend("普通成員成員iniClass2初始化");

 String s;

 SampleClass(String s) {
  this.s = s;
  System.out.println(s);
 }

 SampleClass() {
  System.out.println("SampleClass默認構造函數(shù)被調用");
 }

 @Override
 public String toString() {
  return this.s;
 }
}

 

輸出結果:

靜態(tài)成員sam初始化
static塊執(zhí)行
靜態(tài)塊內初始化sam成員變量
第1個主類對象:
普通成員sam1初始化
普通成員sam2初始化
InitialOrderWithoutExtend默認構造函數(shù)被調用
第2個主類對象:
普通成員sam1初始化
普通成員sam2初始化
InitialOrderWithoutExtend默認構造函數(shù)被調用
2個主類對象的靜態(tài)對象:
第1個主類對象, 靜態(tài)成員sam.s: 靜態(tài)塊內初始化sam成員變量
第2個主類對象, 靜態(tài)成員sam.s: 靜態(tài)塊內初始化sam成員變量

 

由輸出結果可知,執(zhí)行順序為:

  1. static靜態(tài)代碼塊和靜態(tài)成員
  2. 普通成員
  3. 構造函數(shù)執(zhí)行

當具有多個靜態(tài)成員和靜態(tài)代碼塊或者多個普通成員時,初始化順序和成員在程序中申明的順序一致。

注意到在該程序的靜態(tài)代碼塊中,修改了靜態(tài)成員sam的引用。main()方法中創(chuàng)建了2個主類對象,但是由輸出結果可知,靜態(tài)成員和靜態(tài)代碼塊只進行了一次初始化,并且新建的2個主類對象的靜態(tài)成員sam.s是相同的。由此可知,類的靜態(tài)成員和靜態(tài)代碼塊在類加載中是最先進行初始化的,并且只進行一次。該類的多個實例共享靜態(tài)成員,靜態(tài)成員的引用指向程序最后所賦予的引用。

繼承關系中的初始化順序

此處使用了3個類來驗證繼承關系中的初始化順序:Father父類、Son子類和Sample類。父類和子類中各自包含了非靜態(tài)代碼區(qū)、靜態(tài)代碼區(qū)、靜態(tài)成員、普通成員。運行時的主類為InitialOrderWithExtend類,main()方法中創(chuàng)建了一個子類的對象,并且使用Father對象指向Son類實例的引用(父類對象指向子類引用,多態(tài))。

代碼2

package com.j2se;

public class InitialOrderWithExtend {
 public static void main(String[] args) {
  Father ts = new Son();
 }
}

class Father {
 {
  System.out.println("父類 非靜態(tài)塊 1 執(zhí)行");
 }
 static {
  System.out.println("父類 static塊 1 執(zhí)行");
 }
 static Sample staticSam1 = new Sample("父類 靜態(tài)成員 staticSam1 初始化");
 Sample sam1 = new Sample("父類 普通成員 sam1 初始化");
 static Sample staticSam2 = new Sample("父類 靜態(tài)成員 staticSam2 初始化");
 static {
  System.out.println("父類 static塊 2 執(zhí)行");
 }

 Father() {
  System.out.println("父類 默認構造函數(shù)被調用");
 }

 Sample sam2 = new Sample("父類 普通成員 sam2 初始化");

 {
  System.out.println("父類 非靜態(tài)塊 2 執(zhí)行");
 }

}

class Son extends Father {
 {
  System.out.println("子類 非靜態(tài)塊 1 執(zhí)行");
 }

 static Sample staticSamSub1 = new Sample("子類 靜態(tài)成員 staticSamSub1 初始化");

 Son() {
  System.out.println("子類 默認構造函數(shù)被調用");
 }

 Sample sam1 = new Sample("子類 普通成員 sam1 初始化");
 static Sample staticSamSub2 = new Sample("子類 靜態(tài)成員 staticSamSub2 初始化");

 static {
  System.out.println("子類 static塊1 執(zhí)行");
 }

 Sample sam2 = new Sample("子類 普通成員 sam2 初始化");

 {
  System.out.println("子類 非靜態(tài)塊 2 執(zhí)行");
 }

 static {
  System.out.println("子類 static塊2 執(zhí)行");
 }
}

class Sample {
 Sample(String s) {
  System.out.println(s);
 }

 Sample() {
  System.out.println("Sample默認構造函數(shù)被調用");
 }
}

運行結果:

父類 static塊 1 執(zhí)行
父類 靜態(tài)成員 staticSam1 初始化
父類 靜態(tài)成員 staticSam2 初始化
父類 static塊 2 執(zhí)行
子類 靜態(tài)成員 staticSamSub1 初始化
子類 靜態(tài)成員 staticSamSub2 初始化
子類 static塊1 執(zhí)行
子類 static塊2 執(zhí)行
父類 非靜態(tài)塊 1 執(zhí)行
父類 普通成員 sam1 初始化
父類 普通成員 sam2 初始化
父類 非靜態(tài)塊 2 執(zhí)行
父類 默認構造函數(shù)被調用
子類 非靜態(tài)塊 1 執(zhí)行
子類 普通成員 sam1 初始化
子類 普通成員 sam2 初始化
子類 非靜態(tài)塊 2 執(zhí)行
子類 默認構造函數(shù)被調用

由輸出結果可知,執(zhí)行的順序為:

  1. 父類靜態(tài)代碼區(qū)和父類靜態(tài)成員
  2. 子類靜態(tài)代碼區(qū)和子類靜態(tài)成員
  3. 父類非靜態(tài)代碼區(qū)和普通成員
  4. 父類構造函數(shù)
  5. 子類非靜態(tài)代碼區(qū)和普通成員
  6. 子類構造函數(shù)

與非繼承關系中的初始化順序一致的地方在于,靜態(tài)代碼區(qū)和父類靜態(tài)成員、非靜態(tài)代碼區(qū)和普通成員是同一級別的,當存在多個這樣的代碼塊或者成員時,初始化的順序和它們在程序中申明的順序一致;此外,靜態(tài)代碼區(qū)和靜態(tài)成員也是僅僅初始化一次,但是在初始化過程中,可以修改靜態(tài)成員的引用。

初始化順序圖示

非繼承關系

繼承關系

類初始化順序的JVM解釋

類初始化順序受到JVM類加載機制的控制,類加載機制包括加載、驗證、準備、解析、初始化等步驟。不管是在繼承還是非繼承關系中,類的初始化順序主要受到JVM類加載時機、解析和clinit()初始化規(guī)則的影響。

加載時機

加載是類加載機制的第一個階段,只有在5種主動引用的情況下,才會觸發(fā)類的加載,而在其他被動引用的情況下并不會觸發(fā)類的加載。關于類加載時機和5中主動引用和被動引用詳見【深入理解JVM】:類加載機制。其中3種主動引用的形式為:

  • 程序啟動需要觸發(fā)main方法的時候,虛擬機會先觸發(fā)這個類的初始化
  • 使用new關鍵字實例化對象、讀取或設置一個類的靜態(tài)字段(被final修飾、JIT時放入常量池的靜態(tài)字段除外)、調用一個類的靜態(tài)方法,會觸發(fā)初始化
  • 當初始化一個類的時候,如果其父類沒有初始化,則需要先觸發(fā)其父類的初始化

代碼1中觸發(fā)main()方法前,需要觸發(fā)主類InitialOrderWithoutExtend的初始化,主類初始化觸發(fā)后,對靜態(tài)代碼區(qū)和靜態(tài)成員進行初始化后,打印”第1個主類對象:”,之后遇到newInitialOrderWithoutExtend ts = new InitialOrderWithoutExtend();,再進行其他普通變量的初始化。

代碼2是繼承關系,在子類初始化前,必須先觸發(fā)父類的初始化。

類解析在繼承關系中的自下而上遞歸

類加載機制的解析階段將常量池中的符號引用替換為直接引用,主要針對的是類或者接口、字段、類方法、方法類型、方法句柄和調用點限定符7類符號引用。關于類的解析過程詳見【深入理解JVM】:類加載機制

而在字段解析、類方法解析、方法類型解析中,均遵循繼承關系中自下而上遞歸搜索解析的規(guī)則,由于遞歸的特性(即數(shù)據(jù)結構中棧的“后進先出”),初始化的過程則是由上而下、從父類到子類的初始化順序。

初始化clinit()方法

初始化階段是執(zhí)行類構造器方法clinit() 的過程。clinit() 是編譯器自動收集類中所有類變量(靜態(tài)變量)的賦值動作和靜態(tài)語句塊合并生成的。編譯器收集的順序是由語句在源文件中出現(xiàn)的順序決定的。JVM會保證在子類的clinit() 方法執(zhí)行之前,父類的clinit() 方法已經執(zhí)行完畢。

因此所有的初始化過程中clinit()方法,保證了靜態(tài)變量和靜態(tài)語句塊總是最先初始化的,并且一定是先執(zhí)行父類clinit(),在執(zhí)行子類的clinit()。

代碼順序與對象內存布局

在前面的分析中我們看到,類的初始化具有相對固定的順序:靜態(tài)代碼區(qū)和靜態(tài)變量先于非靜態(tài)代碼區(qū)和普通成員,先于構造函數(shù)。在相同級別的初始化過程中,初始化順序與變量定義在程序的中順序是一致的。

而代碼順序在對象內存布局中同樣有影響。(關于JVM對象內存布局詳見【深入理解JVM】:Java對象的創(chuàng)建、內存布局、訪問定位。)

在HotSpot虛擬機中,對象在內存中存儲的布局可以分為3塊區(qū)域:對象頭(Header)、實例數(shù)據(jù)(Instance Data)和對齊填充(Padding)。而實例數(shù)據(jù)是對象真正存儲的有效信息,也是程序代碼中所定義的各種類型的字段內容。

無論是從父類繼承還是子類定義的,都需要記錄下來,這部分的存儲順序JVM參數(shù)和字段在程序源碼中定義順序的影響。HotSpot虛擬機默認的分配策略為longs/doubles、ints、shorts/chars、bytes/booleans、oop,從分配策略中可以看出,相同寬度的字段總是分配到一起。滿足這個條件的前提下,父類中定義的變量會出現(xiàn)在子類之前。不過,如果啟用了JVM參數(shù)CompactFields(默認為true,啟用),那么子類中較窄的變量也可能會插入到父類變量的空隙中。

更多java相關內容感興趣的讀者可查看本站專題:《Java面向對象程序設計入門與進階教程》、《Java數(shù)據(jù)結構與算法教程》、《Java操作DOM節(jié)點技巧總結》、《Java文件與目錄操作技巧匯總》和《Java緩存操作技巧匯總

希望本文所述對大家java程序設計有所幫助。

相關文章

  • Java鏈表(Linked List)基本原理與實現(xiàn)方法入門示例

    Java鏈表(Linked List)基本原理與實現(xiàn)方法入門示例

    這篇文章主要介紹了Java鏈表(Linked List)基本原理與實現(xiàn)方法,結合實例形式分析了Java鏈表(Linked List)的功能、原理、實現(xiàn)方法與操作注意事項,需要的朋友可以參考下
    2020-03-03
  • MyBatis?handleResultSet結果集解析過程示例

    MyBatis?handleResultSet結果集解析過程示例

    這篇文章主要為大家介紹了MyBatis?handleResultSet結果集解析過程示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-02-02
  • Java數(shù)據(jù)結構之鏈表的增刪查改詳解

    Java數(shù)據(jù)結構之鏈表的增刪查改詳解

    在這篇文章中,小編將帶大家了解一下Java數(shù)據(jù)結構中鏈表的增刪查改(以下結果均在IDEA中編譯)希望在方便自己復習的同時也能幫助到大家
    2022-09-09
  • MyBatis-Plus通過version機制實現(xiàn)樂觀鎖的思路

    MyBatis-Plus通過version機制實現(xiàn)樂觀鎖的思路

    version機制的核心思想就是,假設發(fā)生并發(fā)沖突的幾率很低,只有當更新數(shù)據(jù)的時候采取檢查是否有沖突,而判斷是否有沖突的依據(jù)就是version的值是否被改變了,這篇文章主要介紹了MyBatis-Plus通過version機制實現(xiàn)樂觀鎖的思路,需要的朋友可以參考下
    2021-09-09
  • 詳解spring cloud hystrix緩存功能的使用

    詳解spring cloud hystrix緩存功能的使用

    這篇文章主要介紹了詳解spring cloudhystrix緩存功能的使用,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-08-08
  • springboot實現(xiàn)文件上傳步驟解析

    springboot實現(xiàn)文件上傳步驟解析

    這篇文章主要介紹了springboot實現(xiàn)文件上傳步驟解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2019-12-12
  • JDK1.8中ConcurrentHashMap中computeIfAbsent死循環(huán)bug問題

    JDK1.8中ConcurrentHashMap中computeIfAbsent死循環(huán)bug問題

    這篇文章主要介紹了JDK1.8中ConcurrentHashMap中computeIfAbsent死循環(huán)bug,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-08-08
  • java學習筆記之eclipse+tomcat 配置

    java學習筆記之eclipse+tomcat 配置

    俗話說:工欲善其事必先利其器,既然要學習java,首先把java的開發(fā)環(huán)境搗鼓一下吧,這里我們來談談eclipse+tomcat的配置方法。
    2014-11-11
  • Java中class和Class的區(qū)別示例詳解

    Java中class和Class的區(qū)別示例詳解

    class 是java的關鍵字,在聲明java類時使用,Class是java JDK提供的一個類,完整路徑為java.lang.Class,下面這篇文章主要給大家介紹了關于Java中class和Class區(qū)別的相關資料,需要的朋友可以參考下
    2022-04-04
  • Java深入講解AWT實現(xiàn)事件處理流程

    Java深入講解AWT實現(xiàn)事件處理流程

    AWT的事件處理是一種委派式事件處理方式:普通組件(事件源)將整個事件處理委托給特定的對象(事件監(jiān)聽器);當該事件源發(fā)生指定的事件時,就通知所委托的事件監(jiān)聽器,由事件監(jiān)聽器來處理這個事件
    2022-04-04

最新評論

双辽市| 独山县| 田林县| 澄城县| 饶河县| 左云县| 库尔勒市| 读书| 巩留县| 墨竹工卡县| 广东省| 开原市| 沙湾县| 商水县| 井陉县| 屯昌县| 沁阳市| 三明市| 聂荣县| 芦溪县| 高台县| 苏州市| 宜良县| 蛟河市| 翁源县| 化隆| 金溪县| 洛浦县| 砀山县| 曲靖市| 龙江县| 禹城市| 壶关县| 池州市| 广汉市| 正定县| 肇州县| 望城县| 安岳县| 拉萨市| 吴堡县|