Java梳理之理解多態(tài)

說(shuō)起來(lái)接觸java以來(lái)也挺久了,卻一直沒(méi)有對(duì)自己進(jìn)行全面的整合,查漏補(bǔ)缺,拿起筆發(fā)現(xiàn)有種無(wú)從下手的感覺(jué)。梳理了一下,如果文中有錯(cuò)誤或遺漏的地方,請(qǐng)幫忙指正。謝謝~

面向?qū)ο缶幊逃腥筇匦裕悍庋b、繼承、多態(tài)。
封裝:將事物特征和行為抽象出來(lái),并隱藏內(nèi)部具體的實(shí)現(xiàn)機(jī)制。隱藏即可以保護(hù)數(shù)據(jù)安全,也可以在不影響類的使用情況下對(duì)類進(jìn)行修改。對(duì)外界而言,暴露的僅僅是一個(gè)方法。
繼承:若兩個(gè)類之間是is-a的關(guān)系,就可以使用extends關(guān)鍵字對(duì)父類的代碼進(jìn)行復(fù)用。同時(shí)繼承允許將對(duì)象視為它本身的類型或者它的父類型進(jìn)行處理,這是使用繼承設(shè)計(jì)多態(tài)的基礎(chǔ)。
多態(tài):程序中定義的引用變量,它指向的具體類型和它的調(diào)用方法在編譯中并不確定,只有在程序運(yùn)行時(shí)才確定。這樣,不用修改程序代碼,就可以讓引用變量綁定不同的具體類型,使得調(diào)用的方法也隨之改變。

多態(tài)分成編譯時(shí)多態(tài)和運(yùn)行時(shí)多態(tài)。編譯時(shí)多態(tài)指的是方法的重載,屬于靜態(tài)多態(tài),當(dāng)編譯時(shí),會(huì)根據(jù)參數(shù)列表來(lái)區(qū)分不同的方法,編譯完成后,會(huì)生成不同的方法。而運(yùn)行時(shí)多態(tài)則為運(yùn)行時(shí)動(dòng)態(tài)綁定方法來(lái)實(shí)現(xiàn),指的就是多態(tài)性。

多態(tài)性

前置概念:

方法綁定:將一個(gè)方法的調(diào)用和方法主體關(guān)聯(lián)起來(lái)就叫做方法綁定。
從多態(tài)的概念上可以看出,在程序中,方法綁定并不一定發(fā)生在程序運(yùn)行期間,還有在程序運(yùn)行前就綁定的情況。在程序運(yùn)行前就綁定的稱作前期綁定,而在運(yùn)行時(shí)根據(jù)對(duì)象具體類型進(jìn)行綁定的稱作后期綁定或動(dòng)態(tài)綁定。實(shí)現(xiàn)后期綁定必須有某種機(jī)制以便在運(yùn)行時(shí)判斷對(duì)象的類型。

向上轉(zhuǎn)型:把一個(gè)對(duì)象的引用視為對(duì)它父類型的引用稱作向上轉(zhuǎn)型。缺陷:在使用過(guò)程中,只能以父類為基準(zhǔn),使用也只能使用父類中的屬性方法,導(dǎo)致丟失子類的一部分屬性和方法。
例如:蘋(píng)果,香蕉,橙子都是水果,實(shí)體類Apple,Banana,Orange全都繼承Fruit類。

public class Fruit {
    public void name(){
        System.out.println("水果");
    }
    public static void main(String[] arg0){
        Fruit apple = new Apple();
        apple.name();
    } 
}
class Apple extends Fruit{
    public void name(){
        System.out.println("青蘋(píng)果");
    }
    public void name(String name){
        System.out.println("設(shè)置名字為"+name);
    }
    public void setName(String color){
        System.out.println("設(shè)置名字為"+name);
    }
}
class Banana extends Fruit{
    public void name(){
        System.out.println("香蕉");
    }
}
class Orange extends Fruit{
    public void name(){
        System.out.println("橙子");
    }
}

那么

Fruit apple = new Apple();
Fruit banana = new Banana();
Fruit orange = new Orange();

就是Fruit的多態(tài)表現(xiàn)。可以理解成引用變量apple類型為Fruit,具體指向的則是Apple對(duì)象的實(shí)例,具體理解為:Apple對(duì)象繼承Fruit,所以Apple會(huì)自動(dòng)的向上轉(zhuǎn)型為Fruit對(duì)象,所以apple可以指向Apple。但是由于使用了向上轉(zhuǎn)型,那么也會(huì)存在向上轉(zhuǎn)型的缺陷。例如:apple是不能使用name(String color)setName(String name)方法的,不管是子類的屬性還是子類特有的方法,包括子類重載的方法。例如apple.name();可以得到值:青蘋(píng)果。但是,編寫(xiě)apple.name("紅蘋(píng)果")或者apple.setName("紅蘋(píng)果")是會(huì)提示錯(cuò)誤。

