1多态
1.1多态特点
- 需要继承
- 父类引用指向子类
- 方法的覆盖(需要函数名相同,参数相同)
- 抽象类
1.2父类引用指向子类
上栗子:Animal类
/*动物类*/
class Animal{
//动物有的属性
String type;
//带参数的构造器
public Animal(String type){
this.type = type;
}
//动物都有的行为(叫)
public void call(){
System.out.println(type+"在叫");
}
}
Dog类:
/*狗类*/
class Dog extends Animal{
//dog自己的一些属性
String color;
String hair;
//添加构造器
public Dog(){
super("小狗"); //调用构造函数
this.color = "黑色";
this.hair = "卷毛";
}
//覆盖父类(叫)的行为,要函数名,参数都一样
public void call(){
System.out.println(color+hair+"的"+this.type+"在叫");
}
}
main:
public static void main(String[] args) {
//父类引用指向了子类
Animal a = new Dog();
Dog dog = (dog) a; //这样就可以强制转换成Dog
a.call(); //输出:黑色卷毛的小狗在叫
}
看main方法中,父类Animal用子类来实例化,Dog 就可以指向a了
其实在用Dog来实例化是 a 不仅指向内部的父类,还有一根虚线指向外部的父类(图中红线)
看内存模型:
1.3方法的覆盖
main代码中最后一行是用a.call(),来调用用方法,为什么输出的结果与想象中的不一样呢?
那是因为在Dog类中,在写了一个与父类名字相同,参数相同的方法,这样就会覆盖了父类原有的方法,这是调用就只会调用Dog子类中的方法,这就是多态。
1.4抽象类
- 作用:就是用来被继承和函数的覆盖
- 有一个抽象函数的类就叫抽象类
- 该类不能实例化
- 子类一定要覆盖父类的抽象方法
例:将Animal类抽象
/*动物类*/
abstract class Animal{ //有了抽象方法,类名前面也加abstract关键字,否者会编译报错
//动物有的属性
String type;
//带参数的构造器
public Animal(String type){
this.type = type;
}
//将call方法抽象
public abstract void call(); //在返回值前面添加abstract关键字,并且抽象方法没有语句块,直接“;”结束
}
Dog类不变,直接main:
public static void main(String[] args) {
//*抽象类不能实例化,否者会报错
// Animal a = new Animal("xiaogou")
Dog d = new Dog();
d.call(); //输出:黑色卷毛的小狗在叫
}
1.5,为什么要抽象化
抽象其实就是给我们确定了一种规则:
如我们要用多态,就需要父类引用指向子类,子类要覆盖父类的方法,如果不小心把覆盖方法名写错了,这样是不会报错的,因为父类中有该方法。
例:如果将Dog中call()方法写成calll(),这样方法与父类的就不相同,也就不会形成覆盖
//覆盖父类(叫)的行为,要函数名,参数都一样(不小心写错了)
public void calll(){
System.out.println(color+hair+"的"+this.type+"在叫");
}
运行也不会报错,但这样输出的结果就会与预期的不一样,这样的问题就不好找到
如果抽象化了,子类就一定要覆盖这个抽象方法,要不就会报错,这样就把错误暴露出来,一下就能找到