Java基礎學習筆記(十二)—— 多態

Java基礎學習筆記(十二)—— 多態

You want something. Go get it!

| @Author:TTODS


什麼是多態?

多態是同一個行爲具有多個不同表現形式或形態的能力。(下面的例子可以幫助理解)
我們先建一個Person類,再建一個Person類的子類Student類,Student類中都重寫了toString的方法,且StudentPerson類多了一個School的成員變量。

package first;

public class Person{
	String name;
	int age;
	Person(String _name,int _age){
		name = _name;
		age = _age;
	}
	public String toString() {
		return String.format("Person [ name:%s , age:%d]",name,age);
	}
}
package first;

public class Student extends Person{
	String school;
	Student(String _name,int _age,String _school){
		super(_name,_age);//調用父類的構造方法
		school = _school;
	}
	public String toString() {
		return String.format("Student [ name:%s , age:%d, school:%s]",name,age,school);
	} 
	public static void main(String[] args) {
		Person A = new Person("小A",20); //Person類型的引用指向Person類型的實例
		Person B = new Student("小B",20,"XX學校");//Person類型的引用指向Student類型的實例
		Student C = new Student("小C",10,"XX學校");//Student類型的引用指向Student類型的實例
		System.out.println(A);
		System.out.println(B);
		System.out.println(C);
	}
}

輸出:

Person [ name:小A , age:20]
Student [ name:小B , age:20, school:XX學校]
Student [ name:小C , age:10, school:XX學校]

從上例可以看到一個奇怪的用法,其中的變量B是一個Person類型的引用,但卻指向了一個Student的實例,但這並不難理解,StudentPerson的子類,或者說Student也是一個Person.值得注意的是對於上面三個Person引用調用的toString方法卻有所不同,準確的說是調用了實例的方法。現在因該不難理解"一樣的類型的同一個方法有不同的表現形式"這句話了。

多態發生的前提條件

  1. 繼承。多態發生一定要子類和父類之間。
  2. 覆蓋。子類覆蓋了父類的方法。
  3. 聲明的變量類型是父類類型,但實例則指向子類實例。

多態有什麼用?

先看下面這例子:狼人殺是前段時間很火的一個遊戲,我們先建一個玩家類,如下:Player類有一個成員變量name.

package first;

public class Player{
	protected String name;
	Player(String n){
		name = n;
	}
	void dead() {
		System.out.printf("%s(玩家) 出局了,OOO~\n",name);
	}

建兩個Player類的子類,Seer類和Villager類,他們都重寫dead方法。

package first;

public class Seer extends Player{
	Seer(String n){
		super(n);//調用父類的構造函數  
	}
	void dead() {
		System.out.printf("%s(先知) 出局了,OOO~\n",name);
	}
}
package first;

public class Villager extends Player{
	Villager(String n){
		super(n);
	}
	void dead() {
		System.out.printf("%s(村民) 出局了,OOO~\n",name);
	}
}

再建一個Werewolf類,它也重寫了dead方法(雖然此例中不需要用),此外他新增了一個kill方法和用於運行的main方法。

package first;

public class Werewolf extends Player{
	Werewolf(String n){
		super(n);
	}
	void dead() {
		System.out.printf("%s(狼人) 出局了,OOO~\n",name);
	}
	void kill(Player p) {
		p.dead();
	}
	public static void main(String[] arg) {
		Werewolf werewolf1 = new Werewolf("小T");
		Seer seer = new Seer("小白");
		Villager villager1 = new Villager("小紅"); 
		werewolf1.kill(seer);
		werewolf1.kill(villager1);
	}
}

輸出

小白(先知) 出局了,OOO~
小紅(村民) 出局了,OOO~

從上例中可以看到,在Werewolf類中的kill方法接受的參數是Player類型,在kill中調用的是Playerdead;但是kill卻可以接收Seer類型和Villager類型的變量,並且分別調用它們的dead方法。這給我們帶來了極大的便利,因爲我們沒必要重載多個接受不同參數的kill方法。

instanceof 關鍵字

有時我們需要用instanceof關鍵字來判斷一個對象的具體類型,方法如下:
改寫第一個例子中的Student類的main方法

package first;

public class Student extends Person{
	String school;
	Student(String _name,int _age,String _school){
		super(_name,_age);
		school = _school;
	}
	public String toString() {
		return String.format("Student [ name:%s , age:%d, school:%s]",name,age,school);
	} 
	public static void main(String[] args) {
		Person A = new Person("小A",20);
		Person B = new Student("小B",20,"XX學校");
		Student C = new Student("小C",10,"XX學校");
		System.out.println("A instanceof Person: "+(A instanceof Person));
		System.out.println("A instanceof Student: "+(A instanceof Student));
		System.out.println("B instanceof Person: "+(B instanceof Person));
		System.out.println("B instanceof Student: "+(B instanceof Student));
		System.out.println("C instanceof Person: "+(C instanceof Person));
		System.out.println("C instanceof Student: "+(C instanceof Student));
		System.out.println("C instanceof Object: "+(C instanceof Object));
	}
}

輸出

A instanceof Person: true
A instanceof Student: false
B instanceof Person: true
B instanceof Student: true
C instanceof Person: true
C instanceof Student: true
C instanceof Object: true

不支持多態的方法

先了解這樣一個概念:方法綁定,即將一個方法調用和一個方法主體關聯起來。方法綁定又分爲前期綁定後期綁定,前期綁定就是在程序運行之前(比如編譯過程)進行綁定。但是回到我們多態發生的特殊情景,由於引用類型與實例類型並不相同,編譯器只知道有一個Person類型的引用,並不知道它指向的是一個Student的實例,它無法知道應該調用誰的方法,於是就有了後期綁定即運行時綁定。(關於方法綁定在《on java8》(《java編程思想第5版》)第九章中有詳細介紹)。
Java 中除了 staticfinal 方法(private 方法也是隱式的 final)外,其他所有方法都是後期綁定。也就是說staticfinalprivate方法都是直接調用引用類型的方法。

package first;

public class Person{
	String name;
	int age;
	Person(String _name,int _age){
		name = _name;
		age = _age;
	}
	static void sleep() {
		System.out.println("Person sleep()");
	}
	final void code() {
		System.out.println("Person code()");
	}
	private void eat() {
		System.out.println("Person eat()");
	}
	public String toString() {
		return String.format("Person [ name:%s , age:%d]",name,age);
	}
}
package first;

public class Student extends Person{
	String school;
	Student(String _name,int _age,String _school){
		super(_name,_age);
		school = _school;
	}
	public String toString() {
		return String.format("Student [ name:%s , age:%d, school:%s]",name,age,school);
	} 
	public void eat() {
		System.out.println("Student eat()");
	}
	static void sleep() {
		System.out.println("Student sleep()");
	}
	public static void main(String[] args) {
		Person A = new Person("小A",20);
		Person B = new Student("小B",20,"XX學校");
		Student C = new Student("小C",10,"XX學校");
		//B.eat(); //編譯錯誤,eat()是Person的private方法,在Student中不可視,雖然Student中也寫了eat()方法,但這是一個全新的eat方法並不是繼承。
		B.code();
		B.sleep();
	}
}

輸出

Person code()
Person sleep()

eat()Personprivate方法,在Student中不可視,雖然Student中也寫了eat()方法,但這是一個全新的eat方法並不是繼承。
code()Personfinal方法,在Student中無法覆蓋,不能重寫


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