物件導向-多型

ZHOU_VIP發表於2017-05-06

多型(掌握)

(1)同一個物件在不同時刻體現出來的不同狀態。

(2)多型的前提:

    A:有繼承或者實現關係。

    B:有方法重寫。

    C:有父類或者父介面引用指向子類物件。

    多型的分類:

        a:具體類多型

            class Fu {}

            class Zi extends Fu {}

            Fu f = new Zi();

        b:抽象類多型

            abstract class Fu {}

            class Zi extends Fu {}

            Fu f = new Zi();

        c:介面多型

            interface Fu {}

            class Zi implements Fu {}

            Fu f = new Zi();

(3)多型中的成員訪問特點

    A:成員變數

        編譯看左邊,執行看左邊

    B:構造方法

        子類的構造都會預設訪問父類構造

    C:成員方法

        編譯看左邊,執行看右邊

    D:靜態方法

        編譯看左邊,執行看左邊


/*
	多型:同一個物件(事物),在不同時刻體現出來的不同狀態。
	舉例:
		貓是貓,貓是動物。
		水(液體,固體,氣態)。
		
	多型的前提:
		A:要有繼承關係。
		B:要有方法重寫。
			其實沒有也是可以的,但是如果沒有這個就沒有意義。
				動物 d = new 貓();
				d.show();
				動物 d = new 狗();
				d.show();
		C:要有父類引用指向子類物件。
			父 f =  new 子();
			
	用程式碼體現一下多型。
	
	多型中的成員訪問特點:變數沒有重寫,用的還是父親的;因為方法有重寫,父類方法被子類覆蓋掉了,只留下子類的方法
		A:成員變數
			編譯看左邊,執行看左邊。
		B:構造方法
			建立子類物件的時候,訪問父類的構造方法,對父類的資料進行初始化。
		C:成員方法
			編譯看左邊,執行看右邊。
		D:靜態方法
			編譯看左邊,執行看左邊。
			(靜態和類相關,算不上重寫,所以,訪問還是左邊的)
			
		由於成員方法存在方法重寫,所以它執行看右邊。
*/
public class Fu {
	public int num = 100;

	public void show() {
		System.out.println("show Fu");
	}

	public static void function() {
		System.out.println("function Fu");
	}
}

public class Zi extends Fu {
	public int num = 1000;
	public int num2 = 200;

	public void show() {
		System.out.println("show Zi");
	}
	
	public void method() {
		System.out.println("method zi");
	}
	
	public static void function() {
		System.out.println("function Zi");
	}
}

public class DuoTaiDemo {
	
	public static void main(String[] args) {
		//要有父類引用指向子類物件。
		//父 f =  new 子();
		Fu f = new Zi();
		System.out.println(f.num);   //100
		//System.out.println(f.num2);//報錯:找不到符號
		
		f.show();     //show Zi
		//f.method(); //報錯:找不到符號
		f.function(); //function Fu
	}

}

(4)多型的好處:

    A:提高程式碼的維護性(繼承體現)

    B:提高程式碼的擴充套件性(多型體現)


/*
	多型的好處:
		A:提高了程式碼的維護性(繼承保證)
		B:提高了程式碼的擴充套件性(由多型保證)
		
	貓狗案例程式碼
*/
class Animal {
	public void eat(){
		System.out.println("eat");
	}
	
	public void sleep(){
		System.out.println("sleep");
	}
}

class Dog extends Animal {
	public void eat(){
		System.out.println("狗吃肉");
	}
	
	public void sleep(){
		System.out.println("狗站著睡覺");
	}
}

class Cat extends Animal {
	public void eat() {
		System.out.println("貓吃魚");
	}
	
	public void sleep() {
		System.out.println("貓趴著睡覺");
	}
}

class Pig extends Animal {
	public void eat() {
		System.out.println("豬吃白菜");
	}
	
	public void sleep() {
		System.out.println("豬側著睡");
	}
}

//針對動物操作的工具類
class AnimalTool {
	private AnimalTool(){}

	/*
	//呼叫貓的功能
	public static void useCat(Cat c) {
		c.eat();
		c.sleep();
	}
	
	//呼叫狗的功能
	public static void useDog(Dog d) {
		d.eat();
		d.sleep();
	}
	
	//呼叫豬的功能
	public static void usePig(Pig p) {
		p.eat();
		p.sleep();
	}
	*/
	public static void useAnimal(Animal a) {
		a.eat();
		a.sleep();
	}
	
}

