Java 繼承和多態(tài)的作用及好處
1. 繼承
1.1 什么是繼承
生活中有很多繼承的例子,如兒子繼承父親的家產(chǎn),學(xué)弟繼承學(xué)長(zhǎng)的宿舍等,而在Java中繼承就是子類繼承了父類里面的成員,也就相當(dāng)于在父類的基礎(chǔ)上進(jìn)行拓展的內(nèi)容放到子類里面,從簡(jiǎn)單到復(fù)雜的過(guò)程,而Java里面的繼承是通過(guò)extends關(guān)鍵字實(shí)現(xiàn)的。
1.2 繼承的作用和好處
當(dāng)有兩個(gè)類dog類和cat類如下:
public class Dog { public String name; public int age; public void eat() { System.out.println(this.name + "在吃東西"); } public void map() { System.out.println(this.name + "在喵喵叫"); } }
public class Cat { public String name; public int age; public void eat() { System.out.println(this.name + "在吃東西"); } public void wap() { System.out.println(this.name + "在汪汪叫"); } }
通過(guò)觀察會(huì)發(fā)現(xiàn)上面的dog類和cat類里面有一些共有的成員變量和方法,這時(shí)候我們就在想能不能把這些元素放到同一個(gè)類里面被其他需要的類使用呢?
這時(shí)候就想到了繼承,繼承就可以實(shí)現(xiàn)這一需求,這時(shí)我們?cè)賱?chuàng)建一個(gè)類:
public class Test { public String name; public int age; public void eat() { System.out.println(this.name + "在吃東西"); } }
此時(shí)我們就可以通過(guò)關(guān)鍵字extends來(lái)實(shí)現(xiàn)將Test類里面的成員用到dog和cat類中。
而通過(guò)繼承可以減少代碼的復(fù)用。
1.3 繼承的語(yǔ)法
修飾符 class 子類 extends 父類 { }
上面的cat和dog類就可以通過(guò)extends關(guān)鍵字實(shí)現(xiàn)繼承,如下圖:
public class Dog extends Test { public void map() { System.out.println(this.name + "在喵喵叫"); } }
public class Cat extends Test { public void wap() { System.out.println(this.name + "在汪汪叫"); } }
此時(shí)dog類和cat類就包含了父類里面的成員,在內(nèi)存里也就是:
1.4 子類訪問(wèn)父類里面的成員變量
1. 子類和父類里面的成員變量不同名
這時(shí)候可以直接訪問(wèn):
//父類 public class Base { int a; int b; } //子類 public class Derived extends Base { int c; public void test() { a = 10; b = 20; c = 30; } }
子類可以直接使用this訪問(wèn)子類和父類中的成員變量。
2. 子類和父類中存在成員變量同名
//父類 public class Base { int a; int b; } //子類 public class Derived extends Base { int c; int a; public void test() { a = 10; b = 20; c = 30; } }
此時(shí)父類和子類都有成員變量a,此時(shí)子類會(huì)優(yōu)先訪問(wèn)子類的變量。
- 當(dāng)子類和父類有相同名字的成員變量時(shí)候時(shí)候,會(huì)優(yōu)先訪問(wèn)子類的成員變量。
- 若子類和父類都沒(méi)有的成員變量訪問(wèn)則會(huì)編譯錯(cuò)誤。
1.5 子類訪問(wèn)父類里面的成員方法
1. 子類和父類方法名字不同。
則子類直接訪問(wèn)訪問(wèn)父類的方法。并且子類和父類的方法可以構(gòu)成方法的重載,如下面代碼:
public class Base { public void sit() { System.out.println("hehe"); } } public class Derived extends Base { public void print() { System.out.println("haha"); } public void sit(int a) { System.out.println("hehe"); } public void test() { print();//調(diào)用子類的方法 //構(gòu)成重載 sit();//調(diào)用父類的方法 sit(10);//調(diào)用子類的方法 } }
當(dāng)子類和父類的方法相同時(shí),則子類調(diào)用父類時(shí),則會(huì)就近原則優(yōu)先調(diào)用子類。代碼如下:
public class Base { public void sit() { System.out.println("hehe"); } } public class Derived extends Base { public void print() { System.out.println("haha"); } public void sit() { System.out.println("haha"); } public void test() { print();//調(diào)用子類的方法 sit();//子類和父類都有該方法會(huì)優(yōu)先調(diào)用子類的方法,會(huì)打印haha。 } }
1.6 super關(guān)鍵字
上面我們發(fā)現(xiàn)子類和父類當(dāng)中如果出現(xiàn)相同的成員變量或者成員方法時(shí),子類調(diào)用該成員時(shí)會(huì)優(yōu)先調(diào)用子類的成員,那如何調(diào)用父類的成員呢?
這里就要用到super關(guān)鍵字了,super關(guān)鍵字就是用來(lái)在子類中調(diào)用父類的成員的,當(dāng)子類和父類有相同的成員時(shí),就使用super關(guān)鍵字區(qū)分。
super關(guān)鍵字用于子類調(diào)用父類成員變量:
下面代碼可以發(fā)現(xiàn)當(dāng)子類和父類都有同一個(gè)成員變量a時(shí),就可以通過(guò)super關(guān)鍵字來(lái)調(diào)用父類的成員變量a,當(dāng)然this既可以調(diào)用子類里面的成員變量,又可以調(diào)用父類的成員變量,只有子類和父類有相同名字成員變量時(shí),才用super加以區(qū)分。
public class Base { int a; int b; } public class Derived extends Base { int a; int c; public void test1() { this.a = 10;//this調(diào)用的父類的成員變量。 super.a = 20;//super調(diào)用的子類的成員變量。 this.b = 40;//this調(diào)用的子類的成員變量。 } }
在堆區(qū)里是這樣的:
super關(guān)鍵字用于子類調(diào)用父類成員方法:
這里和成員變量一樣,用super關(guān)鍵字來(lái)區(qū)分相同的成員方法,這里的成員方法支持方法的重載:
public class Base { public void print() { System.out.println("nihao"); } public void sit() { System.out.println("hehe"); } } public class Derived extends Base { public void print() { System.out.println("buhao"); } public void sit(int a) { System.out.println("hehe"); } public void test() { this.print();//調(diào)用子類的方法 super.print();//調(diào)用父類的方法 //構(gòu)成重載 this.sit();//調(diào)用父類的方法 this.sit(10);//調(diào)用子類的方法 } }
super關(guān)鍵字用于調(diào)用構(gòu)造方法:
當(dāng)父類有構(gòu)造方法的時(shí)候,子類也必須有構(gòu)造方法,因?yàn)樽宇愐ㄟ^(guò)super關(guān)鍵字先調(diào)用父類的構(gòu)造方法,再設(shè)置自己的成員方法,并且通過(guò)super(父類的構(gòu)造方法名)的形式調(diào)用,并且必須放在構(gòu)造方法的第一行。所以super和this不能共用,代碼如下:
public class Base { int a; int b; //父類的構(gòu)造方法 public Base(int a, int b) { this.a = a; this.b = b; } } public class Derived extends Base { int c; int d; //子類的構(gòu)造方法 public Derived(int a, int b, int c, int d) { super(a,b);//第一行先調(diào)用父類的構(gòu)造方法 this.c = c; this.d = d; } }
當(dāng)子類和父類都沒(méi)有構(gòu)造方法是,Java中提供了隱藏的構(gòu)造方法在子類和父類中。
當(dāng)父類中定義了無(wú)參的 構(gòu)造方法時(shí),子類的構(gòu)造方法第一行不寫調(diào)用父類的代碼時(shí),Java也會(huì)隱藏的在第一行寫上該代碼,但如果是有參的,則需要自己書寫。
1.7 this和super
相同:
- 都不能在靜態(tài)方法中使用。
- 都是關(guān)鍵字。
- 在構(gòu)造方法中調(diào)用時(shí)必須是第一條語(yǔ)句。
不同:
- this是對(duì)當(dāng)前對(duì)象的成員調(diào)用,而super是對(duì)從父類中繼承下來(lái)的成員的調(diào)用。
- 在構(gòu)造方法中,this用于對(duì)本類構(gòu)造方法的訪問(wèn),而super是對(duì)父類的構(gòu)造方法的訪問(wèn)。兩者不能同時(shí)存在。
1.8 繼承關(guān)系上的調(diào)用順序
我們之前說(shuō)過(guò),在初始化成員變量時(shí),在實(shí)例化一個(gè)類時(shí),調(diào)用優(yōu)先級(jí)是:靜態(tài)代碼塊>實(shí)例代碼塊>構(gòu)造方法。
而在同時(shí)調(diào)用一個(gè)繼承父類的子類和該父類時(shí),調(diào)用優(yōu)先級(jí)是:父類的靜態(tài)代碼塊優(yōu)于子類的靜態(tài)代碼塊,父類的實(shí)例和構(gòu)造優(yōu)于子類的實(shí)例和構(gòu)造,而在第二次實(shí)例化對(duì)象時(shí),靜態(tài)初始化不再調(diào)用,因?yàn)轭愔患虞d一次。
1.8 protected
我們知道protected是訪問(wèn)限定符,而它修飾的變量使用的范圍是同一包下的都可以訪問(wèn),再加上不同包下的子類可以訪問(wèn),代碼如下:
這里變量d是用protected修飾的在demo2包里,而在demo3里面繼承Base,可以調(diào)用d變量。
package demo2; public class Base { protected int d; } package demo3; import demo2.Base; public class Derived extends Base { public void test() { super.d = 20; } }
1.9 繼承方式
Java里面提供了以下繼承方式:
單繼承:就是A 繼承 B。
多層繼承:A 繼承 B ,B 繼承 C
不同類繼承同一個(gè)類,A 繼承 C,B 繼承 C;
但是不支持一個(gè)類繼承多個(gè)類:A 繼承 B, A 繼承 C;這種是錯(cuò)誤的繼承方式。
1.10 final關(guān)鍵字
finai關(guān)鍵字有三種用法:
1. 密封類 當(dāng)一個(gè)類被final修飾后,則該類不能再被其他的類繼承了。
2.修飾變量:final修飾變量后該變量就變成常量了。
3.密封成員方法:表示該方法不能被重寫。
1.11 組合
組合就是說(shuō)對(duì)象之間是包含關(guān)系,它可以把類與類之間分開(kāi)成獨(dú)立的個(gè)體,需要的時(shí)候相互調(diào)用,不同于繼承的思想,當(dāng)父類改變時(shí)一定會(huì)影響子類,相當(dāng)于汽車這個(gè)對(duì)象是由輪胎,發(fā)動(dòng)機(jī)等對(duì)象組成的,而汽車這個(gè)類里面可以調(diào)用輪胎和發(fā)動(dòng)機(jī)類里面的一些屬性和方法來(lái)使用,如果這時(shí)加入一個(gè)電池類,但是油車沒(méi)有電池,電車才有,如果是繼承關(guān)系那么油車就不能繼承Car這個(gè)類了,但如果是組合思想,電車就可以單獨(dú)調(diào)用這個(gè)電池類。
class Tire { //輪胎類 } class Engine { //發(fā)動(dòng)機(jī)類 } public class Car { public Tire tire; public Engine engine; }
2. 多態(tài)
2.1 方法的重寫
方法的重寫是在繼承的條件下,子類對(duì)父類的方法進(jìn)行重寫,子類重寫的方法,返回值類型,方法名,參數(shù)都完全相同,并且子類的訪問(wèn)修飾符的范圍要大于等于父類的訪問(wèn)修飾符的范圍。
當(dāng)然參數(shù)之間也可以不用相同,參數(shù)之間構(gòu)成父子類關(guān)系也可以實(shí)現(xiàn)方法的重寫,這種叫做協(xié)變類型。
可以使用@Override來(lái)判斷該方法是否重寫,如果重寫錯(cuò)誤,還會(huì)顯示報(bào)錯(cuò)。
private修飾的方法,構(gòu)造方法,靜態(tài)方法,final修飾的方法等都能重寫。
下面代碼中,Cat繼承Animal,子類對(duì)父類的eat方法進(jìn)行了重寫。
public class Animal{ public String name; public int age; public Animal() { } public void eat() { System.out.println("正在吃食物"); } } public class Cat extends Animal{ public Cat() { super(); } @Override public void eat() { System.out.println("正在吃貓糧"); } }
2.2 向上轉(zhuǎn)型
向上轉(zhuǎn)型就是父類的引用指向子類的對(duì)象。下面代碼父類Animal引用指向子類Cat對(duì)象。
Animal animal = new Cat(); animal.eat();
這時(shí)候父類的引用可以調(diào)用父類的成員,但是只能調(diào)用子類重寫父類的方法,子類的其他成員都不能調(diào)用。
常見(jiàn)的向上轉(zhuǎn)型的方式:
- 直接賦值
- 參數(shù)傳遞
- 返回值傳遞
2.3 動(dòng)態(tài)綁定和靜態(tài)綁定
動(dòng)態(tài)綁定就是,在父類的引用調(diào)用重寫的方法時(shí),編譯過(guò)程調(diào)用的是父類的方法,但在運(yùn)行時(shí)綁定的卻是子類的方法,最后運(yùn)行結(jié)果調(diào)用的是子類的方法。
這是因?yàn)樵贘ava虛擬機(jī)中存在一塊空間為方法區(qū),這里的每個(gè)方法都綁定一個(gè)地址,在運(yùn)行時(shí)父類方法綁定的地址變?yōu)樽宇惙椒ń壎ǖ牡刂罚哉{(diào)用的子類的方法。
動(dòng)態(tài)綁定的條件 :
- 發(fā)生向上轉(zhuǎn)型
- 存在方法的重寫
- 父類引用調(diào)用了重寫的方法
靜態(tài)綁定是在編譯時(shí)候綁定的,比如說(shuō)方法的重載,根據(jù)參數(shù)來(lái)選擇調(diào)用的方法。
2.4 多態(tài)
多態(tài)就是同一種類型的引用調(diào)用同一種方法,該引用所指的對(duì)象不同,得到的結(jié)果也不同。
下面代碼中,父類的引用分別指向子類Bird和子類Cat,都調(diào)用eat方法,產(chǎn)生的結(jié)果也不相同,這就是多態(tài)。
public class Animal{ public String name; public int age; public Animal() { } public void eat() { System.out.println("正在吃食物"); } } public class Cat extends Animal{ public Cat() { super(); } @Override public void eat() { System.out.println("正在吃貓糧"); } } public class Bird exteends Animal { public Bird() { super(); } @Override public void eat() { System.out.println("正在吃鳥(niǎo)糧"); } public void fly() { System.out.println("正在飛"); } } public class Test { public static void main(String[] args) { Animal animal1 = new Cat(); animal1.eat(); Animal animal2 = new Bird(); animal2.eat(); } }
結(jié)果:
多態(tài)產(chǎn)生的條件:
- 子類對(duì)父類的方法進(jìn)行重寫
- 繼承的條件下
- 通過(guò)父類的引用調(diào)用子類的重寫的方法。
2.5 向下轉(zhuǎn)型
向下轉(zhuǎn)型就是說(shuō)將父類引用的類型強(qiáng)制類型轉(zhuǎn)換為子類的類型。
Animal animal = new Cat(); Cat cat = (Cat)animal;
向下轉(zhuǎn)型后,就可以調(diào)用子類的成員。
但是如果父類引用所指的對(duì)象的類型,與強(qiáng)制類型轉(zhuǎn)換的類型不一樣就會(huì)編譯報(bào)錯(cuò),產(chǎn)生ClassCastExceprion(類型轉(zhuǎn)換異常),所以向上轉(zhuǎn)型也不太安全,所以不經(jīng)常使用。
為了解決這種安全性,Java引入了關(guān)鍵字 instanceof 用來(lái)判斷強(qiáng)制類型轉(zhuǎn)換是否安全。
Animal animal = new Cat(); if(animal instanceof Cat) { Cat cat = (Cat)animal; }else { System.out.println("不安全"); }
2.6 圈復(fù)雜度
圈復(fù)雜度是用來(lái)描述代碼的復(fù)雜程度的,一般跟條件語(yǔ)句和循環(huán)語(yǔ)句的數(shù)量有關(guān),而多態(tài)就可以很好的解決圈復(fù)雜度過(guò)大的問(wèn)題。
假設(shè)要打印形狀,就可以利用多態(tài)來(lái)實(shí)現(xiàn):
public class Shape { public Shape() { } public void print() { System.out.println("畫一個(gè)圖案"); } } public class Rect extends Shape { public Rect() { super(); } public void print() { System.out.println("畫一個(gè)矩形"); } } public class Cycle extends Shape { public Cycle() { super(); } public void print() { System.out.println("畫一個(gè)圓形"); } } public class Test { public static void drawShapes(Shape shape) { shape.print(); } public static void main(String[] args) { Rect rect = new Rect(); Cycle cycle = new Cycle(); Shape[] shapes = {rect,cycle,rect,cycle}; for(Shape shape : shapes) { drawShapes(shape); } } }
打印結(jié)果如下:
利用多態(tài)實(shí)現(xiàn)的代碼的圈復(fù)雜度相對(duì)來(lái)說(shuō)就很低。
注意:盡量不要在構(gòu)造方法中調(diào)用重寫的方法,構(gòu)造方法也支持動(dòng)態(tài)綁定。減少使用實(shí)例化的方法。否則可能會(huì)出現(xiàn)問(wèn)題??梢允褂胒inlal和private修飾的方法,它們不支持方法的重寫。
到此這篇關(guān)于Java 繼承和多態(tài)的作用及好處的文章就介紹到這了,更多相關(guān)Java 繼承和多態(tài)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java設(shè)計(jì)模式詳解之門面模式(外觀模式)
為子系統(tǒng)中的一組接口提供一個(gè)一致的界面, Facade 模式定義了一個(gè)高層接口,這個(gè)接口使得這一子系統(tǒng)更加容易使用。本文給大家介紹Java設(shè)計(jì)模式詳解之門面模式(外觀模式),感興趣的朋友參考下吧2016-04-04如何創(chuàng)建SpringBoot項(xiàng)目
這篇文章主要介紹了如何創(chuàng)建SpringBoot項(xiàng)目,幫助大家更好的學(xué)習(xí)和使用springboot框架,感興趣的朋友可以了解下2021-01-01Java語(yǔ)言中的文件數(shù)據(jù)流示例詳解
這篇文章主要為大家介紹了Java語(yǔ)言中的文件數(shù)據(jù)流示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11IDEA 創(chuàng)建一個(gè)Mybatis Maven項(xiàng)目的方法步驟(圖文)
這篇文章主要介紹了IDEA 創(chuàng)建一個(gè)Mybatis Maven項(xiàng)目的方法步驟(圖文),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03java使用RSA加密方式實(shí)現(xiàn)數(shù)據(jù)加密解密的代碼
這篇文章給大家分享java使用RSA加密方式實(shí)現(xiàn)數(shù)據(jù)加密解密,通過(guò)實(shí)例代碼文字相結(jié)合給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友參考下2019-11-11Java 在PPT中添加文本和圖片超鏈接的實(shí)現(xiàn)方法
這篇文章主要介紹了Java 在PPT中添加文本和圖片超鏈接的實(shí)現(xiàn)方法,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-05-05基于Jpa中ManyToMany和OneToMany的雙向控制
這篇文章主要介紹了Jpa中ManyToMany和OneToMany的雙向控制,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12