學(xué)習(xí)一個(gè)新知識(shí)的第一步,就是要知道它是什么帽借,然后要知道為什么要用它珠增,最后要知道如何使用它。這篇文章砍艾,我們重新認(rèn)識(shí)一下java中的繼承蒂教。
繼承是個(gè)什么東西
我們先來看一下上一篇文章中的代碼:
你會(huì)發(fā)現(xiàn),這兩個(gè)類中都有name屬性脆荷,都有marry方法凝垛。一個(gè)人,不可能只有名字吧简烘。他還有年齡苔严,地址,手機(jī)號碼孤澎,身份證號碼届氢,身高,體重巴拉巴拉的覆旭。除了男人和女人退子,還有小孩,老人型将,教師寂祥。。七兜。
如果我們每個(gè)類里都寫一遍name丸凭,age。腕铸。惜犀。也許你還沒寫完程序,自己就先累死了狠裹。不用我說虽界,大家也應(yīng)該知道了,沒錯(cuò)涛菠,我們需要繼承的幫助莉御。
我們把相同的屬性抽取出來撇吞,定義一個(gè)新的類Person,然后讓男人礁叔,女人都去繼承它牍颈,從而獲得Person的屬性,這樣晴圾,就大大簡化了我們的工作颂砸。
我們來嘗試一下。
//父類
public class Person {
protected String name;
protected int age;
public void eat(){
System.out.println("i am eating");
}
}
//Man類 繼承Person
public class Man extends Person {
private boolean hasBeard;
public void showMan(){
System.out.println("i am a man");
}
public boolean isHasBeard() {
return hasBeard;
}
}
//woman類 繼承Person
public class Woman extends Person{
private boolean hasLongHair;
public void shouWoman(){
System.out.println("i am a woman");
}
public boolean isHasLongHair() {
return hasLongHair;
}
}
簡單的繼承我想大家都懂死姚,我就不多說占用篇幅了。
繼承的特點(diǎn)
我們已經(jīng)知道了什么是繼承勤篮,那么繼承有沒有什么限制呢都毒?
1.java中只支持單繼承
也就是說,一個(gè)類只能夠有一個(gè)父類碰缔。但是java支持“多重繼承”账劲。
單繼承:
class A(){}
class B extends A (){}
多重繼承:
class A{}
class B extends A {}
class C extends B {}
為什么java不支持多繼承呢?因?yàn)槿菀自斐刹槐匾幕靵y金抡。比如說:
- 結(jié)構(gòu)復(fù)雜化:如果是單一繼承瀑焦,一個(gè)類的父類是什么,父類的父類是什么梗肝,都很明確榛瓮,因?yàn)橹挥袉我坏睦^承關(guān)系,然而如果是多重繼承的話巫击,一個(gè)類有多個(gè)父類禀晓,這些父類又有自己的父類,那么類之間的關(guān)系就很復(fù)雜了坝锰。
- 優(yōu)先順序模糊:假如我有A粹懒,C類同時(shí)繼承了基類,B類繼承了A類顷级,然后D類又同時(shí)繼承了B和C類凫乖,所以D類繼承父類的方法的順序應(yīng)該是D、B弓颈、A帽芽、C還是D、B恨豁、C嚣镜、A,或者是其他的順序橘蜜,很不明確菊匿。
- 功能沖突:因?yàn)槎嘀乩^承有多個(gè)父類付呕,所以當(dāng)不同的父類中有相同的方法是就會(huì)產(chǎn)生沖突。如果B類和C類同時(shí)又有相同的方法時(shí)跌捆,D繼承的是哪個(gè)方法就不明確了徽职,因?yàn)榇嬖趦煞N可能性。
當(dāng)然佩厚,多繼承的這些問題很多語言已經(jīng)解決了姆钉,比如c++,python等抄瓦,但并不是所有的語言都有必要去解決這個(gè)問題潮瓶。java的類雖然不能實(shí)現(xiàn)多繼承,但是java的接口支持多實(shí)現(xiàn)钙姊,這個(gè)我們講到接口的時(shí)候再說毯辅。
對多繼承感興趣的可以google一下mixin(混入),還可以去看一下基于java8的mixin實(shí)現(xiàn)(大多數(shù)都是線程不安全的煞额,不要隨便用)思恐。
2.子類擁有父類非private的屬性,方法
也就是說膊毁,父類的屬性或者方法如果是peivate的胀莹,那么子類是不能繼承它的。講到這里婚温,就必須得提一下四個(gè)修飾符了:
---- | 本類 | 同包(無關(guān)類或子類) | 不同包(子類) | 不同包(無關(guān)類) |
---|---|---|---|---|
private | ? | |||
default | ? | ? | ||
protected | ? | ? | ? | |
public | ? | ? | ? | ? |
在java中描焰,protected關(guān)鍵字大展身手的地方就是在繼承中$哉伲《thinking in java》中是這樣介紹protected的:
在理想世界中栈顷,僅靠關(guān)鍵字private已經(jīng)足夠了。但在實(shí)際項(xiàng)目中嵌巷,經(jīng)常會(huì)想要將某些事物盡可能堆這個(gè)世界隱藏起來萄凤,但仍然允許導(dǎo)出的類的成員訪問他們。關(guān)鍵字protected就是起這個(gè)作用的搪哪。它指明”就類用戶而言靡努,這是privated,但是對于任何一個(gè)繼承于此類的導(dǎo)出類或其他任何一個(gè)位于同一個(gè)包內(nèi)的類來說晓折,他卻是可以訪問的”
怎么理解呢惑朦?寫個(gè)代碼你就明白了
package cn.pkgA
class A {
protected String name;
}
class B extends A{}
class C {
B b = new B();
b.name;//可以訪問到
}
package cn.pkgB
class C {
B b = new B();
b.name;//訪問不到
}
3.子類可以擁有自己的屬性和方法漓概,即子類可以對父類進(jìn)行擴(kuò)展漾月。
如果子類只能有父類的屬性和方法,那要子類還有什么用胃珍?梁肿?
4.子類可以用自己的方式實(shí)現(xiàn)父類的方法蜓陌。
這個(gè)叫做函數(shù)重寫(覆蓋),我們一會(huì)會(huì)重點(diǎn)分析吩蔑。
構(gòu)造器
除了被peivate修飾的方法和變量之外钮热,父類的構(gòu)造器也不能被子類繼承。
但是父類的構(gòu)造器帶有參數(shù)的烛芬,則必須在子類的構(gòu)造器中顯式地通過super關(guān)鍵字調(diào)用父類的構(gòu)造器并配以適當(dāng)?shù)漠?dāng)屬列表隧期。
如果父類有無參構(gòu)造器,則在子類的構(gòu)造器中用super調(diào)用父類構(gòu)造器不是必須的赘娄,如果沒有使用super關(guān)鍵字仆潮,系統(tǒng)會(huì)自動(dòng)調(diào)用父類的無參構(gòu)造器。
我們給Person類添加一個(gè)構(gòu)造器:
public class Person {
protected String name;
protected int age;
public void eat(){
System.out.println("i am eating");
}
//帶參數(shù)的構(gòu)造器
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
這個(gè)時(shí)候擅憔,如果你不給子類添加構(gòu)造器并在第一行寫入super(name鸵闪,age),則會(huì)報(bào)錯(cuò):
在子類添加如下方法后錯(cuò)誤消失:
public Man(String name, int age/*, boolean hasBeard*/) {
super(name, age);
//this.hasBeard = hasBeard;
}
//注釋掉的地方可有可無
為什么會(huì)有這個(gè)要求呢暑诸?你一會(huì)就知道了,先賣個(gè)關(guān)子辟灰。
重寫與重載
重寫
重寫又叫覆蓋个榕,發(fā)生在繼承關(guān)系下的子類中。我們上面說過芥喇,子類可以用自己的方式實(shí)現(xiàn)父類的方法西采,重寫不能改變參數(shù)列表,也不能縮小方法的訪問權(quán)限继控,如果父類方法拋出異常械馆,子類拋出的異常不能比父類的異常“大”武通,也不能拋出新的異常霹崎。
我們Person類中有一個(gè)方法:
public void eat(){
System.out.println("i am eating");
}
有一個(gè)子類修道成仙了,不吃飯冶忱,于是他可以在他自己的類里這樣改
@Override //這個(gè)是注解尾菇,表明這個(gè)方法是重寫了父類的方法,最好寫上
public void eat(){
System.out.println("i don't eat");
}
這里提一下囚枪,子類重寫父類方法不能縮小父類方法的訪問權(quán)限但擴(kuò)大是可以的派诬。比如說父類有一個(gè)protected方法,子類重寫它的時(shí)候不能改為private链沼,但是可以改成public默赂。
這里還有一個(gè)不大不小的坑。如果你的父類方法是peivate的括勺,比如:
private void eat(){
System.out.println("i am eating");
}
你可以在子類中這樣寫:
public void eat(){
System.out.println("i don't eat");
}
但是缆八,這不是重寫G!R铩蜈缤!因?yàn)楦割惙椒ㄊ撬接械模宰宇惛緵]有得到eat()這個(gè)方法冯挎,子類的eat()方法是你重新定義的一個(gè)和父類沒有半毛錢的函數(shù)底哥。
重載
把重載放到這里講只是因?yàn)樗椭貙懹械娜簧瞪捣植磺宄剌d和繼承沒有任何關(guān)系(當(dāng)然房官,繼承之間也存在重載趾徽,也就是說,繼承可以重載翰守,但是重載不一定繼承)孵奶,它發(fā)生在類本身。重載方法的特點(diǎn)是方法名相同而參數(shù)列表不同蜡峰。
比如這樣:
public void count(int a , int b){
System.out.println("a+b=" + (a+b));
}
public void count(int a , int b,int c){
System.out.println("a+b=" + (a+b+c));
}
public void count(int a , int b ,double c){
System.out.println("a+b=" + (a+b+c));
}
函數(shù)重載的特點(diǎn):
- 被重載的方法必須改變參數(shù)列表(參數(shù)個(gè)數(shù)或類型或順序不一樣)了袁;
- 被重載的方法可以改變返回類型;
- 被重載的方法可以改變訪問修飾符湿颅;
- 被重載的方法可以聲明新的或更廣的檢查異常(區(qū)別于重寫)载绿;
- 方法能夠在同一個(gè)類中或者在一個(gè)子類中被重載。
注意:參數(shù)列表必須不同油航!
繼承的缺點(diǎn)
- 繼承是一種強(qiáng)耦合關(guān)系崭庸,父類變,子類就必須變谊囚。
- 繼承破壞了封裝怕享,對于父類而言,它的實(shí)現(xiàn)細(xì)節(jié)對與子類來說都是透明的镰踏。
提醒函筋!慎用繼承!
如果你知道高手寫代碼都想著怎么解耦你就知道這個(gè)缺點(diǎn)室友多么討厭了余境。
你可能會(huì)問驻呐,我不用繼承用什么?別急芳来,接下來的幾篇文章會(huì)告訴你含末。
昨天的遺留問題
看了上一篇文章的人可能還記得那個(gè)遺留問題。我們現(xiàn)在來解決一下:
//父類
public class Person {
protected String name;
public void marry(Person p){
System.out.println("marry");
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
//man類
public class Man extends Person {
private Woman wife;
private double money;
@Override
public void marry(Person p) {
this.wife = (Woman)p;
p.marry(this);
}
//只有自己和妻子可以用錢
public void setMoney(Person p,double money) {
if (p == this || p == this.wife)
this.money = money;
else
System.out.println(p.getName()+"搶錢即舌!");
}
public double getMoney() {
return money;
}
}
//woman類
public class Woman extends Person{
private boolean hasLongHair;
private Man husband;
@Override
public void marry(Person p) {
this.husband = (Man)p;
}
}
我們來看一下效果:
看起來還不錯(cuò)佣盒,不是么。
當(dāng)然顽聂,我更喜歡這么做
public void setMoney(Person p,double money) {
if (p == this || p == this.wife)
this.money = money;
else if(money > this.money)
this.money = money;
else
System.out.println(p.getName()+"搶錢肥惭!");
}
總結(jié)
繼承還有很多知識(shí)點(diǎn)盯仪,比如向上轉(zhuǎn)型和向下轉(zhuǎn)型(上面解決上一篇問題的代碼就用到了這個(gè)知識(shí)點(diǎn)),在繼承中蜜葱,對象是怎么初始化的全景,靜態(tài)代碼塊的使用,final關(guān)鍵字的使用等等牵囤。
但是我打算先放一放再講爸黄,等寫完組合,聚合和多態(tài)再來討論這些知識(shí)會(huì)更好一點(diǎn)揭鳞。
下一篇《重新認(rèn)識(shí)java(四) --- 組合炕贵、聚合與繼承的愛恨情仇》敬請期待。
有錯(cuò)誤或者我沒講到的地方或者更好的思路請及時(shí)與我聯(lián)系野崇!
轉(zhuǎn)載請注明出處3瓶!E依妗1詈洹!
本文原創(chuàng)自csdn和簡書7龆啤脆霎!
csdn地址:http://blog.csdn.net/qq_31655965/article/details/53381737
簡書地址:http://www.reibang.com/p/4258962bebb6