新聞中心
對(duì)象實(shí)例何時(shí)被創(chuàng)建,這個(gè)問(wèn)題也許你用一句話就能回答完了。但是它的潛在陷阱卻常常被人忽視,這個(gè)問(wèn)題也許并不像你想的那么簡(jiǎn)單,不信請(qǐng)你耐心看下去。

員工經(jīng)過(guò)長(zhǎng)期磨合與沉淀,具備了協(xié)作精神,得以通過(guò)團(tuán)隊(duì)的力量開(kāi)發(fā)出優(yōu)質(zhì)的產(chǎn)品。成都創(chuàng)新互聯(lián)堅(jiān)持“專(zhuān)注、創(chuàng)新、易用”的產(chǎn)品理念,因?yàn)椤皩?zhuān)注所以專(zhuān)業(yè)、創(chuàng)新互聯(lián)網(wǎng)站所以易用所以簡(jiǎn)單”。公司專(zhuān)注于為企業(yè)提供網(wǎng)站制作、成都做網(wǎng)站、微信公眾號(hào)開(kāi)發(fā)、電商網(wǎng)站開(kāi)發(fā),重慶小程序開(kāi)發(fā)公司,軟件按需網(wǎng)站開(kāi)發(fā)等一站式互聯(lián)網(wǎng)企業(yè)服務(wù)。
我前幾天問(wèn)一個(gè)同學(xué),是不是在調(diào)用構(gòu)造函數(shù)后,對(duì)象才被實(shí)例化?他不假思索的回答說(shuō)是。
請(qǐng)看下面代碼:
Java代碼
- Date date=new Date();
- em.out.println(date.getTime());
新手在剛接觸構(gòu)造函數(shù)這個(gè)概念的時(shí)候。他們常常得出這樣的結(jié)論:對(duì)象實(shí)例是在調(diào)用構(gòu)造函數(shù)后創(chuàng)建的。因?yàn)檎{(diào)用構(gòu)造函數(shù)后,調(diào)用引用(date)的實(shí)例方法便不會(huì)報(bào)NullPointerException的錯(cuò)誤了。
二、經(jīng)驗(yàn)者的觀點(diǎn)
然而,稍稍有經(jīng)驗(yàn)的Java程序員便會(huì)發(fā)現(xiàn)上面的解釋并不正確。這點(diǎn)從構(gòu)造函數(shù)中我們可以調(diào)用this關(guān)鍵字可以看出。
請(qǐng)看下面代碼:
Java代碼
- public class Test
- {
- public Test()
- {
- this.DoSomething();
- }
- private void DoSomething()
- {
- System.out.println("do init");
- }
- }
這段代碼中我們?cè)跇?gòu)造函數(shù)中已經(jīng)可以操作對(duì)象實(shí)例。這也就證明了構(gòu)造函數(shù)其實(shí)只是用于初始化,早在進(jìn)入構(gòu)造函數(shù)之前。對(duì)象實(shí)例便已經(jīng)被創(chuàng)建了。
三、父類(lèi)構(gòu)造函數(shù)
當(dāng)創(chuàng)建一個(gè)有父類(lèi)的子類(lèi)的時(shí)候。對(duì)象的實(shí)例又是何時(shí)被創(chuàng)建的呢?我們也許接觸過(guò)下面經(jīng)典的代碼:
Java代碼
- public class BaseClass
- {
- public BaseClass()
- {
- System.out.println("create base");
- }
- }
- public class SubClass
- {
- public SubClass()
- {
- System.out.println("create sub");
- }
- public static void main(String[] args)
- {
- new SubClass();
- }
- }
結(jié)果是先輸出create base,后輸出create sub。這個(gè)結(jié)果看起來(lái)和現(xiàn)實(shí)世界完全一致,先有老爸,再有兒子。因此我相信有很多程序員跟我一樣會(huì)認(rèn)為new SubClass()的過(guò)程是:實(shí)例化BaseClass->調(diào)用BaseClass構(gòu)造函數(shù)初始化->實(shí)例化SubClass->調(diào)用SubClass構(gòu)造函數(shù)初始化。然而非常不幸的是,這是個(gè)錯(cuò)誤的觀點(diǎn)。
四、奇怪的代碼
以下代碼是為了駁斥上面提到的錯(cuò)誤觀點(diǎn)。但是這種代碼其實(shí)在工作中甚少出現(xiàn)。
Java代碼
- public class BaseClass
- {
- public BaseClass()
- {
- System.out.println("create base");
- init();
- }
- protected void init() {
- System.out.println("do init");
- }
- }
- //
- public class SubClass
- {
- public SubClass()
- {
- System.out.println("create sub");
- }
- @Override
- protected void init()
- {
- assert this!=null;
- System.out.println("now the working class is:"+this.getClass().getSimpleName());
- System.out.println("in SubClass");
- }
- public static void main(String[] args)
- {
- new SubClass();
- }
- }
這段代碼運(yùn)行的結(jié)果是先調(diào)用父類(lèi)的構(gòu)造函數(shù),再調(diào)用子類(lèi)的init()方法,再調(diào)用子類(lèi)的構(gòu)造函數(shù)。
這是一段奇妙的代碼,子類(lèi)的構(gòu)造函數(shù)居然不是子類(lèi)***個(gè)被執(zhí)行的方法。我們?cè)缫蚜?xí)慣于通過(guò)super方便的調(diào)用父類(lèi)的方法,但是好像從沒(méi)這樣嘗試從父類(lèi)調(diào)用子類(lèi)的方法。
再次聲明,這只是個(gè)示例。是為了與您一起探討對(duì)象實(shí)例化的秘密。通過(guò)這個(gè)示例,我們?cè)俅斡∽C了開(kāi)頭的觀點(diǎn):早在構(gòu)造函數(shù)被調(diào)用之前,實(shí)例便已被創(chuàng)造。若該對(duì)象有父類(lèi),則早在父類(lèi)的構(gòu)造函數(shù)被調(diào)用之前,實(shí)例也已被創(chuàng)造。這讓java顯得有些不面向?qū)ο螅瓉?lái)老子兒子其實(shí)是一塊兒出生的。
五、奇怪但危險(xiǎn)的代碼
本篇是對(duì)上篇奇怪代碼的延續(xù)。但是這段代碼更加具有疑惑性,理解不當(dāng)將會(huì)讓你出現(xiàn)致命失誤。
請(qǐng)看下面代碼:
Java代碼
- public class BaseClass {
- public BaseClass()
- {
- System.out.println("create base");
- init();
- }
- protected void init() {
- System.out.println("in base init");
- }
- }
- public class SubClass extends BaseClass{
- int i=1024;
- String s="13 leaf";
- public SubClass()
- {
- System.out.println("create sub");
- init();
- }
- @Override
- protected void init() {
- assert this!=null;
- System.out.println("now the working class is:"+this.getClass().getSimpleName());
- System.out.println("in SubClass");
- /////////////great line/////////////////
- System.out.println(i);
- System.out.println(s);
- }
- public static void main(String[] args) {
- new SubClass();
- //oh!my god!!
- }
- }
這段代碼相比上一篇,只是在子類(lèi)中添加了一些成員變量。而我們的目標(biāo)正是集中在討論成員變量初始化的問(wèn)題上。
這段代碼的執(zhí)行順序是:父類(lèi)、子類(lèi)實(shí)例化->調(diào)用父類(lèi)構(gòu)造函數(shù)->調(diào)用子類(lèi)init()方法->調(diào)用子類(lèi)構(gòu)造函數(shù)->調(diào)用子類(lèi)init()方法。最終的輸出結(jié)果向我們揭示了成員變量初始化的秘密。
當(dāng)父類(lèi)構(gòu)造函數(shù)調(diào)用子類(lèi)的init()方法的時(shí)候。子類(lèi)的成員變量統(tǒng)統(tǒng)是空的,這個(gè)空是指的低級(jí)初始化。(值類(lèi)型為0,布爾類(lèi)型為false,引用類(lèi)型為null)。而當(dāng)子類(lèi)構(gòu)造函數(shù)調(diào)用init()方法的時(shí)候,成員變量才真正被初始化。這是一個(gè)危險(xiǎn)的訊息,那就是使用父類(lèi)構(gòu)造函數(shù)調(diào)用子類(lèi)時(shí)存在成員變量未初始化的風(fēng)險(xiǎn)。
我們的討論也到此為止了。再次回顧,總結(jié)一下實(shí)例何時(shí)被創(chuàng)建這個(gè)問(wèn)題。我得出了以下結(jié)論:
本文到此便結(jié)束了。鑒于本人才疏學(xué)淺,若是專(zhuān)業(yè)術(shù)語(yǔ)有錯(cuò)誤,或是哪里講的不對(duì),也歡迎各位高手拍磚。
附上第五篇中SubClass的部分字節(jié)碼,方便大家深入理解:
Java代碼
- public SubClass();
- aload_0 [this] //aload_0是啥?
- invokespecial ques.BaseClass() [26] //調(diào)用父類(lèi)構(gòu)造函數(shù)
- aload_0 [this]
- sipush 1024 //初始化i成員變量
- putfield ques.SubClass.i : int [28]
- aload_0 [this]
- ldc "13 leaf"> [30] //初始化s成員變量
- putfield ques.SubClass.s : java.lang.String [32]
- getstatic java.lang.System.out : java.io.PrintStream [34]
- ldc "create sub"> [40]
- invokevirtual java.io.PrintStream.println(java.lang.String) : void [42]
- aload_0 [this]
- invokevirtual ques.SubClass.init() : void [48] //調(diào)用init
- return
網(wǎng)站名稱(chēng):對(duì)象實(shí)例是何時(shí)被創(chuàng)建的?
轉(zhuǎn)載源于:http://www.5511xx.com/article/djdojop.html


咨詢(xún)
建站咨詢(xún)
