Java類(lèi)的初始化

之前整理了《JVM之類(lèi)加載機(jī)制》的文章焦人,對(duì)于一個(gè)類(lèi)的初始化階段介紹太過(guò)簡(jiǎn)略奶甘,這里再開(kāi)一篇文章篷店,著重介紹類(lèi)的初始化流程。

類(lèi)初始化是類(lèi)加載過(guò)程的最后一個(gè)階段臭家,到初始化階段疲陕,才真正開(kāi)始執(zhí)行類(lèi)中的Java程序代碼。虛擬機(jī)規(guī)范嚴(yán)格規(guī)定了有且只有四種情況必須立即對(duì)類(lèi)進(jìn)行初始化

  • 遇到new钉赁、getstatic蹄殃、putstatic、invokestatic這四條字節(jié)碼指令時(shí)橄霉,如果類(lèi)還沒(méi)有進(jìn)行過(guò)初始化窃爷,則需要先觸發(fā)其初始化。生成這四條指令最常見(jiàn)的Java代碼場(chǎng)景是:使用new關(guān)鍵字實(shí)例化對(duì)象時(shí)姓蜂、讀取或設(shè)置一個(gè)類(lèi)的靜態(tài)字段(static)時(shí)(被static修飾又被final修飾的按厘,已在編譯期把結(jié)果放入常量池的靜態(tài)字段除外)、以及調(diào)用一個(gè)類(lèi)的靜態(tài)方法時(shí)钱慢。

  • 使用Java.lang.refect包的方法對(duì)類(lèi)進(jìn)行反射調(diào)用時(shí)导狡,如果類(lèi)還沒(méi)有進(jìn)行過(guò)初始化伍派,則需要先觸發(fā)其初始化。

  • 當(dāng)初始化一個(gè)類(lèi)的時(shí)候,如果發(fā)現(xiàn)其父類(lèi)還沒(méi)有進(jìn)行初始化贞滨,則需要先觸發(fā)其父類(lèi)的初始化糖赔。

  • 當(dāng)虛擬機(jī)啟動(dòng)時(shí)窿祥,用戶(hù)需要指定一個(gè)要執(zhí)行的主類(lèi)顶燕,虛擬機(jī)會(huì)先執(zhí)行該主類(lèi),也就是main方法所在的類(lèi)饿敲。

上面所說(shuō)時(shí)顯示的對(duì)象創(chuàng)建妻导,還有幾種隱式初始化的方式也說(shuō)明一下:
  • 給String類(lèi)型變量賦值時(shí),若String對(duì)象在常量池中不存在怀各,則創(chuàng)建一個(gè)新的String對(duì)象

  • 對(duì)String對(duì)象進(jìn)行拼接操作倔韭,同上

  • 自動(dòng)裝箱機(jī)制可能會(huì)引起一個(gè)原子類(lèi)型的包裝類(lèi)對(duì)象被創(chuàng)建。

這里再說(shuō)一下類(lèi)不被初始化的情況:
  • 對(duì)于靜態(tài)字段(沒(méi)有final修飾)瓢对,只有直接定義這個(gè)字段的類(lèi)才會(huì)被初始化寿酌,子類(lèi)調(diào)用父類(lèi)的靜態(tài)字段并不會(huì)觸發(fā)子類(lèi)的初始化

  • static final 修飾的常量,在編輯時(shí)就存入了調(diào)用者的Class文件常量池中硕蛹,調(diào)用時(shí)并不會(huì)觸發(fā)定義類(lèi)的初始化醇疼,也就是這個(gè)常量已經(jīng)使用的類(lèi)綁定硕并。

  • 數(shù)組初始化過(guò)程并不會(huì)觸發(fā)引用類(lèi)的初始化

類(lèi)或接口初始化(準(zhǔn)備階段)