class DuoTaiDemo2 {
	public static void main(String[] args) {
		//我喜歡貓,就養了一隻
		Cat c = new Cat();
		c.eat();
		c.sleep();
		
		//我很喜歡貓,所以,又養了一隻
		Cat c2 = new Cat();
		c2.eat();
		c2.sleep();
		
		//我特別喜歡貓,又養了一隻
		Cat c3 = new Cat();
		c3.eat();
		c3.sleep();
		//...
		System.out.println("--------------");
		/*
		問題來了,我養了很多隻貓,每次建立物件是可以接受的
		但是呢?呼叫方法,你不覺得很相似嗎?僅僅是物件名不一樣。
		我們準備改進
		*/
		//useCat(c);
		//useCat(c2);
		//useCat(c3);
		
		//AnimalTool.useCat(c);
		//AnimalTool.useCat(c2);
		//AnimalTool.useCat(c3);
		
		AnimalTool.useAnimal(c);
		AnimalTool.useAnimal(c2);
		AnimalTool.useAnimal(c3);
		System.out.println("--------------");
		
		//我喜歡狗
		Dog d = new Dog();
		Dog d2 = new Dog();
		Dog d3 = new Dog();
		//AnimalTool.useDog(d);
		//AnimalTool.useDog(d2);
		//AnimalTool.useDog(d3);
		AnimalTool.useAnimal(d);
		AnimalTool.useAnimal(d2);
		AnimalTool.useAnimal(d3);
		System.out.println("--------------");
		
		//我喜歡寵物豬
		//定義一個豬類,它要繼承自動物,提供兩個方法,並且還得在工具類中新增該類方法呼叫
		Pig p = new Pig();
		Pig p2 = new Pig();
		Pig p3 = new Pig();
		//AnimalTool.usePig(p);
		//AnimalTool.usePig(p2);
		//AnimalTool.usePig(p3);
		AnimalTool.useAnimal(p);
		AnimalTool.useAnimal(p2);
		AnimalTool.useAnimal(p3);
		System.out.println("--------------");
		
		/*
		我喜歡寵物狼,老虎,豹子...
		定義對應的類,繼承自動物,提供對應的方法重寫,並在工具類新增方法呼叫
		前面幾個必須寫,我是沒有意見的
		但是,工具類每次都改,麻煩不
		我就想,你能不能不改了
		太簡單:把所有的動物都寫上。問題是名字是什麼呢?到底哪些需要被加入呢?
		改用另一種解決方案。
		public static void useAnimal(Animal a) {
			a.eat();
			a.sleep();
		}
		*/
		
	}
	
}

(5)多型的弊端:

    父不能使用子的特有功能。

    現象:

        子可以當作父使用,父不能當作子使用。


/*
	多型的弊端:
		不能使用子類的特有功能。
*/
class Fu {
	public void show() {
		System.out.println("show fu");
	}
}

class Zi extends Fu {
	public void show() {
		System.out.println("show zi");
	}
	
	public void method() {
		System.out.println("method zi");
	}

}

class DuoTaiDemo3 {
	public static void main(String[] args) {
		//測試
		Fu f = new Zi();
		f.show();  //show zi
		f.method();//報錯:方法未定義
	}
}

(6)多型中的轉型

    A:向上轉型

        從子到父,父類引用指向子類物件:Fu f = new Zi();

    B:向下轉型

        從父到子,父類引用轉為子類物件:Zi  z = (Zi)f;


/*
	多型的弊端:
		不能使用子類的特有功能。
		
	我就想使用子類的特有功能?行不行?
		行。
		
	怎麼用呢?
		A:建立子類物件呼叫方法即可。(可以,但是很多時候不合理。而且,太佔記憶體了)
		B:把父類的引用強制轉換為子類的引用。(向下轉型)
		
	物件間的轉型問題:
		向上轉型:
			Fu f = new Zi();
		向下轉型:
			Zi z = (Zi)f; //要求該f必須是能夠轉換為Zi的。
*/
class Fu {
	public void show() {
		System.out.println("show fu");
	}
}

class Zi extends Fu {
	public void show() {
		System.out.println("show zi");
	}
	
	public void method() {
		System.out.println("method zi");
	}

}

class DuoTaiDemo4 {
	public static void main(String[] args) {
		//測試
		Fu f = new Zi();
		f.show();  //show zi
		//f.method();//報錯:方法未定義
		
		//建立子類物件,可以,但是很多時候不合理。而且,太佔記憶體了。
		//Zi z = new Zi();
		//z.show();
		//z.method();
		
		//你能夠把子的物件賦值給父親,那麼我能不能把父的引用賦值給子的引用呢?
		//如果可以,程式碼如下:
		//向下轉型
		Zi z = (Zi)f;
		z.show();  //show zi
		z.method();//method zi
	}
}

(7)孔子裝爹的案例幫助大家理解多型

