你可能誤解了的類初始化和對象初始化

不知道大家有沒有這么一種經(jīng)驗——無論遇到的是新舊知識租漂,經(jīng)常地會以為自己掌握了理盆,但當換另一種問法俯抖,或者增加一點難度的問題输瓜,往往我們會手足無措,不知所云芬萍。這段時間非常自信自己已經(jīng)掌握了一個知識點尤揣,隔段時間才發(fā)現(xiàn)之前自己的理解其實完全是錯誤的,當初愉悅自信的心情依稀記于心中柬祠,而此刻對比起來卻是啪啪啪把臉打的很疼北戏,心情也變得很低落。現(xiàn)在的我可以肯定——自己太功利浮躁漫蛔,懶于思考嗜愈。意識到這種現(xiàn)象后旧蛾,為了變得更好,與自己和解蠕嫁,腳踏實地花時間死磕吧锨天,一開始進步可能很慢,時間久了剃毒,相信一定會有突破的~

那今天要提的一個現(xiàn)象呢病袄,是我在學習Java類加載機制時候碰到的。關(guān)于詳細的Java類加載知識的輸出赘阀,我還需要一段時間的整合益缠。這篇文章先做個小記錄。

額外插入一個注意事項——靜態(tài)代碼塊和靜態(tài)方法不是同一個東西基公。其實基礎(chǔ)常常不會難幅慌,只是我們太過功利焦急,囫圇吞棗轰豆,把很多概念混淆了欠痴,后來越來越亂。在寫法上秒咨,下圖前3行就是靜態(tài)代碼塊,后3行是靜態(tài)方法掌挚。二者都是在類加載的時候初始化的雨席,區(qū)別就是——靜態(tài)代碼塊是自動執(zhí)行的,而靜態(tài)方法是被調(diào)用的時候才執(zhí)行的吠式,比如Test.function(); 陡厘。

網(wǎng)上看到很多篇文章寫了幾個小小的demo,然后根據(jù)運行結(jié)果總結(jié)了靜態(tài)代碼塊特占、構(gòu)造代碼塊和構(gòu)造方法的執(zhí)行順序——靜態(tài)代碼塊>非靜態(tài)代碼塊>構(gòu)造方法糙置,完事。原本我也以為自己背下這個結(jié)論就萬事大吉了是目。直到我遇到了下面這個例子谤饭,我才開始反思,當我在看別人博客的時候懊纳,我在看什么揉抵。花兩分鐘想想下面代碼的運行結(jié)果會是什么呢嗤疯?

當親自碼代碼并運行代碼后冤今,我對運行結(jié)果充滿了疑惑。

運行結(jié)果

網(wǎng)上的文章不都說執(zhí)行順序是靜態(tài)代碼塊>非靜態(tài)代碼塊>構(gòu)造方法嗎茂缚?雖然這個代碼里沒模擬輸出構(gòu)造方法(大家可以自己加上試試)戏罢,但為什么結(jié)果是三行輸出屋谭,并且第一行結(jié)果是normal?難道網(wǎng)上文章的總結(jié)是錯誤的龟糕?其實不盡然桐磁,我掌握的知識不夠全面,以至于沒法判斷導(dǎo)致此現(xiàn)象的原因翩蘸,更別說舉一反三了所意。

當Java源代碼(.java文件)被編譯器編譯成Java字節(jié)碼(.class文件)時,會自動產(chǎn)生兩個方法催首,一個是類的初始化方法<clinit>扶踊,另一個是對象實例的初始化方法<init>。但需要注意的是郎任,并不是所有的類的.class文件中都擁有一個<clinit>()的——如果類沒有聲明任何類變量(類變量即靜態(tài)變量秧耗,被static修飾的變量。類的成員變量包含靜態(tài)變量和普通成員變量)舶治,也沒有靜態(tài)初始化語句分井,那么就不會有<clinit>();如果類僅包含靜態(tài)final變量的類變量初始化語句霉猛,并且這類類變量初始化語句采用編譯時常量表達式尺锚,則類也不會有<clinit>方法。

public class Extended {

? ? ? static final int age = 18;

? ? ? ? static final int height = age*10;

}

類Extended聲明了兩個常量——age和height惜浅,并賦了初始值瘫辩,但這兩個字段并沒有被當做類變量,而是被Java編譯器特殊處理了坛悉,因為被final修飾了伐厌,被當做常量。JVM在使用了它們的任何類的常量池或者字節(jié)碼流中直接存放的是它們表示的常量的 int 值裸影。

