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

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

Java類(lèi)繼承關(guān)系中的初始化順序?qū)嵗斀?/h1>

本文實(shí)例講述了Java類(lèi)繼承關(guān)系中的初始化順序。分享給大家供大家參考,具體如下:

公司主營(yíng)業(yè)務(wù):做網(wǎng)站、網(wǎng)站設(shè)計(jì)、移動(dòng)網(wǎng)站開(kāi)發(fā)等業(yè)務(wù)。幫助企業(yè)客戶(hù)真正實(shí)現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競(jìng)爭(zhēng)能力。成都創(chuàng)新互聯(lián)公司是一支青春激揚(yáng)、勤奮敬業(yè)、活力青春激揚(yáng)、勤奮敬業(yè)、活力澎湃、和諧高效的團(tuán)隊(duì)。公司秉承以“開(kāi)放、自由、嚴(yán)謹(jǐn)、自律”為核心的企業(yè)文化,感謝他們對(duì)我們的高要求,感謝他們從不同領(lǐng)域給我們帶來(lái)的挑戰(zhàn),讓我們激情的團(tuán)隊(duì)有機(jī)會(huì)用頭腦與智慧不斷的給客戶(hù)帶來(lái)驚喜。成都創(chuàng)新互聯(lián)公司推出鎮(zhèn)平免費(fèi)做網(wǎng)站回饋大家。

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

非繼承關(guān)系中的初始化順序

對(duì)于非繼承關(guān)系,主類(lèi)InitialOrderWithoutExtend中包含了靜態(tài)成員變量(類(lèi)變量)SampleClass 類(lèi)的一個(gè)實(shí)例,普通成員變量SampleClass 類(lèi)的2個(gè)實(shí)例(在程序中的順序不一樣)以及一個(gè)靜態(tài)代碼塊,其中靜態(tài)代碼塊中如果靜態(tài)成員變量sam不為空,則改變sam的引用。main()方法中創(chuàng)建了2個(gè)主類(lèi)對(duì)象,打印2個(gè)主類(lèi)對(duì)象的靜態(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)塊內(nèi)初始化sam成員變量");
 }

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

 InitialOrderWithoutExtend() {
  System.out.println("InitialOrderWithoutExtend默認(rèn)構(gòu)造函數(shù)被調(diào)用");
 }

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

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

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

class SampleClass {
 // SampleClass 不能包含任何主類(lèi)InitialOrderWithoutExtend的成員變量
 // 否則導(dǎo)致循環(huán)引用,循環(huán)初始化,調(diào)用棧深度過(guò)大
 // 拋出 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默認(rèn)構(gòu)造函數(shù)被調(diào)用");
 }

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

 

輸出結(jié)果:

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

 

由輸出結(jié)果可知,執(zhí)行順序?yàn)椋?/p>

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

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

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

繼承關(guān)系中的初始化順序

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

代碼2

package com.j2se;

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

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

 Father() {
  System.out.println("父類(lèi) 默認(rèn)構(gòu)造函數(shù)被調(diào)用");
 }

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

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

}

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

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

 Son() {
  System.out.println("子類(lèi) 默認(rèn)構(gòu)造函數(shù)被調(diào)用");
 }

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

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

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

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

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

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

 Sample() {
  System.out.println("Sample默認(rèn)構(gòu)造函數(shù)被調(diào)用");
 }
}

運(yùn)行結(jié)果:

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

由輸出結(jié)果可知,執(zhí)行的順序?yàn)椋?/p>

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

與非繼承關(guān)系中的初始化順序一致的地方在于,靜態(tài)代碼區(qū)和父類(lèi)靜態(tài)成員、非靜態(tài)代碼區(qū)和普通成員是同一級(jí)別的,當(dāng)存在多個(gè)這樣的代碼塊或者成員時(shí),初始化的順序和它們?cè)诔绦蛑猩昝鞯捻樞蛞恢拢淮送?,靜態(tài)代碼區(qū)和靜態(tài)成員也是僅僅初始化一次,但是在初始化過(guò)程中,可以修改靜態(tài)成員的引用。

初始化順序圖示

非繼承關(guān)系

Java類(lèi)繼承關(guān)系中的初始化順序?qū)嵗斀?></p><p><strong>繼承關(guān)系</strong></p><p><img src=類(lèi)初始化順序的JVM解釋

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

加載時(shí)機(jī)

加載是類(lèi)加載機(jī)制的第一個(gè)階段,只有在5種主動(dòng)引用的情況下,才會(huì)觸發(fā)類(lèi)的加載,而在其他被動(dòng)引用的情況下并不會(huì)觸發(fā)類(lèi)的加載。關(guān)于類(lèi)加載時(shí)機(jī)和5中主動(dòng)引用和被動(dòng)引用詳見(jiàn)【深入理解JVM】:類(lèi)加載機(jī)制。其中3種主動(dòng)引用的形式為:

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

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

類(lèi)解析在繼承關(guān)系中的自下而上遞歸

類(lèi)加載機(jī)制的解析階段將常量池中的符號(hào)引用替換為直接引用,主要針對(duì)的是類(lèi)或者接口、字段、類(lèi)方法、方法類(lèi)型、方法句柄和調(diào)用點(diǎn)限定符7類(lèi)符號(hào)引用。關(guān)于類(lèi)的解析過(guò)程詳見(jiàn)【深入理解JVM】:類(lèi)加載機(jī)制。

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

初始化clinit()方法

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

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

代碼順序與對(duì)象內(nèi)存布局

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

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

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

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

更多java相關(guān)內(nèi)容感興趣的讀者可查看本站專(zhuān)題:《Java面向?qū)ο蟪绦蛟O(shè)計(jì)入門(mén)與進(jìn)階教程》、《Java數(shù)據(jù)結(jié)構(gòu)與算法教程》、《Java操作DOM節(jié)點(diǎn)技巧總結(jié)》、《Java文件與目錄操作技巧匯總》和《Java緩存操作技巧匯總》

希望本文所述對(duì)大家java程序設(shè)計(jì)有所幫助。


分享標(biāo)題:Java類(lèi)繼承關(guān)系中的初始化順序?qū)嵗斀?
文章位置:http://weahome.cn/article/psooej.html

其他資訊

在線咨詢(xún)

微信咨詢(xún)

電話咨詢(xún)

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部