多型的問題理解:
	class 孔子爹 {
		public int age = 40;
		
		public void teach() {
			System.out.println("講解JavaSE");
		}
	}
	
	class 孔子 extends 孔子爹 {
		public int age = 20;
		
		public void teach() {
			System.out.println("講解論語");
		}
		
		public void playGame() {
			System.out.println("英雄聯盟");
		}
	}
	
	//Java培訓特別火,很多人來請孔子爹去講課,這一天孔子爹被請走了
	//但是還有人來請,就剩孔子在家,價格還挺高。孔子一想,我是不是可以考慮去呢?
	//然後就穿上爹的衣服,帶上爹的眼睛,粘上爹的鬍子。就開始裝爹
	//向上轉型
	孔子爹 k爹 = new 孔子();
	//到人家那裡去了
	System.out.println(k爹.age);      //40
	System.out.println(k爹.teach());  //講解論語
	//k爹.playGame();                 //這不行,這是兒子才能做的
	
	
	//講完了,下班回家了,脫下爹的裝備,換上自己的裝備
	//向下轉型
	孔子 k = (孔子) k爹; 
	System.out.println(k.age); //20
	k.teach();                 //講解論語
	k.playGame();              //英雄聯盟


補充:多型繼承中的記憶體圖解


補充:一般在多型的向下轉型中容易出現ClassCastException:型別轉換異常



/*
	ClassCastException:型別轉換異常
	一般在多型的向下轉型中容易出現
*/
class Animal {
	public void eat(){}
}

class Dog extends Animal {
	public void eat() {}
	
	public void lookDoor() {
	
	}
}

class Cat extends Animal {
	public void eat() {
	
	}
	
	public void playGame() {
		
	}
}

class DuoTaiDemo5 {
	public static void main(String[] args) {
		//記憶體中的是狗
		Animal a = new Dog();
		Dog d = (Dog)a;
		
		//記憶體中是貓
		a = new Cat();
		Cat c = (Cat)a;
		
		//記憶體中是貓
		Dog dd = (Dog)a; //ClassCastException
	}
}

(8)多型的練習


/*
	多型練習:貓狗案例
*/
class Animal {
	public void eat(){
		System.out.println("吃飯");
	}
}

class Dog extends Animal {
	public void eat() {
		System.out.println("狗吃肉");
	}
	
	public void lookDoor() {
		System.out.println("狗看門");
	}
}

class Cat extends Animal {
	public void eat() {
		System.out.println("貓吃魚");
	}
	
	public void playGame() {
		System.out.println("貓捉迷藏");
	}
}

class DuoTaiTest {
	public static void main(String[] args) {
		//定義為狗
		Animal a = new Dog();
		a.eat();//狗吃肉

		//還原成狗
		Dog d = (Dog)a;
		d.eat();     //狗吃肉
		d.lookDoor();//狗看門

		//變成貓
		a = new Cat();
		a.eat();//貓吃魚

		//還原成貓
		Cat c = (Cat)a;
		c.eat();     //貓吃魚
		c.playGame();//貓捉迷藏

		
		//演示錯誤的內容
		//Dog dd = new Animal();//不相容的型別
		//Dog ddd = new Cat();  //不相容的型別
		
		//Dog dd = (Dog)a;      //ClassCastException
		
	}
}	


/*
	不同地方飲食文化不同的案例
*/
class Person {
	public void eat() {
		System.out.println("吃飯");
	}
}

class SouthPerson extends Person {
	public void eat() {
		System.out.println("炒菜,吃米飯");
	}
	
	public void jingShang() {
		System.out.println("經商");
	}
}

class NorthPerson extends Person {
	public void eat() {
		System.out.println("燉菜,吃饅頭");
	}
	
	public void yanJiu() {
		System.out.println("研究");
	}
}

class DuoTaiTest2 {
	public static void main(String[] args) {
		//測試
		//南方人
		Person p = new SouthPerson();
		p.eat();         //炒菜,吃米飯

		SouthPerson sp = (SouthPerson)p;
		sp.eat();        //炒菜,吃米飯
		sp.jingShang();  //經商

		
		//北方人
		p = new NorthPerson();
		p.eat();         //燉菜,吃饅頭

		NorthPerson np = (NorthPerson)p;
		np.eat();        //燉菜,吃饅頭
		np.yanJiu();     //研究
	}
}

看程式寫結果:

/*
	看程式寫結果:先判斷有沒有問題,如果沒有,寫出結果
	
	多型的成員訪問特點:
		方法:編譯看左邊,執行看右邊。
		
	繼承的時候:
		子類中有和父類中一樣的方法,叫重寫。
		子類中沒有父親中出現過的方法,方法就被繼承過來了。
*/
class A {
	public void show() {
		show2();
	}
	public void show2() {
		System.out.println("我");
	}
}
class B extends A {
	/*可以理解存在該程式碼,因為繼承了父類的方法
	public void show() {
		show2();
	}
	*/

	public void show2() {
		System.out.println("愛");
	}
}
class C extends B {
	public void show() {
		super.show();
	}
	public void show2() {
		System.out.println("你");
	}
}
public class DuoTaiTest4 {
	public static void main(String[] args) {
		A a = new B();
		a.show();  //愛
		
		B b = new C();
		b.show();  //你
	}
}


相關文章