Java中構(gòu)造器內(nèi)部的多態(tài)方法的行為實(shí)例分析
本文實(shí)例講述了Java中構(gòu)造器內(nèi)部的多態(tài)方法的行為操作。分享給大家供大家參考,具體如下:
這篇文章主要討論的是,若在一個(gè)構(gòu)造器中調(diào)用正在構(gòu)造的對(duì)象的某個(gè)動(dòng)態(tài)綁定的方法時(shí)會(huì)出現(xiàn)的情況。在此之前,我們需要知道構(gòu)造器是如何在復(fù)雜的層次結(jié)構(gòu)中運(yùn)作的,盡管構(gòu)造方法并不具有多態(tài)性,因?yàn)樗鼈儗?shí)際上是static方法,只不過(guò)是隱式聲明的static。
復(fù)雜層次結(jié)構(gòu)中構(gòu)造器的調(diào)用順序
基類的構(gòu)造器總是在導(dǎo)出類的構(gòu)造過(guò)程中被調(diào)用,而且按照繼承層次逐漸向上鏈接,以使每個(gè)基類的構(gòu)造器都能得到調(diào)用。這樣做是因?yàn)椋贘ava類中,我們通常將字段設(shè)置為private類型,也就是說(shuō),在子類中通常無(wú)法直接訪問(wèn)基類的字段,那么只有通過(guò)調(diào)用基類的構(gòu)造器才能對(duì)基類對(duì)象的元素進(jìn)行初始化,那么就必須保證所有的構(gòu)造器都得到調(diào)用,這樣才能正確地構(gòu)造完整的對(duì)象。下面的例1展示了包含有組合與繼承關(guān)系的各類中構(gòu)造器的調(diào)用順序:
例1:
class Meal {
Meal() { System.out.println("Meal()"); }
}
class Bread {
Bread() { System.out.println("Bread()"); }
}
class Cheese {
Cheese() { System.out.println("Cheese()"); }
}
class Lettuce {
Lettuce() { System.out.println("Lettuce()"); }
}
class Lunch extends Meal {
Lunch() { System.out.println("Lunch()"); }
}
class PortableLunch extends Lunch {
PortableLunch() { System.out.println("PortableLunch()");}
}
public class Sandwich extends PortableLunch {
private Bread b = new Bread();
private Cheese c = new Cheese();
private Lettuce l = new Lettuce();
public Sandwich() { System.out.println("Sandwich()"); }
public static void main(String[] args) {
new Sandwich();
}
}
例1反映了關(guān)于Meal、Lunch和Sandwich之間三層繼承關(guān)系(不包含Object類),以及Bread、Cheese和Lettuce與Sandwich的組合關(guān)系。在main函數(shù)中創(chuàng)建一個(gè)Sandwich對(duì)象后,我們就可以看到輸出結(jié)果:

這說(shuō)明在復(fù)雜的層次結(jié)構(gòu)中構(gòu)造器的調(diào)用遵從的順序?yàn)椋?nbsp;
** 1、調(diào)用基類構(gòu)造器。這個(gè)步驟會(huì)不斷地反復(fù)遞歸下去,首先是構(gòu)造這種層次結(jié)構(gòu)的根,然后是下一層導(dǎo)出類,等等,直到最低層的導(dǎo)出類;
2、按聲明的順序調(diào)用成員的初始化方法;
3、調(diào)用導(dǎo)出類構(gòu)造器的主體。**
構(gòu)造器內(nèi)部的多態(tài)方法的行為
那么,現(xiàn)在我們回到文章開(kāi)頭提到的問(wèn)題,若在一個(gè)構(gòu)造器中調(diào)用正在構(gòu)造的對(duì)象的某個(gè)動(dòng)態(tài)綁定的方法,會(huì)出現(xiàn)什么情況呢?我們知道,動(dòng)態(tài)綁定(或后期綁定)的方法的調(diào)用是在運(yùn)行時(shí)才決定的,因?yàn)閷?duì)象在程序運(yùn)行之前無(wú)從得知它自己到底是基類的對(duì)象,還是某個(gè)導(dǎo)出類的對(duì)象。如果在基類的構(gòu)造器內(nèi)部調(diào)用某個(gè)動(dòng)態(tài)綁定方法,該方法是被導(dǎo)出類覆蓋的,那么這便可能產(chǎn)生難以預(yù)料的后果,因?yàn)樵搶?dǎo)出類的對(duì)象還未被完全構(gòu)造,但它的方法卻被調(diào)用了。我們可以通過(guò)例2看到問(wèn)題所在:
例2:
class Glyph {
void draw() { System.out.println("Glyph.draw()"); }
Glyph() {
System.out.println("Glyph() before draw()");
draw();
System.out.println("Glyph() after draw()");
}
}
class RoundGlyph extends Glyph {
private int radius = 1;
RoundGlyph(int r) {
radius = r;
System.out.println("RoundGlyph.RoundGlyph(), radius = " + radius);
}
void draw() {
System.out.println("RoundGlyph.draw(), radius = " + radius);
}
}
public class PolyConstructors {
public static void main(String[] args) {
new RoundGlyph(5);
}
}
運(yùn)行結(jié)果:

在運(yùn)行結(jié)果中,我們看到,基類Glyph的構(gòu)造器中調(diào)用了被子類RoundGlyph覆蓋的draw()方法,并且輸出了radius=0,這顯然是一個(gè)錯(cuò)誤,因?yàn)檫@個(gè)“0”是在其他任何事物發(fā)生之前,系統(tǒng)分配給對(duì)象的存儲(chǔ)空間的初始值——二進(jìn)制的零,而非我們想要設(shè)定的初始值“1”。這是因?yàn)?,我們?cè)趧?chuàng)建子類(RoundGlyph)對(duì)象時(shí)會(huì)先調(diào)用基類(Glyph)的構(gòu)造器構(gòu)造基類對(duì)象,而在基類的構(gòu)造器中卻調(diào)用了被子類覆蓋的動(dòng)態(tài)綁定的方法(draw()),而這個(gè)方法所操縱的可能是子類中的還未進(jìn)行初始化的成員(radius),這便會(huì)招致災(zāi)難,盡管編譯器并沒(méi)有報(bào)錯(cuò)。
因此,在編寫構(gòu)造器中有一條有效的準(zhǔn)則:“用盡可能簡(jiǎn)單的方法使對(duì)象進(jìn)入正常狀態(tài);如果可以的話,避免調(diào)用其他方法”。在構(gòu)造器中,唯一能夠安全調(diào)用的是基類中的final方法(包括private方法),因?yàn)檫@些方法不能被子類覆蓋,也就不會(huì)出現(xiàn)上述的問(wèn)題。
更多java相關(guān)內(nèi)容感興趣的讀者可查看本站專題:《Java面向?qū)ο蟪绦蛟O(shè)計(jì)入門與進(jìn)階教程》、《Java數(shù)據(jù)結(jié)構(gòu)與算法教程》、《Java操作DOM節(jié)點(diǎn)技巧總結(jié)》、《Java文件與目錄操作技巧匯總》和《Java緩存操作技巧匯總》
希望本文所述對(duì)大家java程序設(shè)計(jì)有所幫助。
相關(guān)文章
jmeter中json提取器如何提取多個(gè)參數(shù)值
關(guān)于jmeter中的正則表達(dá)式及json提取器可以提取響應(yīng)值,但是實(shí)際可以需要上個(gè)接口的多個(gè)響應(yīng)值,本文就詳細(xì)的介紹一下如何使用,感興趣的可以了解一下2021-11-11
Java使用Apache POI庫(kù)讀取Excel表格文檔的示例
POI庫(kù)是Apache提供的用于在Windows下讀寫各類微軟Office文檔的Java庫(kù),這里我們就來(lái)看一下Java使用Apache POI庫(kù)讀取Excel表格文檔的示例:2016-06-06
Springboot+WebSocket實(shí)現(xiàn)一對(duì)一聊天和公告的示例代碼
這篇文章主要介紹了Springboot+WebSocket實(shí)現(xiàn)一對(duì)一聊天和公告的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04
Java將List轉(zhuǎn)換為String的幾種方式
我們大家在實(shí)際開(kāi)發(fā)中經(jīng)常遇到List轉(zhuǎn)為String字符串的情況,下面這篇文章主要給大家介紹了關(guān)于Java將List轉(zhuǎn)換為String的幾種方式,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-05-05
springcloud Zuul動(dòng)態(tài)路由的實(shí)現(xiàn)
這篇文章主要介紹了springcloud Zuul動(dòng)態(tài)路由的實(shí)現(xiàn),詳細(xì)的介紹了什么是Zuu及其動(dòng)態(tài)路由的實(shí)現(xiàn),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-11-11

