抽象類、多態

1、抽象類(abstract class)

abstract class指的是用關鍵字abstract修飾的類,叫做抽象類,是不允許實例化的類,不能直接創建對象,必須要通過子類創建才能使用abstract類的方法。

abstract不能修飾屬性。

抽象類是不允許實例化的類,因此一般它需要被進行擴展繼承。

在類聲明中使用abstract修飾符以指示某個類只能是其他類的基類。標記爲抽象或包含在抽象類中的成員必須通過從抽象類派生的類來實現。

總結:

        1、抽象類是不能被實例化的,實例化的工作應該交由它的子實現類來完成,它只需要有一個引用即可(抽象類就是爲了被繼承而存在的)。

        2、一個類中如果有抽象方法,則這個類必須定義成抽象類(有抽象方法的類必定是抽象類,抽象類不一定要有抽象方法)。

        3、抽象類中可以包含具體的實現方法。

        4、抽象類可以繼承抽象類,子抽象類可以不用重寫父抽象類抽象方法。但可以實現父抽象類的抽象方法

        5、子實現類繼承了抽象類,必須實現抽象類中定義的所有抽象方法。

        6、如果子實現類/子抽象類重寫父抽象類具體實現方法,將覆蓋父方法實現。

        (abstract不能與final並列修飾同一個類。abstract不能與private、static、final或native並列修飾同一個方法)、、

動物抽象類

package com.ershuai.stu.other.abstractClass;

public abstract class Animal {
	
	private String name;
	private String color;
	
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getColor() {
		return color;
	}

	public void setColor(String color) {
		this.color = color;
	}

	public abstract void intro();

	public abstract void bite();
}

寵物抽象類

package com.ershuai.stu.other.abstractClass;

public abstract class Pet extends Animal {
	
	public double sellPrice;

	public double getSellPrice() {
		return sellPrice;
	}

	public void setSellPrice(double sellPrice) {
		this.sellPrice = sellPrice;
	}

	public void suggest() {
		System.out.println("如果你考慮養只寵物,建議領養哦…");
	}

	@Override
	public void bite() {
		System.out.println(this.getName() + " pet: 我是寵物,我不咬人…");
	}

	public abstract void hair(String hair);
}
狗狗實現類

package com.ershuai.stu.other.abstractClass;

public class Dog extends Pet {
	
	@Override
	public void intro() {
		System.out.println("我是一隻" + this.getColor() + this.getName());
		System.out.println("¥" + this.getSellPrice());
		super.bite();
		this.suggest();
	}

	@Override
	public void hair(String hair) {
		System.out.println(this.getName() + ":" + hair);
	}
}
貓貓實現類

package com.ershuai.stu.other.abstractClass;

public class Cat extends Pet {
	
	public Cat() {}
	
	public Cat(String name, String color, double sellPrice) {
		this.setName(name);
		this.setColor(color);
		this.setSellPrice(sellPrice);
	}
	
	@Override
	public void intro() {
		System.out.println("我是一隻" + this.getColor() + this.getName());
		System.out.println("¥" + this.getSellPrice());
		bite();
		this.suggest();
	}

	@Override
	public void bite() {
		System.out.println(this.getName() + " cat: 我是寵物,我不咬人…");
	}

	@Override
	public void hair(String hair) {
		System.out.println(this.getName() + ":" + hair);
	}
}

測試方法

package com.ershuai.stu.other.abstractClass;

/**
 * 
 * @author ershuai
 * @date 2017年6月14日 下午4:45:21
 */
public class Test0 {
	public static void main(String[] args) {
		Pet dog = new Dog();
		dog.setName("狗狗");
		dog.setColor("黑");
		dog.setSellPrice(3000.00);
		dog.intro();
		dog.hair("我是長毛,我要脫毛…");
		
		System.out.println("\r**************************\r");
		
		Pet cat = new Cat();
		cat.setName("貓貓");
		cat.setColor("黑白");
		cat.setSellPrice(2500.00);
		cat.intro();
		cat.hair("我是短毛,我要脫毛…");
	}
}

打印結果

我是一隻黑狗狗
¥3000.0
狗狗 pet: 我是寵物,我不咬人…
如果你考慮養只寵物,建議領養哦…
狗狗:我是長毛,我要脫毛…

**************************

我是一隻黑白貓貓
¥2500.0
貓貓 cat: 我是寵物,我不咬人…
如果你考慮養只寵物,建議領養哦…
貓貓:我是短毛,我要脫毛…

如果抽象類只有僅僅上面的好處,那感覺和普通的類沒有什麼大的區別,我們完全可以定義普通類實現,那麼抽象類的意義何在呢?

Pet a1 = new Dog();
Pet a2 = new Cat();
爲了多態!


2、多態

面向對象編程有三大特性:封裝、繼承、多態。

多態:多態性是允許你將父對象設置成爲一個或更多的他的子對象相等的技術,賦值之後,父對象就可以根據當前賦值給它的子對象的特性以不同的方式運作。簡單的說,就是一句話:允許將子類類型的指針賦值給父類類型的指針。

多態是面向對象的重要特性,簡單點說:“一個接口,多種實現”,就是同一種事物表現出的多種形態。