編譯器自動(dòng)收集類(lèi)變量賦值以及靜態(tài)代碼塊后自動(dòng)合并生成類(lèi)的<clint>(),類(lèi)開(kāi)始初始化時(shí)會(huì)為static變量賦上零值僵腺。

  • <clint>()對(duì)于類(lèi)和接口來(lái)說(shuō)這個(gè)方法并不是必須的鲤孵。
  • <clint>()中,靜態(tài)語(yǔ)句只能訪問(wèn)定義在它之前定義的靜態(tài)變量辰如,定義在它之后的靜態(tài)變量,可以賦值贵试,但不能訪問(wèn)琉兜。
  • 子類(lèi)<clint>()不需要顯示的調(diào)用父類(lèi)的構(gòu)造器,JVM保證子類(lèi)的<clint>()執(zhí)行之前毙玻,父類(lèi)的<clint>()已經(jīng)執(zhí)行完畢豌蟋。
  • 由于父類(lèi)的<clint>()先執(zhí)行,所以父類(lèi)的靜態(tài)語(yǔ)句優(yōu)先與子類(lèi)的靜態(tài)語(yǔ)句執(zhí)行
  • 先對(duì)類(lèi)桑滩,接口的執(zhí)行<clint>()時(shí)并不需要執(zhí)行父接口的<clint>()方法梧疲,只有使用父接口定義的變量時(shí),父接口才會(huì)初始化运准。接口的實(shí)現(xiàn)類(lèi)初始化時(shí)也不會(huì)調(diào)用接口的<clint>()
  • JVM保證一個(gè)類(lèi)的<clint>()執(zhí)行時(shí)線程安全的幌氮,多線程執(zhí)行類(lèi)的<clint>()時(shí)只能有一個(gè)被執(zhí)行,其余線程等待(執(zhí)行完畢后其他線程不再進(jìn)入<clint>())胁澳。如果一個(gè)類(lèi)的<clint>()執(zhí)行耗時(shí)操作该互,可能會(huì)造成多進(jìn)程阻塞

這里還有幾個(gè)注意點(diǎn):

接口也有初始化過(guò)程,在接口中不能使用“static{}”語(yǔ)句塊韭畸,但編譯器仍然會(huì)為接口生成<clint>()宇智,用于初始化接口中定義的成員變量(實(shí)際上是static final修飾的全局常量)。

二者在初始化時(shí)最主要的區(qū)別是:當(dāng)一個(gè)類(lèi)在初始化時(shí)胰丁,要求其父類(lèi)全部已經(jīng)初始化過(guò)了随橘,但是一個(gè)接口在初始化時(shí),并不要求其父接口全部都完成了初始化锦庸,只有在真正使用到父接口的時(shí)候(如引用接口中定義的常量)机蔗,才會(huì)初始化該父接口。這點(diǎn)也與類(lèi)初始化的情況很不同酸员,調(diào)用類(lèi)中的static final常量時(shí)并不會(huì) 觸發(fā)該類(lèi)的初始化蜒车,但是調(diào)用接口中的static final常量時(shí)便會(huì)觸發(fā)該接口的初始化

類(lèi)變量的賦值

下面簡(jiǎn)要說(shuō)明下final、static幔嗦、static final修飾的字段賦值的區(qū)別:

  • static修飾的字段在類(lèi)加載過(guò)程中的準(zhǔn)備階段被初始化為0或null等默認(rèn)值酿愧,而后在初始化階段(觸發(fā)類(lèi)構(gòu)造器<clint>())才會(huì)被賦予代碼中設(shè)定的值,如果沒(méi)有設(shè)定值邀泉,那么它的值就為默認(rèn)值嬉挡。

  • final修飾的字段在運(yùn)行時(shí)被初始化(可以直接賦值钝鸽,也可以在實(shí)例構(gòu)造器中賦值),一旦賦值便不可更改庞钢;

  • static final修飾的字段在Javac時(shí)生成ConstantValue屬性拔恰,在類(lèi)加載的準(zhǔn)備階段根據(jù)ConstantValue的值為該字段賦值,它沒(méi)有默認(rèn)值基括,必須顯式地賦值颜懊,否則Javac時(shí)會(huì)報(bào)錯(cuò)》缑螅可以理解為在編譯期即把結(jié)果放入了常量池中河爹。

實(shí)例對(duì)象的初始化(初始化階段)

編譯器自動(dòng)收集實(shí)例變量初始化以及實(shí)例代碼塊后自動(dòng)合并生成類(lèi)的<init>()

  • 子類(lèi)初始化時(shí)會(huì)先調(diào)用父類(lèi)<init>(),用以保證子類(lèi)能正常初始化桐款。
  • 執(zhí)行子類(lèi)的<init>()

那么從上面可以知道咸这,一個(gè)類(lèi)在初始化過(guò)程中,構(gòu)造方法的執(zhí)行過(guò)程如下:

  • 父類(lèi)的<clint>()
  • 子類(lèi)的<clint>()
  • 父類(lèi)的<init>()
  • 子類(lèi)的<init>()

一個(gè)子類(lèi)自身代碼的初始化執(zhí)行順序如下:

  • Static Field Initial (類(lèi)變量)

  • Static Patch Initial (靜態(tài)初始化塊)

  • Field Initial (成員變量)

  • Field Patch Initial (初始化塊)

  • Structure Initial (構(gòu)造器)

上面第一條和第二條依據(jù)代碼定義的順序不同魔眨,執(zhí)行的順序也不同(定義在靜態(tài)代碼塊之后的的類(lèi)變量可以被靜態(tài)代碼塊賦值媳维,但是不能被訪問(wèn))

舉個(gè)栗子驗(yàn)證一下

public class ClassInitTest {

    public static void main(String[] args) {
        new Child();
    }