另外需要注意的一點是挣轨,Java編譯器為它編譯的每個類中的構(gòu)造方法都產(chǎn)生一個<init>方法。我們可以想想轩猩,一個類可以有很多構(gòu)造函數(shù)卷扮,所以一個類的.class文件中至少生成這樣一個<init>方法(即無參默認構(gòu)造方法)。

好界轩,就算上面的內(nèi)容你并不能記憶画饥,也不影響接下來內(nèi)容的掌握。只是多了解點片塊知識浊猾,有利于搭建更大的知識模塊抖甘,到時候融會貫通,感受設(shè)計之美『鳎現(xiàn)在我們來談?wù)務(wù)f<clinit>方法和<init>()方法都是什么時候被調(diào)用的衔彻。這是關(guān)鍵點薇宠。

<clinit>():在JVM第一次加載.class文件到內(nèi)存時調(diào)用。包括靜態(tài)變量初始化語句(如第9行和第11行)和靜態(tài)代碼塊(如第16~18行)的執(zhí)行艰额。

<init>():在對象實例創(chuàng)建出來的時候調(diào)用澄港。

對應(yīng)到我們上面的代碼例子要怎么理解呢?別急柄沮,我們再了解一些背景——JVM的其中一個工作內(nèi)容是借助類裝載器子系統(tǒng)將.class文件讀取到內(nèi)存中的回梧。我們知道.class文件是一種8位字節(jié)的二進制流文件,JVM裝載一個.class文件時祖搓,它會從這個二進制數(shù)據(jù)中解析類型信息狱意,然后把這些類型信息放到方法區(qū)中。方法區(qū)中存有類變量(類變量即靜態(tài)變量)和方法等拯欧。而當程序運行時详囤,JVM會把所有該程序在運行時創(chuàng)建的對象都放到堆中猖败。

一般情況下巡球,靜態(tài)隨著類的加載而加載,而且優(yōu)先于對象的存在凉蜂「眉郑回過頭來對應(yīng)我們上面的代碼羔杨。在走19行入口之前會先加載Test這個類。那怎么加載呢杨蛋,按照實際代碼寫的內(nèi)容问畅,并且先執(zhí)行靜態(tài)內(nèi)容,或者我們可以換個說法六荒,<clinit>()中有的內(nèi)容是第9、11矾端、16~18行掏击。而<init>()中是13~15行內(nèi)容。因為在19行入口前先加載Test這個類秩铆,所以先執(zhí)行<clinit>()砚亭,而當加載完畢,開始執(zhí)行20行代碼時殴玛,就調(diào)用<init>()捅膘。

但是照我這么說,還是不明白為什么運行結(jié)果先輸出了“normal”滚粟?那當然了寻仗,我還沒講到這點。

代碼從上到下凡壤,發(fā)現(xiàn)第9行是類變量署尤,放到方法區(qū)去耙替,然后再按順序?qū)ふ移渌o態(tài)內(nèi)容。我們可以看到第11行的時候曹体,創(chuàng)建了一個static的Test類對象test俗扇。咋辦呀咋辦呀,它好像很特別箕别?不按常理出牌铜幽?是static,但是又是新創(chuàng)建的對象串稀?怎么不是一個基本類型除抛?這樣可怎么整呀?其實很簡單厨诸,我們上面說了<init>():在對象實例創(chuàng)建出來的時候調(diào)用镶殷。那淡定地在11行調(diào)起<init>()就好。那<init>()里面有什么內(nèi)容微酬?就是13~15行的代碼了绘趋,輸出“normal”。

我們繼續(xù)看代碼颗管,執(zhí)行完11行代碼后陷遮,再尋找下一部分靜態(tài)內(nèi)容——16~18行,輸出“static 1”垦江。至此帽馋,Test類里沒有靜態(tài)內(nèi)容了。加載結(jié)束比吭,該進入第19行了绽族,到第20行的時候,發(fā)現(xiàn)又創(chuàng)建了一個Test對象衩藤,那怎么做吧慢?調(diào)用<init>()呀,即又跑了一遍13~15行的代碼赏表,輸出“normal”了检诗。

一點小建議:可以在第20行之前再寫一行代碼

System.out.println("==========");

大家還可以試試在Test類中增加構(gòu)造函數(shù)的代碼,并且構(gòu)造函數(shù)里面做類似的輸出瓢剿,看看會是什么樣的結(jié)果逢慌。

