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