三、繼承
繼承:
繼承是類與類的一種關(guān)系,是一種“is a”的關(guān)系。比如說(shuō),狗是一種動(dòng)物(dog is an animal),汽車是一種交通工具(car is a vehicle),在這里動(dòng)物和交通工具就稱為父類(基類),狗和汽車就稱為子類(派生類)。就像日常生活中我們所說(shuō)的繼承一樣,兒子會(huì)繼承父親的一些相貌上或者性格上的特點(diǎn),還會(huì)耳濡目染、有樣學(xué)樣地繼承父親的一些行為方式;而在Java中,子類則會(huì)繼承父類所有的屬性和方法(當(dāng)然private修飾的屬性和方法不可以繼承哦)。
繼承的關(guān)鍵字為extends具體定義語(yǔ)法為:
[權(quán)限修飾符] class 類名 extends 父父類名{
//定義或修改屬性
//定義或重寫方法
}
注意:類是單繼承的,即每個(gè)類只能有一個(gè)父類。
方法的重寫與方法的重載:
上面說(shuō)過(guò)子類可以繼承父類的方法,但如果父類中的方法不夠完善或者不能滿足子類的要求,這時(shí)候子類就可以修改父類的方法,拓展功能或者重新定義,這就是方法的重寫。
在同一個(gè)類中,我們可以定義一些名稱相同的方法,但這些方法的參數(shù)或者返回值類型卻不同。在調(diào)用方法時(shí),雖然這些方法都是同名的,但Java會(huì)根據(jù)不同的參數(shù)列表來(lái)選擇正確的方法進(jìn)行調(diào)用,這就是方法的重載。
方法的重寫和重載從字面上來(lái)看只有一字之差,但是本質(zhì)上卻有很大的區(qū)別,以下是需要注意的幾點(diǎn):
- 子類中重寫的方法與父類中的方法有完全相同的返回值類型、方法名、參數(shù)個(gè)數(shù)以及參數(shù)類型,只是方法體不同
- 重載的方法必須具有不同的參數(shù)列表,即不同的參數(shù)類型或者不同的參數(shù)個(gè)數(shù)或者不同的參數(shù)順序,因此方法體也就不同了。除此之外,權(quán)限修飾符和返回值類型也可以不同,但方法名必須要相同
- 方法的重寫和重載都可以改變權(quán)限修飾符,但都只能將范圍擴(kuò)大(關(guān)于權(quán)限修飾符的作用范圍上文有介紹)
- 一般來(lái)說(shuō),重載方法時(shí),方法之間需要存在一定的聯(lián)系,例如功能相似,因?yàn)檫@樣才能使方法的重載有意義,并且提高程序的可讀性
super關(guān)鍵字:
如果子類已經(jīng)將父類中的方法重寫了,調(diào)用的時(shí)候肯定是調(diào)用被重寫過(guò)的方法,那如果現(xiàn)在一定要調(diào)用父類中的方法,此時(shí)就可以通過(guò)使用super關(guān)鍵字:super.方法名(參數(shù)列表)來(lái)實(shí)現(xiàn)
下面以狗類(子類)和動(dòng)物類(父類)為例具體演示一下上面介紹的幾個(gè)概念,先定義Dog類和Animal類:
package com.project;
public class Animal{//定義Animal類
int legs = 2;//定義legs屬性
public void eat(){//定義eat方法
System.out.println("動(dòng)物具有吃東西的能力");
}
package com.project;
public class Dog extends Animal{//定義Dog類
}
修改項(xiàng)目測(cè)試類的代碼:
public class Test {
public static void main(String[] args) {
Dog obj = new Dog();//創(chuàng)建Dog類對(duì)象obj
System.out.println(obj.legs);//輸出obj的legs屬性值
obj.eat();//調(diào)用obj的eat()方法
}
}
控制臺(tái)界面會(huì)輸出以下信息:

說(shuō)明子類可以繼承父類的屬性和方法。
修改Dog類的代碼:
public class Dog extends Animal{
int legs = 4;
public void eat(){//重寫eat()方法
super.eat();//用super關(guān)鍵字調(diào)用Animal類中的eat()方法
System.out.println("狗具有吃骨頭的能力");
}
}
其他代碼不作改動(dòng),控制臺(tái)界面會(huì)輸出以下信息:

說(shuō)明子類可以修改父類的屬性和重寫父類的方法。
四、多態(tài)
多態(tài):
多態(tài),從字面上理解就是多種形態(tài),Java中指的是對(duì)象的多種形態(tài)。
多態(tài)分為兩種:
- 引用多態(tài):父類的引用既可以指向本類的對(duì)象,也可以引用子類的對(duì)象
- 方法多態(tài):當(dāng)通過(guò)父類創(chuàng)建本類對(duì)象時(shí),調(diào)用的方法為本類的方法;創(chuàng)建子類對(duì)象時(shí),調(diào)用的方法為子類繼承的方法或重寫的方法(即不能調(diào)用子類獨(dú)有的方法)
還是以Dog類和Animal類為例,修改Dog類和項(xiàng)目測(cè)試類主方法的代碼:
public class Dog extends Animal{
public void eat(){
System.out.println("狗具有吃骨頭的能力");
}
}
public class Test {
public static void main(String[] args) {
Animal obj1 = new Animal();//通過(guò)Animal類創(chuàng)建Animal類對(duì)象obj1
Animal obj2 = new Dog();//通過(guò)Animal類創(chuàng)建Dog類對(duì)象obj2(通過(guò)構(gòu)造方法可以區(qū)別創(chuàng)建的是哪類對(duì)象)
obj1.eat();//調(diào)用obj1的eat()方法
obj2.eat();//調(diào)用obj2重寫的eat()方法
}
}
控制臺(tái)界面會(huì)輸出以下信息:

再次修改Dog類和項(xiàng)目測(cè)試類主方法的代碼:
public class Dog extends Animal{
public void eat(){
System.out.println("狗具有吃骨頭的能力");
}
public void watchDoor(){//定義watchDoor方法
System.out.println("狗具有看門的能力");
}
}
public class Test {
public static void main(String[] args) {
Animal obj1 = new Animal();
Animal obj2 = new Dog();
obj1.eat();
obj2.eat();
obj2.watchDoor();//調(diào)用obj2的獨(dú)有的watchDoor方法
}
}
此時(shí),obj2.watchDoor();這個(gè)地方程序會(huì)報(bào)錯(cuò):

因?yàn)楦割愔胁](méi)有定義watchDoor()方法,通過(guò)父類創(chuàng)建子類對(duì)象并調(diào)用這個(gè)子類獨(dú)有的方法時(shí)就會(huì)報(bào)錯(cuò)。
抽象類:
抽象類只是規(guī)定子類必須擁有的方法,但并不規(guī)定這些方法是如何實(shí)現(xiàn)的。因此,可以從多個(gè)具有相同特征的類中抽象出一個(gè)抽象類,這個(gè)抽象類作為子類的模版,從而避免子類設(shè)計(jì)的隨意性。需要注意的是:
- 抽象類和抽象方法都是用
abstract關(guān)鍵字來(lái)修飾 - 當(dāng)
abstract定義抽象方法時(shí),只需要聲明即可,不需要定義具體實(shí)現(xiàn)(因?yàn)槌橄箢惒⒉魂P(guān)注這些方法是如何實(shí)現(xiàn)的),即abstract 類名();//沒(méi)有方法體 - 抽象類中既可以有普通方法,也可以有抽象方法
- 包含抽象方法的類一定是抽象類
下面以狗類(子類)、貓類(子類)和動(dòng)物類(父類,同時(shí)也是抽象類)為例具體演示一下上面介紹的幾個(gè)概念,先定義Dog類、Cat類和Animal類:
public abstract class Animal{//定義Animal抽象類
public abstract void eat();//定義eat抽象方法
public abstract void voice();//定義voice抽象方法
}
public class Dog extends Animal{
public void eat(){//定義eat()方法在Dog類中的具體實(shí)現(xiàn)
System.out.println("狗具有吃骨頭的能力");
}
public void voice(){//定義voice()方法在Dog類中的具體實(shí)現(xiàn)
System.out.println("狗“汪汪汪”地叫");
}
}
public class Cat extends Animal{
public void eat(){//定義eat()方法在Cat類中的具體實(shí)現(xiàn)
System.out.println("貓具有吃魚(yú)的能力");
}
public void voice(){//定義voice()方法在Cat類中的具體實(shí)現(xiàn)
System.out.println("貓“喵喵喵”地叫");
}
}
修改項(xiàng)目測(cè)試類的代碼:
public class Test {
public static void main(String[] args) {
Dog obj1 = new Dog();
Cat obj2 = new Cat();
obj1.eat();
obj1.voice();
obj2.eat();
obj2.voice();
}
}
控制臺(tái)界面會(huì)輸出以下信息:

接口:
接口定義的是某一批類所共同遵守的規(guī)范,既不關(guān)心這些類中的數(shù)據(jù),也不關(guān)心這些類中方法的具體實(shí)現(xiàn),只規(guī)定這些類中必須包含的方法(這一點(diǎn)與抽象類很相似)。
定義接口的關(guān)鍵字為interface。接口不同于類,它可以繼承多個(gè)父接口。具體的定義語(yǔ)法為:
[權(quán)限修飾符] [abstract] interface 接口名 [extends 父接口1, 父接口2, ...]{
//定義一個(gè)或多個(gè)常量
//定義一個(gè)或多個(gè)抽象方法
}
注意:
- 因?yàn)榻涌谥邪氖浅A亢统橄蠓椒ǎ远x接口的時(shí)候系統(tǒng)會(huì)自動(dòng)添加
abstract關(guān)鍵字 - 接口中的屬性均為靜態(tài)常量,因此在定義屬性的時(shí)候系統(tǒng)會(huì)自動(dòng)添加
public static final修飾符 - 接口中的方法均為抽象方法,因此在定義方法的時(shí)候系統(tǒng)會(huì)自動(dòng)添加
public abstract修飾符 - 接口是用來(lái)被繼承和被實(shí)現(xiàn)的,因此不能使用
private和protected修飾(這樣會(huì)使得接口沒(méi)有意義),一般使用public修飾
使用implements關(guān)鍵字實(shí)現(xiàn)接口。而當(dāng)類既繼承父類又實(shí)現(xiàn)接口時(shí),定義語(yǔ)法為:
[權(quán)限修飾符] class 類名 extends 父類 implements 接口1, 接口2, ...{//一個(gè)類可以實(shí)現(xiàn)多個(gè)接口
//如果繼承了抽象類,要包含繼承的抽象方法;此外還要包含接口的抽象方法
}
注意:
- 在給接口命名的時(shí)候通常在名字前面加上一個(gè)“I”
- 繼承父類必須要在實(shí)現(xiàn)接口之前
還是以狗類、貓類為例,現(xiàn)在要他們都繼承哺乳動(dòng)物類。狗和貓都有吃東西和叫的能力,但狗能游泳而貓卻不能。在大自然中,鴨子也是會(huì)游泳的,但很明顯,鴨子并不屬于哺乳動(dòng)物,即不能繼承哺乳動(dòng)物類。因此要想描述狗和鴨子都具有游泳的能力,就要用到接口。
先定義ISwim接口:
package com.project;
public interface ISwim{
public void swim();
}
定義Mammal類(定義的代碼與Animal類一樣,只是類名不同),修改Dog類和Cat類的代碼,:
public class Dog extends Mammal implements ISwim{//實(shí)現(xiàn)ISwim接口
public void eat(){
System.out.println("狗具有吃骨頭的能力");
}
public void voice(){
System.out.println("狗“汪汪汪”地叫");
}
public void swim(){//定義swim()方法在Dog類中的具體實(shí)現(xiàn)
System.out.println("狗具有游泳的能力");
}
}
public class Cat extends Mammal{
public void eat(){
System.out.println("貓具有吃魚(yú)的能力");
}
public void voice(){
System.out.println("貓“喵喵喵”地叫");
}
}
定義Duck類:
public class Duck implements ISwim{//實(shí)現(xiàn)ISwim接口
public void swim(){//定義swim()方法在Duck類中的具體實(shí)現(xiàn)
System.out.println("鴨子具有游泳的能力");
}
}
修改項(xiàng)目測(cè)試類主方法的代碼:
public class Test {
public static void main(String[] args) {
Dog obj1 = new Dog();
Cat obj2 = new Cat();
Duck obj3 = new Duck();
obj1.eat();
obj1.voice();
obj1.swim();
obj2.eat();
obj2.voice();
obj3.swim();
}
}
控制臺(tái)界面會(huì)輸出以下信息:

后記
以上的內(nèi)容是我通過(guò)看書(shū)和看視頻以及上網(wǎng)找資料歸納、總結(jié)出來(lái)的,目的在于為我今后復(fù)習(xí)Java基礎(chǔ)知識(shí)和編寫Java程序時(shí)提供可查閱的筆記,我也希望能夠給閱讀本文的Java初學(xué)者一些幫助。
參考資料
《Java——從入門到精通》
慕課網(wǎng)(IMOOC)