第4章:Java語言高級類特性2:多態(tài)性


時間:2018-07-21 作者:魏文應(yīng)


一沙峻、對象的多態(tài)性

public class Person {
    public void eat(){
        System.out.println("人吃飯");
    }
    public void animal(){
        System.out.println("人是一種動物");   
    }
}
public class Man extends Person{
    public void eat(){
        System.out.println("男人吃飯比較多!");
    }
    public void entertain(){
        System.out.println("男人請客。");
    }
}
public class TestPerson {
    public static void main(String[] args){
        Person p = new Man();
    }
}

看上面代碼志电,你會發(fā)現(xiàn),一般我們是 Person p = new Person(); 去創(chuàng)建一個對象實例蛔趴,但這里使用的是 Person p = new Man(); 挑辆。這里,就使用了 對象的多態(tài)性。這樣鱼蝉,就可以使用子類中洒嗤,子類重寫的那些方法了。什么意思呢魁亦?看下面例子:

Person p = new Man();
p.eat();               // System.out.println("男人吃飯比較多渔隶!");

這時,使用父類 Person 的 引用p 洁奈,p指向 子類Man的一個對象實體间唉,p.eat() 實際執(zhí)行 的是 子類Man中的eat()方法,這就被叫做 虛擬方法調(diào)用 利术。下面調(diào)用是 錯誤的

Person p = new Man();
p.entertain();       // 這樣使用是錯誤的呈野,不能使用entertain()方法

因為父類Person 并沒有entertain這個方法,這也說明了印叁,這種情況下被冒,如果要 使用子類的方法,只能使用子類中重寫父類的方法(比如轮蜕,Man中有eat()方法姆打,要使用Man中的eat(),父類Person也必須要有一個叫做eat()的方法)肠虽。下面調(diào)用是 正確的

Person p = new Man();
p.animal(); 

雖然子類沒有 animal() 方法幔戏,但父類Person是有這個方法的,所以可以使用税课。直觀的表現(xiàn)就是: 父類Person中的所有方法都可以使用闲延,也只能使用這些方法。只是有些方法被子類重寫了韩玩,執(zhí)行的時候垒玲,跑去執(zhí)行子類的方法去了。 這里說的父類的所有方法找颓,包括父類從其它類繼承來的方法合愈。

子類對象的多態(tài)性使用的前提

其實上面講的那么多,實際上是和 程序有編譯狀態(tài)和運行狀態(tài) 有關(guān)击狮,對于下面語句:

Person p = new Man();

在使用 p 這個引用時佛析,編譯器 只會檢查 Person p 有哪些屬性和方法,它 不管 new Man() 有哪些方法和屬性彪蓬,只要 Man 是 Person 的子類就行了寸莫。

  • 要有類的繼承關(guān)系,比如上面的 Person 和 Man 就有繼承關(guān)系档冬。
  • 要有子類對父類方法的重寫膘茎,比如上面的子類Man中桃纯,對父類Person eat() 方法的重寫(這不是必須的,但如果不這樣披坏,Person p = new Man();Person p = new Person (); 的效果就是一樣的了态坦, 多態(tài)性的作用沒有很好的體現(xiàn)出來)。

多態(tài)性的用途

假設(shè)我們有下面三個類棒拂,三個類都有 eat() 方法:

public class Person {
    public void eat(){
        System.out.println("人吃飯");
    }
}
public class Man extends Person{
    public void eat(){
        System.out.println("男人吃飯比較多驮配!");
    }
}
public class Man extends Person{
    public void eat(){
        System.out.println("女人吃飯比較文雅!");
    }
}
public class TestPerson {
    public static void main(String[] args){
        TestPerson t = new TestPerson();
        Person m = new Man();
        t.eat(m);                          // 使用的是Man 類的eat()方法
        Person w = new Woman();
        t.eat(w);                          // 使用的是Woman 類的eat()方法
        Person p = new Person();
        t.eat(p);                          // 使用的是Person 類的eat()方法
    }

    public void eat(Person p){
        p.eat();
    }

    public void eat(Man m){
        m.eat();
    }