    public static class Parent {
        static {
            System.out.println("Parent static code");
        }

        public Parent() {
            System.out.println("Parent constructor code");
        }
    }

    public static class Child extends Parent {
    
         private int mInt = 100;

        {
            System.out.println(mInt);
            mInt = 200;
            System.out.println(mInt);
        }
    
        static {
            System.out.println("Child static code");
        }

        public Child() {
            System.out.println("Child constructor code");
            System.out.println(mInt);
        }
    }
}

打印如下:

Parent static code
Child static code
Parent constructor code
100
200
Child constructor code
200

上面可以看到即使類(lèi)變量定義在成語(yǔ)變量和初始代碼塊之下也是先被執(zhí)行的,同時(shí)我們可以看到構(gòu)造器的代碼時(shí)最后執(zhí)行的遏暴。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末侄刽,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子拓挥,更是在濱河造成了極大的恐慌唠梨,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,451評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件侥啤,死亡現(xiàn)場(chǎng)離奇詭異当叭,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)盖灸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén)蚁鳖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人赁炎,你說(shuō)我怎么就攤上這事醉箕。” “怎么了徙垫?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,782評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵讥裤,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我姻报,道長(zhǎng)己英,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,709評(píng)論 1 294
  • 正文 為了忘掉前任吴旋,我火速辦了婚禮损肛,結(jié)果婚禮上厢破,老公的妹妹穿的比我還像新娘。我一直安慰自己治拿,他們只是感情好摩泪,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,733評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著劫谅,像睡著了一般见坑。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上捏检,一...
    開(kāi)封第一講書(shū)人閱讀 51,578評(píng)論 1 305
  • 那天鳄梅,我揣著相機(jī)與錄音,去河邊找鬼未檩。 笑死,一個(gè)胖子當(dāng)著我的面吹牛粟焊,可吹牛的內(nèi)容都是我干的冤狡。 我是一名探鬼主播,決...
    沈念sama閱讀 40,320評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼项棠,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼悲雳!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起香追,我...
    開(kāi)封第一講書(shū)人閱讀 39,241評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤合瓢,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后透典,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體晴楔,經(jīng)...
    沈念sama閱讀 45,686評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,878評(píng)論 3 336
  • 正文 我和宋清朗相戀三年峭咒,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了税弃。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,992評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡凑队,死狀恐怖则果,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情漩氨,我是刑警寧澤西壮,帶...
    沈念sama閱讀 35,715評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站叫惊,受9級(jí)特大地震影響款青,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜赋访,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,336評(píng)論 3 330
  • 文/蒙蒙 一可都、第九天 我趴在偏房一處隱蔽的房頂上張望缓待。 院中可真熱鬧,春花似錦渠牲、人聲如沸旋炒。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,912評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)瘫镇。三九已至,卻和暖如春答姥,著一層夾襖步出監(jiān)牢的瞬間铣除,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,040評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工鹦付, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留尚粘,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,173評(píng)論 3 370
  • 正文 我出身青樓敲长,卻偏偏與公主長(zhǎng)得像郎嫁,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子祈噪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,947評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 一個(gè)Java對(duì)象的創(chuàng)建過(guò)程往往包括 類(lèi)初始化 和 類(lèi)實(shí)例化 兩個(gè)階段泽铛。本文討論的是『類(lèi)初始化』的時(shí)機(jī),以及利用這一...
    NoahU閱讀 2,094評(píng)論 0 2
  • ??最近在看回顧Java基礎(chǔ)的時(shí)候辑鲤,發(fā)現(xiàn)看似很簡(jiǎn)單的類(lèi)初始化的順序卻并不是那么簡(jiǎn)單(往往越是簡(jiǎn)單的東西反而越容易出...
    BrightLoong閱讀 1,490評(píng)論 0 2
  • 在java中盔腔,一個(gè)類(lèi)被使用要經(jīng)過(guò)裝載,連接月褥,初始化這樣的過(guò)程 一弛随、裝載: 類(lèi)裝載器(Bootstrap Class...
    暮雨沉淪閱讀 596評(píng)論 0 0
  • 我和媽媽去超市買(mǎi)東西。剛進(jìn)門(mén)準(zhǔn)備寄存吓坚,就看到寄存柜前站著一對(duì)焦急的母女在急促地對(duì)話撵幽。 “我記得給你了〗富鳎” “是啊盐杂,...
    andy花兒閱讀 304評(píng)論 0 0
  • 生活,每個(gè)人心中都有一個(gè)不一樣的理解哆窿,所以它是多樣的链烈,也是善變的。 你以什么樣的態(tài)度來(lái)對(duì)待它挚躯,它將還你一個(gè)什么樣的生活强衡。
    米粒的世界閱讀 238評(píng)論 1 1