至此,我們把文中的代碼運行結(jié)果原因講了一遍间狂。不知道對大家有沒有幫助攻泼。這篇文章看起來不長,但是真的寫了很久很久。我一直在斟酌如何措辭和描述現(xiàn)象坠韩。就連標題我都不確定自己是不是起的準確距潘,很難做到盡善盡美,我大概了解代碼運行涉及到的流程只搁,但有很多細枝末節(jié)的知識點是我暫時沒法確認的音比,盡管我自己會猜測一些原因,也相信自己的猜測是正確的氢惋,但我沒有在書上或者其他地方找到確鑿的證據(jù)洞翩,不敢直接在文章中告訴大家——事實就是blabla。給大家推薦一本書 文納斯的《深入Java虛擬機》焰望,這本書我看了兩遍骚亿,重點章節(jié)翻了好幾次,不過我覺得還可以再多閱讀閱讀熊赖。周志明也寫了一本《深入理解Java虛擬機》来屠,不知道內(nèi)容怎么樣,但豆瓣上的評分也挺高的震鹉。

關(guān)于虛擬機涉及到的相關(guān)內(nèi)容俱笛,我后面還會再做輸出,如有不正確的地方传趾,還請大家批評指正迎膜,我們共同進步,謝謝~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末浆兰,一起剝皮案震驚了整個濱河市磕仅,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌簸呈,老刑警劉巖榕订,帶你破解...
    沈念sama閱讀 211,423評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異蜕便,居然都是意外死亡卸亮,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,147評論 2 385
  • 文/潘曉璐 我一進店門玩裙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人段直,你說我怎么就攤上這事吃溅。” “怎么了鸯檬?”我有些...
    開封第一講書人閱讀 157,019評論 0 348
  • 文/不壞的土叔 我叫張陵决侈,是天一觀的道長。 經(jīng)常有香客問我,道長赖歌,這世上最難降的妖魔是什么枉圃? 我笑而不...
    開封第一講書人閱讀 56,443評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮庐冯,結(jié)果婚禮上孽亲,老公的妹妹穿的比我還像新娘。我一直安慰自己展父,他們只是感情好返劲,可當我...
    茶點故事閱讀 65,535評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著栖茉,像睡著了一般篮绿。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上吕漂,一...
    開封第一講書人閱讀 49,798評論 1 290
  • 那天亲配,我揣著相機與錄音,去河邊找鬼惶凝。 笑死吼虎,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的梨睁。 我是一名探鬼主播鲸睛,決...
    沈念sama閱讀 38,941評論 3 407
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼坡贺!你這毒婦竟也來了官辈?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,704評論 0 266
  • 序言:老撾萬榮一對情侶失蹤遍坟,失蹤者是張志新(化名)和其女友劉穎拳亿,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體愿伴,經(jīng)...
    沈念sama閱讀 44,152評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡肺魁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,494評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了隔节。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鹅经。...
    茶點故事閱讀 38,629評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖怎诫,靈堂內(nèi)的尸體忽然破棺而出瘾晃,到底是詐尸還是另有隱情,我是刑警寧澤幻妓,帶...
    沈念sama閱讀 34,295評論 4 329
  • 正文 年R本政府宣布蹦误,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏强胰。R本人自食惡果不足惜舱沧,卻給世界環(huán)境...
    茶點故事閱讀 39,901評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望偶洋。 院中可真熱鬧熟吏,春花似錦、人聲如沸涡真。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽哆料。三九已至缸剪,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間东亦,已是汗流浹背杏节。 一陣腳步聲響...
    開封第一講書人閱讀 31,978評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留典阵,地道東北人奋渔。 一個月前我還...
    沈念sama閱讀 46,333評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像壮啊,于是被迫代替她去往敵國和親嫉鲸。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,499評論 2 348

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

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法歹啼,類相關(guān)的語法玄渗,內(nèi)部類的語法,繼承相關(guān)的語法狸眼,異常的語法藤树,線程的語...
    子非魚_t_閱讀 31,598評論 18 399
  • 搜索的時候看了好幾篇文,自己就想記錄一遍拓萌,加深一下記憶岁钓,以下是原文的地址,受益匪淺微王。blog.csdn.net/n...
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理屡限,服務(wù)發(fā)現(xiàn),斷路器炕倘,智...
    卡卡羅2017閱讀 134,629評論 18 139
  • 消費錢包適度花 消費是為你的衣食住行買單钧大。 把錢花在效用高的事情上 花得省不算花得值,效用高才真的值激才。 把錢花在今...
    Su公子閱讀 245評論 0 0
  • “到最后仍然是放不下那種別扭,就把這些瑣碎隨筆寫下,卻希望你別太看重瘸恼。我寫小說時有種莫名的慎重劣挫,不肯讓我的人物活得...
    小遠姑娘閱讀 349評論 0 0