自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Java的聲明和初始化:細(xì)看OO程序執(zhí)行的順序

開發(fā) 后端
本文通過(guò)一個(gè)Base和Derived類的實(shí)例,介紹了在Java的聲明和初始化過(guò)程中的程序執(zhí)行順序。在面向?qū)ο蟮氖澜缰校绦驁?zhí)行的順序相當(dāng)?shù)闹匾?/div>

在介紹Java的聲明和初始化的執(zhí)行順序之前,讓我們先來(lái)看兩個(gè)類:Base和Derived類。注意其中的whenAmISet成員變量,和方法preProcess()

  1. public class Base  
  2. {  
  3.     Base() {  
  4.         preProcess();  
  5.     }  
  6.  
  7.     void preProcess() {}  
  1. public class Derived extends Base  
  2. {  
  3.    public String whenAmISet = "set when declared";  
  4.  
  5.    @Override void preProcess()  
  6.    {  
  7.        whenAmISet = "set in preProcess()";  
  8.    }  

如果我們構(gòu)造一個(gè)子類實(shí)例,那么,whenAmISet 的值會(huì)是什么呢?

  1. public class Main  
  2. {  
  3.    public static void main(String[] args)  
  4.    {  
  5.        Derived d = new Derived();  
  6.        System.out.println( d.whenAmISet );  
  7.    }  

再續(xù)繼往下閱讀之前,請(qǐng)先給自己一些時(shí)間想一下上面的這段程序的輸出是什么?是的,這看起來(lái)的確相當(dāng)簡(jiǎn)單,甚至不需要編譯和運(yùn)行上面的代碼,我們也應(yīng)該知道其答案,那么,你覺得你知道答案嗎?你確定你的答案正確嗎?

很多人都會(huì)覺得那段程序的輸出應(yīng)該是“set in preProcess()”,這是因?yàn)楫?dāng)子類Derived 的構(gòu)造函數(shù)被調(diào)用時(shí),其會(huì)隱晦地調(diào)用其基類Base的構(gòu)造函數(shù)(通過(guò)super()函數(shù)),于是基類Base的構(gòu)造函數(shù)會(huì)調(diào)用preProcess() 函數(shù),因?yàn)檫@個(gè)類的實(shí)例是Derived的,而且在子類Derived中對(duì)這個(gè)函數(shù)使用了override關(guān)鍵字,所以,實(shí)際上調(diào)用到的是:Derived.preProcess(),而這個(gè)方法設(shè)置了whenAmISet 成員變量的值為:“set in preProcess()”。

當(dāng)然,上面的結(jié)論是錯(cuò)誤的。如果你編譯并運(yùn)行這個(gè)程序,你會(huì)發(fā)現(xiàn),程序?qū)嶋H輸出的是“set when declared ”。怎么為這樣呢?難道是基類Base 的preProcess() 方法被調(diào)用啦?也不是!你可以在基類的preProcess中輸出點(diǎn)什么看看,你會(huì)發(fā)現(xiàn)程序運(yùn)行時(shí),Base.preProcess()并沒有被調(diào)用到(不然這對(duì)于Java所有的應(yīng)用程序?qū)?huì)是一個(gè)***災(zāi)難性的Bug)。

雖然上面的結(jié)論是錯(cuò)誤的,但推導(dǎo)過(guò)程是合理的,只是不完整,下面是整個(gè)運(yùn)行的流程:

◆進(jìn)入Derived 構(gòu)造函數(shù)。

◆Derived 成員變量的內(nèi)存被分配。

◆Base 構(gòu)造函數(shù)被隱含調(diào)用。

◆Base 構(gòu)造函數(shù)調(diào)用preProcess()。

◆Derived 的preProcess 設(shè)置whenAmISet 值為 “set in preProcess()”。

◆Derived 的成員變量初始化被調(diào)用。

◆執(zhí)行Derived 構(gòu)造函數(shù)體。

