我們知道,繼承是java設(shè)計(jì)里的一個(gè)失敗的地方。高司令說過:如果讓他重新設(shè)計(jì)java的話,他會(huì)把繼承去掉。而java里繼承到底怎么了,會(huì)這么不受人歡迎呢?
我們知道,繼承設(shè)計(jì)的初衷是為了代碼復(fù)用。在C++里也確實(shí)做到了這一點(diǎn),因?yàn)镃++允許多重繼承。特別是C++里的析構(gòu)函數(shù),申明為了virtual的時(shí)候可以多重復(fù)用,用起來也很舒服。
那么為什么在java里繼承就這么讓人詬病呢?
第一:java里的類不能多重繼承,如果一個(gè)類一旦繼承了它的父類,那么它就別想再繼承別的類。一個(gè)兒子只能有一個(gè)老爸,原本設(shè)計(jì)是沒錯(cuò)的??墒窃趯?shí)際應(yīng)用中,就會(huì)出現(xiàn)各種問題。
第二:java里有更好的接口,接口從本質(zhì)上來說可以更好的代替繼承。實(shí)現(xiàn)像C++里一樣的宏定義的功能,把你想要的東西都抽象在接口里,讓具體的類去實(shí)現(xiàn)這些功能。
如果你去面試過,肯定遇到過不少考察java繼承機(jī)制的題目,其中最顯著的一個(gè)就是構(gòu)造函數(shù)的調(diào)用和重寫方法的調(diào)用。
這里我再強(qiáng)調(diào)一下重載和重寫:
重載是同一個(gè)類里面相同方法名,不同參數(shù)類型或個(gè)數(shù)的方法。這也是C++類里面為什么出現(xiàn)函數(shù)模板的原因,就拿求和來說:
int add(int a,int b);
long add(long a,long b);
int main(){
}
因?yàn)榍蠛涂赡苁乔骾nt的和,也可能是求long的和,這個(gè)時(shí)候就出現(xiàn)了需求(我始終認(rèn)為,一種東西的出現(xiàn)必定是因?yàn)閷?duì)他的需求)
而重寫呢,就是子類對(duì)父類里的方法的重改,就是他改寫了父類的方法。倫理上看似不孝,而道理上是事物總是要不斷發(fā)展的。這就像是社會(huì)的改革一樣。
下面我來分析一下父類和子類的構(gòu)造函數(shù)調(diào)用順序:
在內(nèi)存機(jī)制中,父類和子類是占用同一塊內(nèi)存的,只不過子類在父類的基礎(chǔ)上增加了自己的部分(包括數(shù)據(jù)成員和屬性),這樣一來就好理解了。子類是依附于父類的,先有父類再有子類。所以說一個(gè)子類對(duì)象的產(chǎn)生,必須先調(diào)用父類的構(gòu)造函數(shù)產(chǎn)生一個(gè)父類實(shí)例,然后在這個(gè)實(shí)例基礎(chǔ)上添加自己的部分。
而實(shí)際的運(yùn)行機(jī)制,也正是這樣的。
因?yàn)檫@樣就很容易理解了,先調(diào)用父類的構(gòu)造函數(shù),再調(diào)用子類的構(gòu)造函數(shù)。
而對(duì)于父類和子類里重寫的方法的調(diào)用,關(guān)鍵要看:子類到底是否產(chǎn)生,如果子類產(chǎn)生了,子類改寫了父類的方法,看似父類和子類各自有一個(gè)方法,其實(shí)它們?cè)趦?nèi)存模型里占用的是同一塊內(nèi)存,子類方法會(huì)覆蓋父類方法。
我們看下面的程序:
class SuperStringTest {
SuperStringTest(){
System.out.println("Father is constructed.");
}
public void test(){
System.out.println("Father is running.");
}
}
public class StringTest extends SuperStringTest{
StringTest(){
System.out.println("Son is constructed.");
}
public void test(){
System.out.println("Son is running.");
}
public static void main(String[] args){
/*
SuperStringTest sst = new StringTest();
此時(shí),派生類的方法覆蓋了基類的方法,基類的方法對(duì)派生類來說為不可見(有先像作用域),也就是派生類里的同名方法重新寫了基類的同名方法。此時(shí),對(duì)基類和派生類來說只有被派生類改寫后的唯一的一個(gè)方法。所以,只能調(diào)用派生類的方法。
Father is constructed.
Son is constructed.
Son is running.
*/
/*
StringTest sst = new StringTest();
解釋同上面
Father is constructed.
Son is constructed.
Son is running.
*/
/*
SuperStringTest sst = new SuperStringTest();
此時(shí),基類所占的內(nèi)存單元中并沒有派生類的東西。所以,方法沒被改寫,調(diào)用父類的方法。
Father is constructed.
Father is running.
*/
sst.test();
}
}
聯(lián)系客服