多態(tài)的實(shí)現(xiàn):

1.用繼承設(shè)計(jì)進(jìn)行設(shè)計(jì)

條件:繼承關(guān)系、重寫(xiě)父類中的方法和隱式的向上轉(zhuǎn)型。
在之前的代碼,添加一個(gè)實(shí)體類Person,內(nèi)部存在行為eat(Fruit fruit)方法。

class Person{
    public void eat(Fruit fruit){
        System.out.print("吃的");
        fruit.name();
    } 
}   
    public static void main(String[] arg0){
        Fruit apple = new Apple();
        Fruit banana = new Banana();
        Fruit orange = new Orange();
        
        Person july = new Person();
        july.eat(apple);
        july.eat(banana);
        july.eat(orange);
    }
輸出:
    吃的青蘋(píng)果
    吃的香蕉
    吃的橙子

可以看到并沒(méi)有使用eat(Apple apple)一類的方法,但也能正確的執(zhí)行方法,我們不用為單獨(dú)的每個(gè)人創(chuàng)建類似于eatApple(Apple apple)這樣的方法,而且對(duì)于每一個(gè)繼承了Fruit類的水果類來(lái)說(shuō),都可以直接給person.eat(Fruit)調(diào)用。

2.用接口進(jìn)行設(shè)計(jì)

條件:實(shí)現(xiàn)接口,并覆蓋其中的方法。

類似于使用繼承設(shè)計(jì)多態(tài),接口設(shè)計(jì)如下所示:

public class FruitDemo implements IFruit{
    @Override
    public void name() {
        // TODO Auto-generated method stub
        System.out.println("水果");
    }
    public static void main(String[] arg0){
        AppleDemo apple = new AppleDemo();
        BananaDemo banana = new BananaDemo();
        PersonDemo july = new PersonDemo();
        july.eat(apple);
        july.eat(banana);
    }
}
class PersonDemo{
    public void eat(IFruit fruit){
        System.out.print("吃的");
        fruit.name();
    }
}
class AppleDemo implements IFruit{
    @Override
    public void name() {
        // TODO Auto-generated method stub
        System.out.println("蘋(píng)果");
    }
}
class BananaDemo implements IFruit{
    @Override
    public void name() {
        // TODO Auto-generated method stub
        System.out.println("香蕉");
    }
}
interface IFruit{
    void name();
}
    
輸出:
吃的蘋(píng)果
吃的香蕉

可以看到,程序中Person類實(shí)例july動(dòng)態(tài)調(diào)用實(shí)現(xiàn)了IFruit接口的類,并且正確返回了信息。

多態(tài)特性之協(xié)變返回類型

子類方法的返回類型可以是父類方法的返回類型的子類。例如:

public class Fruit {

    public String name = "水果";
    public String getName(){
        System.out.println("fruit name --"+ name);
        return name;
    }
    
    public static void main(String[] arg0){
        Person person = new Person();
        person.buy().getName();
        
        Person man = new Man();
        man.buy().getName();
    } 
}


class Apple extends Fruit{
    public String name = "蘋(píng)果";
    public String getName(){
        System.out.println("apple name --"+ name);
        return name;
    }
}

class Person{
    public Fruit buy(){
        
        return new Fruit();
    }
}

class Man extends Person{
    public Apple buy(){
        return new Apple();
    }
}
輸出:
fruit name --水果
apple name --蘋(píng)果

在這里看到,子類Man中的方法,返回類型并不是Fruit,而是Fruit的子類,運(yùn)行的也是子類Apple的方法。

多態(tài)存在的缺陷:

1.對(duì)私有方法和final修飾的方法無(wú)效。
public class Fruit {

    public void name(){
        System.out.println("水果");
    }
    
    public final void findName(){
        System.out.println("找水果");

    }
    
    private void getName(){
        System.out.println("拿水果");

    }
    
    public static void main(String[] arg0){
        Fruit apple = new Apple();
        apple.findName();
        apple.getName();
    } 
}
class Apple extends Fruit{
    
    public void getName(){
        System.out.println("拿到蘋(píng)果");

    }
    public void name(){
        System.out.println("青蘋(píng)果");
    }
    public void name(String name){
        System.out.println("蘋(píng)果設(shè)置成"+name);
    }
    
    public void setName(String name){
        System.out.println("蘋(píng)果設(shè)置成"+name);
    }
}
輸出:
     找水果
     拿水果
2.對(duì)父類字段和靜態(tài)方法無(wú)效。
public class Fruit {
    public String name = "水果";
    public String getName(){
        return name;
    }
    public static String getFruitName(){
        return "水果";
    }
    