等一等,這怎么可能?在第6步,Derived 成員的初始化居然在 preProcess() 調(diào)用之后?是的,正是這樣,我們不能讓成員變量的聲明和初始化變成一個(gè)原子操作,雖然在Java中我們可以把其寫在一起,讓其看上去像是聲明和初始化一體。但這只是假象,我們的錯(cuò)誤就在在我們把Java的聲明和初始化看成了一體。在C++的世界中,C++并不支持成員變量在聲明的時(shí)候進(jìn)行初始化,其需要你在構(gòu)造函數(shù)中顯式的初始化其成員變量的值,看起來(lái)很土,但其實(shí)C++用心良苦。

在面向?qū)ο蟮氖澜缰?,因?yàn)槌绦蛞詫?duì)象的形式出現(xiàn),導(dǎo)致了我們對(duì)程序執(zhí)行的順序霧里看花。所以,在面向?qū)ο蟮氖澜缰?,程序?zhí)行的順序相當(dāng)?shù)闹匾?/P>

下面是對(duì)上面各個(gè)步驟的逐條解釋。

◆進(jìn)入構(gòu)造函數(shù)。

◆為成員變量分配內(nèi)存。

◆除非你顯式地調(diào)用super(),否則Java 會(huì)在子類的構(gòu)造函數(shù)最前面偷偷地插入super() 。

◆調(diào)用父類構(gòu)造函數(shù)。

◆調(diào)用preProcess,因?yàn)楸蛔宇恛verride,所以調(diào)用的是子類的。

◆于是,初始化發(fā)生在了preProcess()之后。這是因?yàn)椋琂ava需要保證父類的初始化早于子類的成員初始化,否則,在子類中使用父類的成員變量就會(huì)出現(xiàn)問(wèn)題。

◆正式執(zhí)行子類的構(gòu)造函數(shù)(當(dāng)然這是一個(gè)空函數(shù),居然我們沒有聲明)。

你可以查看《Java語(yǔ)言的規(guī)格說(shuō)明書》中的 相關(guān)章節(jié) 來(lái)了解更多的Java創(chuàng)建對(duì)象時(shí)的細(xì)節(jié)。

***,需要向大家推薦一本書,Joshua Bloch 和 Neal Gafter 寫的 Java Puzzlers: Traps, Pitfalls, and Corner Cases,中文版《JAVA解惑》。

【編輯推薦】

  1. Java程序員面試必備的32個(gè)要點(diǎn)
  2. 可能不再有Java SE 7?甲骨文面臨Java許可問(wèn)題
  3. Java未來(lái)的三大謎題:再談甲骨文收購(gòu)Sun
  4. 淺談Java線程的生命周期
  5. 關(guān)于Java繼承的一些復(fù)習(xí)
責(zé)任編輯:yangsai 來(lái)源: 酷殼
相關(guān)推薦

2012-02-28 10:04:09

Java

2009-06-11 13:26:16

Java數(shù)組聲明創(chuàng)建

2013-03-04 11:10:03

JavaJVM

2010-07-28 10:22:33

FlexApplica

2009-10-20 14:03:48

VB.NET數(shù)組聲明VB.NET數(shù)組初始化

2011-03-23 15:02:55

ListenerFilterServlet

2012-03-13 13:38:42

Java

2022-01-04 19:33:03

Java構(gòu)造器調(diào)用

2011-06-09 14:13:06

C++JAVA缺省初始化

2021-04-07 08:03:51

js舉起Hoisting初始化

2011-07-22 17:46:43

java

2012-05-23 12:46:53

JavaJava類

2015-08-14 14:31:57

Java初始化面試題

2015-10-30 09:51:19

Java重寫初始化隱患

2019-11-04 13:50:36

Java數(shù)組編程語(yǔ)言

2009-08-26 18:28:44

C#數(shù)組

2011-06-17 15:29:44

C#對(duì)象初始化器集合初始化器

2011-03-16 10:52:20

2009-06-10 16:17:00

Netbeans JT初始化

2021-07-07 05:00:17

初始化源碼
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)