現實中,關於多態的例子不勝枚舉:比方說按下 F1 鍵這個動作,如果當前在 Flash 界面下彈出的就是 AS 3 的幫助文檔;如果當前在 Word 下彈出的就是 Word 幫助;在 Windows 下彈出的就是 Windows 幫助和支持。同一個事件發生在不同的對象上會產生不同的結果。


Java實現多態有三個必要條件:繼承、重寫、向上轉型。

繼承:在多態中必須存在有繼承關係的子類和父類。

重寫:子類對父類中某些方法進行重新定義,在調用這些方法時就會調用子類的方法。

向上轉型:在多態中需要將子類的引用賦給父類對象,只有這樣該引用才能夠具備技能調用父類的方法和子類的方法。

只有滿足了上述三個條件,我們才能夠在同一個繼承結構中使用統一的邏輯實現代碼處理不同的對象,從而達到執行不同的行爲。

對於Java而言,它多態的實現機制遵循一個原則:當超類對象引用變量引用子類對象時,被引用對象的類型而不是引用變量的類型決定了調用誰的成員方法,但是這個被調用的方法必須是在超類中定義過的,也就是說被子類覆蓋的方法。


多態經典實例

public class A {
    public String show(D obj) {
        return ("A and D");
    }

    public String show(A obj) {
        return ("A and A");
    } 

}

public class B extends A{
    public String show(B obj){
        return ("B and B");
    }
    
    public String show(A obj){
        return ("B and A");
    } 
}

public class C extends B{

}

public class D extends B{

}

public class Test {
    public static void main(String[] args) {
        A a1 = new A();
        A a2 = new B();
        B b = new B();
        C c = new C();
        D d = new D();
        
        System.out.println("1--" + a1.show(b));
        System.out.println("2--" + a1.show(c));
        System.out.println("3--" + a1.show(d));
        System.out.println("4--" + a2.show(b));
        System.out.println("5--" + a2.show(c));
        System.out.println("6--" + a2.show(d));
        System.out.println("7--" + b.show(b));
        System.out.println("8--" + b.show(c));
        System.out.println("9--" + b.show(d));      
    }
}
輸出

1--A and A
2--A and A
3--A and D
4--B and A
5--B and A
6--A and D
7--B and B
8--B and B
9--A and D

在這裏看結果1、2、3還好理解,從4開始就開始糊塗了,對於4來說爲什麼輸出不是“B and B”呢?      
首先我們先看一句話:當超類對象引用變量引用子類對象時,被引用對象的類型而不是引用變量的類型決定了調用誰的成員方法,但是這個被調用的方法必須是在超類中定義過的,也就是說被子類覆蓋的方法。這句話對多態進行了一個概括。其實在繼承鏈中對象方法的調用存在一個優先級:this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O)。      
分析:     
從上面的程序中我們可以看出A、B、C、D存在如下關係。

       首先我們分析5,a2.show(c),a2是A類型的引用變量,所以this就代表了A,a2.show(c),它在A類中找發現沒有找到,於是到A的超類中找(super),由於A沒有超類(Object除外),所以跳到第三級,也就是this.show((super)O),C的超類有B、A,所以(super)O爲B、A,this同樣是A,這裏在A中找到了show(A obj),同時由於a2是B類的一個引用且B類重寫了show(A obj),因此最終會調用子類B類的show(A obj)方法,結果也就是B and A。      
按照同樣的方法我也可以確認其他的答案。      
方法已經找到了但是我們這裏還是存在一點疑問,我們還是來看這句話:當超類對象引用變量引用子類對象時,被引用對象的類型而不是引用變量的類型決定了調用誰的成員方法,但是這個被調用的方法必須是在超類中定義過的,也就是說被子類覆蓋的方法。這我們用一個例子來說明這句話所代表的含義:a2.show(b);      

這裏a2是引用變量,爲A類型,它引用的是B對象,因此按照上面那句話的意思是說有B來決定調用誰的方法,所以a2.show(b)應該要調用B中的show(B obj),產生的結果應該是“B and B”,但是爲什麼會與前面的運行結果產生差異呢?這裏我們忽略了後面那句話“但是這兒被調用的方法必須是在超類中定義過的”,那麼show(B obj)在A類中存在嗎?根本就不存在!所以這句話在這裏不適用?那麼難道是這句話錯誤了?非也!其實這句話還隱含這這句話:它仍然要按照繼承鏈中調用方法的優先級來確認。所以它纔會在A類中找到show(A obj),同時由於B重寫了該方法所以纔會調用B類中的方法,否則就會調用A類中的方法。      

所以多態機制遵循的原則概括爲:當超類對象引用變量引用子類對象時,被引用對象的類型而不是引用變量的類型決定了調用誰的成員方法,但是這個被調用的方法必須是在超類中定義過的,也就是說被子類覆蓋的方法,但是它仍然要根據繼承鏈中方法調用的優先級來確認方法,該優先級爲:this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O)。 


謝老師

http://www.cnblogs.com/chenssy/p/3376708.html

http://www.cnblogs.com/chenssy/p/3372798.html


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章