    public static void main(String[] arg0){
        Fruit apple = new Apple();
        System.out.println("apple.name = "+apple.name+";apple.getName() = "+apple.getName());
        Apple apple1 = new Apple();
        System.out.println("apple1.name = "+apple1.name+";apple1.getName() = "+apple1.getName()+";apple1.getName1() = "+apple1.getName1());
        System.out.println("Fruit.getFruitName = "+ Fruit.getFruitName()+";Apple.getFruitName = "+Apple.getFruitName());

    } 
}
class Apple extends Fruit{
    public String name = "蘋(píng)果";
    public String getName(){
        return name;
    }
    public static String getFruitName(){
        return "蘋(píng)果";
    }
    public String getName1(){
        return super.name;
    }
}

輸出:
    apple.name = 水果;apple.getName() = 蘋(píng)果
    apple1.name = 蘋(píng)果;apple1.getName() = 蘋(píng)果;apple1.getName1() = 水果
    Fruit.getFruitName = 水果;Apple.getFruitName = 蘋(píng)果

可以看到 字段并不會(huì)覆蓋的,在子類Apple中是存在兩個(gè)name字段的,當(dāng)使用Fruit apple引用時(shí),apple.name使用的是父類Fruit中的字段,而當(dāng)Apple apple1時(shí),使用的是子類自己的字段。
靜態(tài)方法是不會(huì)有多態(tài)性的,它關(guān)聯(lián)的對(duì)象,而不是實(shí)例。

構(gòu)造函數(shù)和多態(tài):

構(gòu)造函數(shù)執(zhí)行的順序
1.調(diào)用基類的構(gòu)造器,這個(gè)順序會(huì)不斷遞歸下去,因?yàn)闃?gòu)造一個(gè)類,先構(gòu)造基類,直到樹(shù)結(jié)構(gòu)的最頂層。
2.按聲明順序調(diào)用成員的初始化方法。
3.調(diào)用導(dǎo)出類的構(gòu)造器主體

構(gòu)造器內(nèi)部的多態(tài)方法
如果一個(gè)構(gòu)造方法的內(nèi)部調(diào)用正在構(gòu)造的對(duì)象的一個(gè)動(dòng)態(tài)綁定方法,會(huì)發(fā)生什么情況?例如:

public class Fruit {
    public String name = "水果";
    public Fruit(){
        System.out.println("getName before--");
        System.out.println("getName--"+getName());
        System.out.println("getName after --");
    }
    public String getName(){
        return name;
    }
    public static void main(String[] arg0){
        Fruit apple =new Apple();
    } 
}
class Apple extends Fruit{
    public String name = "蘋(píng)果";
    public Apple(){
        System.out.println("Apple getName--"+getName());
    }
    public String getName(){
        return name;
    }
}

輸出:
getName before--
getName--null
getName after --
Apple getName--蘋(píng)果

可以看到,在結(jié)果中存在一個(gè)null值,如果當(dāng)前屬性是基本數(shù)據(jù)類型,那么輸出的就是類型的初始默認(rèn)值。之后會(huì)按照聲明順序來(lái)構(gòu)造實(shí)例,所以后面得到的就是有值得了。

文章主要參考《Thinking in Java》第八章 多態(tài)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,818評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,185評(píng)論 3 414
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 175,656評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 62,647評(píng)論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,446評(píng)論 6 405
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 54,951評(píng)論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,041評(píng)論 3 440
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,189評(píng)論 0 287
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,718評(píng)論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,602評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,800評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,316評(píng)論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,045評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,419評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 35,671評(píng)論 1 281
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,420評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,755評(píng)論 2 371

推薦閱讀更多精彩內(nèi)容

  • 本文出自 Eddy Wiki ,轉(zhuǎn)載請(qǐng)注明出處:http://eddy.wiki/interview-java.h...
    eddy_wiki閱讀 1,221評(píng)論 0 5
  • 一、繼承 當(dāng)兩個(gè)事物之間存在一定的所屬關(guān)系,即就像孩子從父母那里得到遺傳基因一樣,當(dāng)然,java要遺傳的更完美,這...
    玉圣閱讀 1,061評(píng)論 0 2
  • (一)Java部分 1、列舉出JAVA中6個(gè)比較常用的包【天威誠(chéng)信面試題】 【參考答案】 java.lang;ja...
    獨(dú)云閱讀 7,127評(píng)論 0 62
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法,類相關(guān)的語(yǔ)法,內(nèi)部類的語(yǔ)法,繼承相關(guān)的語(yǔ)法,異常的語(yǔ)法,線程的語(yǔ)...
    子非魚(yú)_t_閱讀 31,709評(píng)論 18 399
  • props一般用于承載組件間傳遞的數(shù)據(jù); state一般用于組件自身的數(shù)據(jù)維護(hù)。
    余生筑閱讀 271評(píng)論 0 1