类的多态
类的多态 =》向上转型 + 方法覆写
先来回顾一下类的实例化
类名 引用=new 类名();
Animal animal = new Animal();
Cat cat = new Cat();
Dog dog = new Dog();下面有三个类,动物类,猫类和狗类继承动物类。
1.向上引用
向上引用最大意义在于参数统一化。可以用一个父类引用,接收所有子类实例。
向上引用发生在三个地方:
1.1直接赋值
父类名 父类引用=new 子类实例();
public class Test {
public static void main(String[] args) {
Animal animal=new Animal("动物",6);
Animal cat=new Cat("毛毛",3);
Animal dog=new Dog("旺财",4);
animal.eat("食物");
cat.eat("猫粮");
dog.eat("狗粮");
}
}
1.2.方法函数参数
public class Test {
public static void main(String[] args) {
Animal animal=new Animal("动物",6);
Cat cat=new Cat("毛毛",3);
Dog dog=new Dog("旺财",4);
fun(animal,"食物"); /** 向上转型**/
fun(cat,"猫粮");
fun(dog,"狗粮");
}
public static void fun(Animal animal,String food){
animal.eat(food);
}
}
结果:fun()函数的参数是Animal类,但输入Cat和Dog类也成功了,因为这时发生了向上转型,猫类和狗类是动物类的子类,有天然继承关系。即猫类和狗类 是 动物类。
1.3.函数返回值
public class Test {
public static void main(String[] args) {
Animal animal=test();
animal.eat("美味");
}
public static Animal test(){
return new Dog("GOU",6); /** 向上转型 **/
}
}
结果:test()函数实际返回Dog类型与声明Animal类型不冲突,此时发生向上转型,Dog 是天然的Animal类。
2.方法覆写(override)
2.1 概念
方法覆写:对于存在继承关系的类时,子类定义了与父类完全相同的方法。
方法重载:在一个类中,定义若干个方法名字相同,但是参数列表不同(参数类型,参数个数)的方法。
问题:父类和子类都有eat方法,那么到底调用哪个类的eat()方法?
回答:看是哪个类new 出来的对象,就调用哪个类的方法。上述三个地方都发生了向上引用,但是eat()方法都是由new 出来的类实现的。
Animal animal=new Animal("动物",6);
Animal cat=new Cat("毛毛",3);
Animal dog=new Dog("旺财",4);
animal.eat("食物");
cat.eat("猫粮"); //看是谁 new 出来的
dog.eat("狗粮");
2.2 方法覆写三大要求:
只能重写成员方法/实例方法,不能覆写静态(static)方法(方法重载可以)
下图,给Dog类覆写父类静态方法出错
子类方法的权限修饰 >= 父类
父类是public ,子类只能是public
父类是包,子类可以是包或public
问:当父类使用private的方法,子类使用public 覆写该方法是否成功?
答:不能
包访问权限:只有同级目录下的类之间可见。
方法覆写时主义是否在同一目录下。
方法覆写的返回值必须相同,至少是向上转型
3.向下转型
3.1 概念
向下转型:(要能发生向下转型,必须先发生向上转型)-强制发生类型转换。
一定可以向下转型? 不一定=父类不一定是子类类型 dog is an animal ->animal is a dog?
Animal animal=new Dog();
Dog dog=(dog)animal;
这里可以发生向下转型,是因为animal本质是Dog类的,只是用父类引用,所以可以向下转型。
什么时候使用向下转型? 当使用子类独有的方法时,需要将父类的引用还原
向下转型是有风险的,若强制转型的两个类没有关系,会出错
Cat cat=new Cat();
Dog dog=(Dog)cat; //出错,两个类没有关系
3.2 instanceof 关键字
引用 instanceof类,表示该引用是否指向这个类的实例
大坑: 下面代码结果是多少?
class A{
public A(){
this.func();
}
public void func(){
System.out.println("A 的 func");
}
}
public class B extends A{
private int num;
public B(int num){
this.num=num;
}
public void func(){
System.out.println("B 的 func"+"num ="+num);
}
public static void main(String[] args) {
new B(100);
}
}
结果:
下面为new B(100)的实例化过程
private int num;
public B(int num)
{
super();// B继承A,先调用A类的无参构造
int num=100;
this.num=num;
}
super()调用A类的构造方法-》调用func函数
但A类和B类都有func函数,调哪一个?
new B();由B类new 出来的,所以,覆写方法调用B类中的func函数,同时num默认赋值为0;
4. final关键字
final关键字(终结器)
1.修饰属性,表示该属性为常量,不能修改
2.修饰类,表示该类不能有子类
3.修饰方法,表示该方法不能被覆写
byte b1=1,b2=2,b3,b4,b5;
final byte b6=4,b7=7,b8=9;
//以下哪个是对的?
b3=b1+b2; (1)
b4=b6+b7; (2)
b5=b1+b6; (3)
b8=b2+b7; (4)
答案:只有(2)是正确的;
(1)错,byte类型在存储时会转为 int 类型,所以byte运算时要注意强转 b3=(byte) b1+b2;
(2)对,final 定义byte值,不仅值不能变,类型也不会变,所以(2)正确。
(3) 错,int +byte =int
(4)错,final byte 值不能变
5.多态
多态性:如下图所示,fun函数通过一个引用,可以输出多种状态
多态的好处:
1.使用门槛低
2.方便扩展
多态主要是 :向上转型 + 方法覆写
1.上述代码执行前提,子类覆写了print方法,否则无法实现多态
2.子类没有覆写想要的方法时,编译不出错,对于子类而言,是否方法覆写是可选择的
3.如果要强制子类进行方法覆写,则父类使用abstract进行抽象,则子类必须覆写,否则出错。
6.抽象类
抽象类:普通类的超集,只是比普通类多了一些抽象方法。java 中用abstract定义抽象类和抽象方法。
普通类写法:
public class Sharp {
public void print(){ }
}
抽象类写法:
Sharp类是一个抽象类,print方法是一个抽象方法。抽象方法一定在抽象类里。抽象方法只有声明,没有方法体{}
public abstract class Sharp {
public abstract void print();
}
重点:
1.抽象方法无法直接实例化对象 Sharp sharp=new Sharp() 错
2.子类继承抽象类,子类必须覆写所有抽象方法(普通子类),若子类也为抽象类,则子类可选择是否覆写父类抽象方法。
3.抽象类仍然可以有构造类,普通方法等(普通类有的,抽象类也有)。子类仍然遵循对象的实例化流程,先调用父类构造
4.抽象类仍然是单继承局限,一个类只能继承一个类
问:
final 和 abstract 能否同时出现?
private 和 abstract 能否同时出现?
答:都不能
final 不能有子类,抽象类必须有子类
private 当前类的内部可见,子类无法覆写。抽象方法必须覆写。
抽象类的意义:强制子类覆写抽象方法,保证多态的正确运行。