Java全面分析面向?qū)ο笾鄳B(tài)
多態(tài)的理解
什么是多態(tài)呢??從字面理解就是多種形態(tài),也就是不同類(lèi)實(shí)例化出來(lái)的對(duì)象調(diào)用同一種方法,也可以理解為不同類(lèi)的對(duì)象經(jīng)過(guò)同一種行為產(chǎn)生的狀態(tài)是不同的,這就是多態(tài)。
要想理解多態(tài),我們必須要了解向上轉(zhuǎn)型和重寫(xiě)這兩個(gè)重點(diǎn)然后在來(lái)深刻理解多態(tài)這一概念,等看完向上轉(zhuǎn)型與重寫(xiě)再來(lái)看多態(tài)的概念,你就會(huì)豁然開(kāi)朗,一下就通透了不少。因?yàn)槎鄳B(tài)的條件就是向上轉(zhuǎn)型,重寫(xiě)以及繼承。
向上轉(zhuǎn)型
首先多態(tài)的前提是繼承,那既然是繼承,那就肯定就有父類(lèi)與子類(lèi)這樣的關(guān)系。
我們?cè)賮?lái)回憶一下怎么創(chuàng)建子類(lèi)對(duì)象和父類(lèi)對(duì)象。
class Animal{
public String name;//名字
public int age;
public void eat() {
System.out.println("我要吃飯!??!");
}
public void sleep() {
System.out.println("我要睡覺(jué)!?。?);
}
}
class Cat extends Animal{
public void mew() {
System.out.println("喵喵喵?。?!");
}
}
public class TestDemo1 {
public static void main(String[] args) {
Cat cat =new Cat();//實(shí)例化子類(lèi)對(duì)象
cat.name="mimi";
Animal animal = new Animal();//實(shí)例化父類(lèi)對(duì)象
animal.eat();
}
}這里就創(chuàng)建了貓這個(gè)類(lèi)然后繼承了Animal類(lèi)。我們實(shí)例化貓和Animal這個(gè)對(duì)象就可以調(diào)用方法和屬性。
那何為向上轉(zhuǎn)型呢???
原本子類(lèi)對(duì)象的引用引用子類(lèi)的對(duì)象,現(xiàn)在讓父類(lèi)的引用引用子類(lèi)對(duì)象這就是向上轉(zhuǎn)型。

我們利用代碼理解一下:

這就是向上轉(zhuǎn)型,我們也可以利用animal這個(gè)父類(lèi)引用 調(diào)用方法;

這時(shí)我們就會(huì)發(fā)現(xiàn)利用這個(gè)引用能夠調(diào)用父類(lèi)的方法和屬性,但是不能夠調(diào)用子類(lèi)的方法和屬性,那為什么呢??原因就是因?yàn)楦割?lèi)沒(méi)有子類(lèi)這個(gè)方法,所以不能調(diào)用??偨Y(jié):向上轉(zhuǎn)型的時(shí)候也就是父類(lèi)引用引用子類(lèi)對(duì)象,這個(gè)父類(lèi)引用只能調(diào)用父類(lèi)有的屬性和方法,不能調(diào)用子類(lèi)的。
向上轉(zhuǎn)型的三種形式
第一種:直接賦值
也就是我們上面的那種寫(xiě)法:
Animal animal1 = new Cat();//父類(lèi)對(duì)象的引用 引用子類(lèi)對(duì)象--->向上轉(zhuǎn)型 Animal animal2 = new Dog();
第二種:作為方法參數(shù):

第三種作為返回值:

我們回到剛才的打印結(jié)果是什么;