    public void eat(Woman w){
        w.eat();
    }
}

上面 TestPerson 類中的三個 eat() 方法之間構(gòu)成方法重載着茸。如果除了 Person、Man琐旁、Woman 涮阔,還有更多的方法需要去重載呢?這樣就會太多方法重載了灰殴,寫起來也很麻煩敬特。因此:

public class TestPerson {
    public static void main(String[] args){
        TestPerson t = new TestPerson();
        Person m = new Man();
        t.eat(m);                          // 使用的是Man 類的eat()方法
        Person w = new Woman();
        t.eat(w);                          // 使用的是Woman 類的eat()方法
        Person p = new Person();
        t.eat(p);                          // 使用的是Person 類的eat()方法
    }

    public void eat(Person p){
        p.eat();
    }
}

上面代碼中,只需 eat(Person p) 牺陶,就可以替代 eat(Man m)伟阔、eat(Person p)eat(Woman w) 這三個方法掰伸。如果Person子類更多皱炉,替代的就越多。這樣就比較方便了狮鸭,這就是對象的多態(tài)性的用途之一合搅。

對象的多態(tài)性是對于方法來說的, 屬性是沒有多態(tài)性的歧蕉。比如下面示例:

public class Person{
    int id = 1001;
}
public class Man extends Person{
    int id = 1002;
}
public class TestPerson {
    public static void main(String[] args){
        Person p = new Man();
        System.out.println(p.id);       // 這時打印出的是1001灾部,是Person類的。
    }
}

說明惯退,屬性是沒有多態(tài)性的赌髓。

二、類的強制類型轉(zhuǎn)換:向下轉(zhuǎn)型

子類繼承父類的屬性和方法(雖然private屬性和方法子類無法直接使用)催跪,可以認為父類是子類的子集锁蠕。所以,下面這個懊蒸,子類賦值給父類匿沛,容量大的賦值給容量小的,稱為 向上轉(zhuǎn)型

Person p = new Man();

容量小的賦值給容量大的榛鼎,稱為 向下轉(zhuǎn)型

Person p = new Man();
Man m = (Man)p;     // 向下轉(zhuǎn)型
m.entertain();      // 子類非重寫的方法逃呼,也可以使用了鳖孤。

轉(zhuǎn)型時,我們要注意抡笼,創(chuàng)建的對象實體是否有相應(yīng)的方法:

public class Person {
}
public class Man extends Person{
}
public class Woman extends Person{
    public void shopping(){
        System.out.println("女人天生愛購物苏揣。");
    }
}
public class TestPerson {
    public static void main(String[] args){
        Person p = new Man();
        Woman w = (Woman)p;
        w.shopping();          // 這里執(zhí)行時會報錯,因為Man沒有shopping()方法推姻。
    }
}

執(zhí)行上面代碼時平匈,就會 拋出異常,因為 new Man() 時藏古,根本就沒有 shopping()方法 在方法區(qū)被創(chuàng)建增炭。編譯時語法沒有錯誤,運行時出錯了:

Exception in thread "main" java.lang.ClassCastException: my.wwy.java1.Man cannot be cast to my.wwy.java1.Woman
    at my.wwy.java1.TestPerson.main(TestPerson.java:33)

三拧晕、instanceof 關(guān)鍵字

格式:

對象  instanceof  類

比如:

Man m = new Man();

if(m instanceof Man){
    System.out.println("這是一個Man類");
}

這就是:判斷對象 m 是否是 類Man 的一個實例隙姿。 是的話,返回true厂捞;不是的話输玷,返回flase。instanceof 判斷時靡馁,如果是父類欲鹏,也會返回true :

public class Man extends Person{
}
Man m = new Man();

if(m instanceof Man){                         // 返回true
    System.out.println("這是一個Man類");
}

if(m instanceof Person){                     // 這個同樣返回true
    System.out.println("這是一個Person類");
}

上面的 m instanceof Manm instanceof Person 判斷都返回 true 。想想也是臭墨,Man 繼承了 Person 赔嚎,也就繼承了Person 的方法和屬性,也算是 Person 類的一個實例胧弛。

