多态
相同类型的不同对象,调用同一个方法,最终执行结果是不同的
多态的前提:
- 子类继承父类
- 子类重写父类中的方法
- 父类的引用指向子类的方法
注意,类实现接口,这是一种特殊形式的继承,多态也可以体现在类和接口的关系中。
编译时多态和运行时多态的区别
编译时多态:即在编译时就能够确定调用哪个方法。
运行时多态:只有在运行时才能确定调用哪个方法。
编译时多态 和 运行时多态在 重载 和 重写上的区别。
在方法重载时,都是编译时多态。在编译期可根据参数的数据类型、个数以及次序来确定调用方法
在方法重写中,当子类对象引用自身类实例方法时,为编译时多态。但是当父类对象引用子类实例方法时(也就是上转型对象时,父类声明,子类实例化),为运行时多态,因为此时只有在运行时才可以去匹配到对应方法进行调用。
多态的应用
public class Animal {
public void eat(){}
public static void main(String[] args) {
//父类引用指向子类对象
Animal cat = new Cat();
cat.eat();
Animal dog = new Dog();
dog.eat();
}
}
class Cat extends Animal {
@Override
public void eat() {
System.out.println("我是猫");
}
}
class Dog extends Animal {
@Override
public void eat() {
System.out.println("我是狗");
}
}
一个父类型的引用,可以指向它的任何一个子类对象,指向哪个子类就调用那个子类的方法
关键点在于,在调用eat方法的时候,引用person指向的对象是谁
public class Game {
public void play(){
}
public static void main(String[] args) {
Game b =new BasketBall();
b.play();
Game f=new Football();
f.play();
}
}
class BasketBall extends Game{
public void play(){
System.out.println("篮球游戏。。。。");
}
}
class Football extends Game{
public void play(){
System.out.println("足球游戏。。。。");
}
}
增加或减少游戏种类变得更加方便;
使用多态,这时候想新增一个球类游戏,并且去运行它,几乎不需要修改什么代码,只需要新增这个类即可
instanceof关键字
在使用多态的情况下,instanceof关键字显得特别重要,因为它告诉我们,当前父类的引用,到底是执行的哪一个子类对象
通过instanceof关键字进行判断即可知道引用p到底接收的是一个类型的对象:
public void test(Person p){
if(p instanceof Student){
//说明p指向的是Student类型对象
}
else if(p instanceof Teacher){
//说明p指向的是Teacher类型对象
}
}
引用类型转换
向上转型(子类转父类)
多态本身就是将子类的对象赋值给父类的引用,这就是一个自动的向上转换的过程(类型自动转换)
例如:Person p = new Student();
向下转型(父类转子类)
父类类型向子类类型转换,是向下转换的过程。(类型强制转换)最好先用instanceof判断是否属于该类,然后再转换,
例如:
Person p = new Student();
Student s = (Student)p; //向下转型
为什么需要做向下类型转换:
当使用多态方式调用方法时,编译器会先检查父类中是否有该方法,如果父类中没有,则编译报错;
在这种情况下,即使子类中有这个方法,也是无济于事,因为编译器这一关都过不了,更不要说去运行
也就是说,在使用多态的情况下,父类引用是无法调用到子类中独有的方法;
public class Animal {
public void eat(){}
public static void main(String[] args) {
Animal bird = new Bird();
bird.fly(); //父类无fly方法,报错
Bird bird1 = (Bird) bird; //向下转型,获得子类特有方法
bird1.fly();
}
}
class Bird extends Animal {
@Override
public void eat() {
System.out.println("我是鸟,我能吃东西");
}
//子类特有方法
public void fly() {
System.out.println("我是鸟,我能飞行");
}
}
引用类型强制转换时的异常:
在类型强制转换的过程中,可能会遇到类型转换异常,例如:
public class Person {
}
class Students extends Person{
//注意,这是子类中单独定义的方法,和父类无关
public void study(){}
}
class Teacher extends Person{
public static void main(String[] args) {
Person p = new Teacher();
//编译通过,运行报错!
//错误类型是:ClassCastException
Students s = (Students)p;
s.study();
}
}
因为
Teacher
对象和Student
类型没有任何子父类的关系,类型转换需要有父子类关系
加上判断条件,代码如下,解决报错
public static void main(String[] args) {
Person person = new Teacher();
//person如果属于Students类着返回true,执行下面代码
if(person instanceof Students){
Students s = (Students)p;
s.study();
}
}