但如果我把父類(lèi)的方法變成我要吃貓糧呢?那結(jié)果毫無(wú)意外就是mimi我要吃貓糧。
但是這就會(huì)出現(xiàn)一個(gè)問(wèn)題,如果我在創(chuàng)建一個(gè)狗類(lèi),然后在調(diào)用eat方法 難道狗也要吃貓糧么?這就會(huì)出現(xiàn)問(wèn)題,那我們可以在子類(lèi)寫(xiě)一個(gè)eat方法;
class Animal{
public String name;//名字
public int age;
public void eat() {
System.out.println(this.name+"要吃飯?。。?);
}
}
class Dog extends Animal{
public void dark() {
System.out.println("汪汪汪?。。?);
}
public void eat() {
System.out.println(this.name+"吃狗糧?。。?);
}
}
class Cat extends Animal{
public void mew() {
System.out.println("喵喵喵?。?!");
}
public void eat() {
System.out.println(this.name+"吃貓糧!??!");
}
}
public class TestDemo1 {
public static void main(String[] args) {
//語(yǔ)法形式 : 父類(lèi) 變量 = new 子類(lèi)();
Animal animal1 = new Cat();//父類(lèi)對(duì)象的引用 引用子類(lèi)對(duì)象--->向上轉(zhuǎn)型
Animal animal2 = new Dog();//父類(lèi)對(duì)象的引用 引用子類(lèi)對(duì)象--->向上轉(zhuǎn)型
animal1.name = "小貓";//訪問(wèn)父類(lèi)屬性
animal2.name = "小狗";//訪問(wèn)父類(lèi)屬性
animal1.eat();
animal2.eat();
// animal.mew();//訪問(wèn)子類(lèi)特有的方法
}
}這時(shí)又創(chuàng)建了一個(gè)狗類(lèi),然后又分別在兩個(gè)子類(lèi)創(chuàng)建兩個(gè)eat方法。

我們發(fā)現(xiàn)這時(shí)候就變得很清楚就達(dá)到我們想要的效果了。
但我們又應(yīng)該想一想,為什么調(diào)用子類(lèi)的eat方法而不調(diào)用父類(lèi)的?
動(dòng)態(tài)綁定和靜態(tài)綁定
此時(shí)其實(shí)發(fā)生了動(dòng)態(tài)綁定,我們可以看一下字節(jié)碼文件,打開(kāi)powershell窗口

我們都知道執(zhí)行一個(gè)程序是先編譯后運(yùn)行,而這個(gè)是在編譯的時(shí)候調(diào)用的是Animal的eat方法,而在運(yùn)行的時(shí)候是調(diào)用的是Cat的方法這就是我們所說(shuō)的運(yùn)行時(shí)綁定或者可以說(shuō)是動(dòng)態(tài)綁定。
那既然有動(dòng)態(tài)綁定那肯定也有靜態(tài)綁定。
動(dòng)態(tài)綁定是在編譯的時(shí)候調(diào)用一個(gè)方法,而在運(yùn)行時(shí)才是最后要確定調(diào)用的方法,也就是在運(yùn)行時(shí)確定要調(diào)用那個(gè)方法。
靜態(tài)綁定就是在編譯期間已經(jīng)確定要調(diào)用哪個(gè)方法。
其中,動(dòng)態(tài)綁定最顯著的代表就是方法重寫(xiě)。
靜態(tài)綁定最顯著的代表就是方法重載。
我們?cè)诨剡^(guò)頭看上面的方法 ε=(´ο`*)))......怎么前面的eat方法返回值,參數(shù)列表,方法名都是一樣的呢?我們來(lái)看一下。

方法的重寫(xiě)
我們之前學(xué)過(guò)方法重載這里回顧一下方法重載,方法重載是方法名相同,返回值不做要求,參數(shù)列表不同。而我們今天學(xué)的方法重寫(xiě)是返回值相同,方法名稱相同,參數(shù)列表相同,說(shuō)是叫方法重寫(xiě)其實(shí)也可以叫做方法覆蓋。
方法重寫(xiě)有幾點(diǎn)注意要求:
方法重寫(xiě)滿足 方法名相同,方法的參數(shù)列表相同,方法的返回值相同。

我們也可以一鍵生成重寫(xiě)

有幾個(gè)注意事項(xiàng):

不能重寫(xiě)被private修飾的方法。

不能重寫(xiě)被final修飾的方法。

子類(lèi)的方法的訪問(wèn)權(quán)限一定要大于等于父類(lèi)的訪問(wèn)權(quán)限。

重寫(xiě)的方法, 可以使用 @Override 注解來(lái)顯式指定. 有了這個(gè)注解能幫我們進(jìn)行一些合法性校驗(yàn). 例如不小心將方法名字拼寫(xiě)錯(cuò)了 (比如寫(xiě)成eat), 那么此時(shí)編譯器就會(huì)發(fā)現(xiàn)父類(lèi)中沒(méi)有 aet 方法, 就會(huì)編譯報(bào)錯(cuò), 提示無(wú)法構(gòu)成重寫(xiě).

被static修飾的方法也不能被重寫(xiě)
總結(jié)方法重寫(xiě)的注意事項(xiàng):
- 被private,final修飾的方法不能被重寫(xiě)。
- 被staitc修飾的方法也不能被重寫(xiě)。
- @override 可以檢查你重寫(xiě)的方法名是否正確,最好要加上。
- 方法重寫(xiě)一定滿足方法名相同,參數(shù)列表相同,返回值相同。
對(duì)比方法重寫(xiě)與方法重載:

最后:重寫(xiě)不是進(jìn)行在原來(lái)基礎(chǔ)的修改,而是在原來(lái)基礎(chǔ)上進(jìn)行迭代和更新。
進(jìn)一步認(rèn)識(shí)和理解多態(tài)
場(chǎng)景:畫(huà)一個(gè)圖形
class Shape{//創(chuàng)建一個(gè)圖形類(lèi)---->作為多種圖形的父類(lèi)
public int length;//圖形的長(zhǎng)
public int wide;//圖形的寬
public int height;//圖形的高
public void draw() {
System.out.println("我要畫(huà)一個(gè)圖形?。?!");
}
}
class rectangle extends Shape{//長(zhǎng)方形
@Override
public void draw() {
System.out.println("我要畫(huà)一個(gè)長(zhǎng)方形?。?!");
}
}
class square extends Shape{
@Override
public void draw() {
System.out.println("我要畫(huà)一個(gè)正方形?。?!");
}
}
class circular extends Shape{
@Override
public void draw() {
System.out.println("我要畫(huà)一個(gè)圓形!?。?);
}
}
public class TestDemo1 {
public static void method(Shape shape) {
shape.draw();
}
public static void main(String[] args) {
Shape shape1 = new circular();
Shape shape2 = new rectangle();
Shape shape3 = new square();
method(shape1);
method(shape2);
method(shape3);
}
}創(chuàng)建一個(gè)Shape(父類(lèi)),然后創(chuàng)建三個(gè)子類(lèi)分別是square ,circular,rectangle,利用父類(lèi)引用這三個(gè)子類(lèi),接著調(diào)用method方法。

這就是多態(tài),不同的對(duì)象,調(diào)用同一個(gè)方法最后結(jié)果產(chǎn)生出不同的狀態(tài)。
我們?cè)賮?lái)總結(jié)多態(tài)產(chǎn)生的條件:
- 要在繼承體系下
- 子類(lèi)要對(duì)父類(lèi)的方法進(jìn)行重寫(xiě)
- 通過(guò)父類(lèi)的引用調(diào)用重寫(xiě)的方法
也就是 在繼承體系下 進(jìn)行向上轉(zhuǎn)型 和 方法重寫(xiě)
多態(tài)的優(yōu)點(diǎn)
優(yōu)點(diǎn):
- 能夠降低代碼的 "圈復(fù)雜度", 避免使用大量的 if - else
- 如果使用多態(tài), 則不必寫(xiě)這么多的 if - else 分支語(yǔ)句, 代碼更簡(jiǎn)單.
- 可擴(kuò)展能力更強(qiáng)
缺點(diǎn):
- 代碼的運(yùn)行效率降低
還有一個(gè)重要點(diǎn)就是不要在構(gòu)造方法中調(diào)用重寫(xiě)方法
到此這篇關(guān)于Java全面分析面向?qū)ο笾鄳B(tài)的文章就介紹到這了,更多相關(guān)Java多態(tài)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JAVA SE包裝類(lèi)和泛型詳細(xì)介紹及說(shuō)明方法
這篇文章主要介紹了JAVA SE包裝類(lèi)和泛型的相關(guān)資料,包括基本數(shù)據(jù)類(lèi)型與包裝類(lèi)的對(duì)應(yīng)關(guān)系,以及裝箱和拆箱的概念,并重點(diǎn)講解了自動(dòng)裝箱和自動(dòng)拆箱的機(jī)制,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2025-03-03
Spring Boot集成教程之異步調(diào)用Async
在項(xiàng)目中,當(dāng)訪問(wèn)其他人的接口較慢或者做耗時(shí)任務(wù)時(shí),不想程序一直卡在耗時(shí)任務(wù)上,想程序能夠并行執(zhí)行,我們可以使用多線程來(lái)并行的處理任務(wù),也可以使用spring提供的異步處理方式@Async。需要的朋友們下面來(lái)一起看看吧。2018-03-03
使用jquery 的ajax 與 Java servlet的交互代碼實(shí)例
這篇文章主要介紹了使用jquery 的ajax 與 Java servlet的交互代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-09-09
Springboot錯(cuò)誤處理機(jī)制實(shí)現(xiàn)原理解析
這篇文章主要介紹了springboot錯(cuò)誤處理機(jī)制實(shí)現(xiàn)原理解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-04-04
DUCC配置平臺(tái)實(shí)現(xiàn)一個(gè)動(dòng)態(tài)化線程池示例代碼
這篇文章主要為大家介紹了DUCC配置平臺(tái)實(shí)現(xiàn)一個(gè)動(dòng)態(tài)化線程池示例代碼,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02
java中l(wèi)ong數(shù)據(jù)類(lèi)型轉(zhuǎn)換為int類(lèi)型
這篇文章主要講解Java中基本數(shù)據(jù)類(lèi)型,java long 類(lèi)型與其java int類(lèi)型的轉(zhuǎn)換的幾種方法,希望能給大家做一個(gè)參考2016-07-07