四尽狠、Object 類及其 equals() 方法

equals() 方法在Object類中,其源代碼是這樣的:

public boolean equals(Object obj) {
    return (this == obj);
}

說明其輸入?yún)?shù)是 類的對象叶圃。比較的是兩個引用變量的 地址值 袄膏。比如下:

class Person{
}
Person p1 = new Person();
Person p2 = new Person();

System.out.println(p1.equals(p2));     // false柑肴, 比較了p1的值是否和p2值相等

上面的p1和p2值不一樣抢腐,返回false淤井。

String str1 = new String("AA");
String str2 = new String("AA");
System.out.println(str1 == str2);        // false
System.out.println(str1.equals(str2));   // true

上面的 String 類输瓜,重寫了Object類的 equals() 方法窃这,變成比較的是內(nèi)容是否一樣缘厢,而不是地址萝毛。

五楷力、String 類在內(nèi)存中存儲

String 類的內(nèi)容眉厨,是存儲在常量池中的:

String str1 = "AA";
String str2 = "AA";
String str3 = new String("AA");
System.out.println(str1 == str2);        // true
System.out.println(str1.equals(str2));   // true
System.out.println(str1 == str3);        // false
System.out.println(str1.equals(str3));   // true
  • string類在內(nèi)存中存儲

String str1 = "AA" 這種方式創(chuàng)建String對象時锌奴,引用變量 str1 直接保存的是常量池中的 AA 字符串的地址。如果以 String str3 = new String("AA") 方式new出來的String變量憾股,str3 保存了內(nèi)存堆的String地址鹿蜀,而內(nèi)存堆中同樣保存 "AA" 字符串在常量池中的位置箕慧,而不是直接在內(nèi)存堆中 new 出 String 字符串 “AA” 。如果 “AA” 被修改為 “AB” 呢茴恰?那么就重新指向處理池中的 “AB”颠焦。如果常量池沒有 “AB”,則常量池自動創(chuàng)建 “AB” 字符串往枣。

六伐庭、toString() 方法的使用

比如,我們有下面代碼:

class Person {
}
Person p1 = new Person();
System.out.println(p1.toString());     // my.wwy.java.Person@311e170c
System.out.println(p1);                // my.wwy.java.Person@311e170c

你發(fā)現(xiàn)上面打印的信息一樣分冈,打印的都是引用變量的值圾另,也就是 內(nèi)存地址值。toString() 方法在JDK中源代碼如下:

public String toString() {
    return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

在我們的代碼中雕沉,在定義類時集乔,應(yīng)該顯式地 重寫 toString() 方法,使得調(diào)用toString()類時蘑秽,能夠 打印類的屬性信息

public String toString(){
    return "Person: " + "name:" + name + ";" + "age:" + age;
}

當然,也可以使用 Eclipse 提供的箫攀,自動生成 toString() 方法肠牲。將光標放在需要創(chuàng)建 toString()方法的類的內(nèi)部,依次選擇 Source -> Generate toString() -> OK 即可:

  • 自動生成toString()方法

自動生成的內(nèi)容靴跛,大致如下:

@Override
public String toString() {
    return "Person [name=" + name + ", age=" + age + "]";
}

這樣缀雳,當我們每次調(diào)用 toString() 方法時,返回的是 當前類的屬性信息梢睛,而不是 引用變量的值:

public class Person {
    String name = new String("魏文應(yīng)");
    int age = 12;

    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + "]";
    }
}
public class TestToString {
    public static void main(String[] args) {
        Person p1 = new Person();
        System.out.println(p1.toString());     // Person [name=魏文應(yīng), age=12]
        System.out.println(p1);                // Person [name=魏文應(yīng), age=12]
    }
}

很多類都對Object類中的toString()方法進行了重寫肥印,比如String類、Data類绝葡、File類深碱、包裝類等。比如下面的String:

public class TestToString {
    public static void main(String[] args) {    
        String str = "AA";
        System.out.println(str.toString());  // 打印結(jié)果為:AA
    }
}

上面的 toString() 方法藏畅,是打印 str 對應(yīng)的字符串的值敷硅,而不是str保存的內(nèi)存地址值。下面是JDK的 String類中的toString() 方法源代碼:

public String toString() {
    return this;
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末愉阎,一起剝皮案震驚了整個濱河市绞蹦,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌榜旦,老刑警劉巖幽七,帶你破解...
    沈念sama閱讀 212,454評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異溅呢,居然都是意外死亡澡屡,警方通過查閱死者的電腦和手機猿挚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來挪蹭,“玉大人亭饵,你說我怎么就攤上這事×豪鳎” “怎么了辜羊?”我有些...
    開封第一講書人閱讀 157,921評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長词顾。 經(jīng)常有香客問我八秃,道長,這世上最難降的妖魔是什么肉盹? 我笑而不...
    開封第一講書人閱讀 56,648評論 1 284
  • 正文 為了忘掉前任昔驱,我火速辦了婚禮,結(jié)果婚禮上上忍,老公的妹妹穿的比我還像新娘骤肛。我一直安慰自己,他們只是感情好窍蓝,可當我...
    茶點故事閱讀 65,770評論 6 386
  • 文/花漫 我一把揭開白布腋颠。 她就那樣靜靜地躺著,像睡著了一般吓笙。 火紅的嫁衣襯著肌膚如雪淑玫。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,950評論 1 291
  • 那天面睛,我揣著相機與錄音絮蒿,去河邊找鬼。 笑死叁鉴,一個胖子當著我的面吹牛土涝,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播幌墓,決...
    沈念sama閱讀 39,090評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼回铛,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了克锣?” 一聲冷哼從身側(cè)響起茵肃,我...
    開封第一講書人閱讀 37,817評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎袭祟,沒想到半個月后验残,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,275評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡巾乳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,592評論 2 327
  • 正文 我和宋清朗相戀三年您没,在試婚紗的時候發(fā)現(xiàn)自己被綠了鸟召。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,724評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡氨鹏,死狀恐怖欧募,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情仆抵,我是刑警寧澤跟继,帶...
    沈念sama閱讀 34,409評論 4 333
  • 正文 年R本政府宣布,位于F島的核電站镣丑,受9級特大地震影響舔糖,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜莺匠,卻給世界環(huán)境...
    茶點故事閱讀 40,052評論 3 316
  • 文/蒙蒙 一金吗、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧趣竣,春花似錦摇庙、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至通砍,卻和暖如春玛臂,著一層夾襖步出監(jiān)牢的瞬間烤蜕,已是汗流浹背封孙。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留讽营,地道東北人虎忌。 一個月前我還...
    沈念sama閱讀 46,503評論 2 361
  • 正文 我出身青樓,卻偏偏與公主長得像橱鹏,于是被迫代替她去往敵國和親膜蠢。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,627評論 2 350

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

  • Scala與Java的關(guān)系 Scala與Java的關(guān)系是非常緊密的@蚶肌挑围! 因為Scala是基于Java虛擬機,也就是...
    燈火gg閱讀 3,427評論 1 24
  • 20- 枚舉,枚舉原始值,枚舉相關(guān)值,switch提取枚舉關(guān)聯(lián)值 Swift枚舉: Swift中的枚舉比OC中的枚...
    iOS_恒仔閱讀 2,256評論 1 6
  • 最近班里的女生都在追一部青春校園劇《致我們單純的小美好》糖荒。在她們強烈的要求下我亦不能免俗杉辙,不得不說江辰那個...
    諸葛邪閱讀 623評論 0 0
  • 看著女兒熟睡的樣子,真的感覺此刻用全世界與我交換我都不樂意捶朵,因為我已經(jīng)擁有全世界-我的寶貝女兒
    米兒媽媽2017閱讀 151評論 0 0
  • 芝麻信用分是根據(jù)信用歷史蜘矢、行為偏好狂男、履約能力、身份特質(zhì)品腹、人脈關(guān)系這五個維度綜合評估而來的岖食。芝麻信用看重的是平時的點...
    武哥說財閱讀 2,257評論 1 1