多态的语法基础:
1.什么是向上转型和向下转型!
向上转型和向下转型只针对具有继承关系的类而言,没有继承关系的两个类进行转型编译器会报错
1.向上转型(自动类型转换)
即:父类的引用指向子类的对象
子---->父
2.向下转型(强制类型转换)
需要强制类型转换符才能实现转换。
2.什么是多态?
顾名思义:多种形态,多种状态!
多态的基本语法是父类的引用指向子类的对象!!!
Animal a = new Dog();
多态分为编译阶段和运行阶段。
对于上述代码:
编译阶段:编译器对于引用变量a会自动绑定父类的方法
运行阶段:代码运行阶段会动态绑定子类型对象的方法。
比如:
public class Test01{
public static void main(String[] args){
Animal a = new Dog();
a.move();
//这里编译器只知道a是Animal类的,编译器会去Animal.class字节码文件
//中寻找move()方法,找到了就绑定上move()方法,编译通过。
//(编译阶段为静态绑定)
//运行阶段,实际上在堆内存中创建的是Dog类型的对象,所以在move的时候,
//执行的是对象的move()方法,这个过程为运行阶段绑定。
//(运行阶段绑定为动态绑定)
//多态就是多种形态:
//编译阶段是静态,
//运行阶段是动态
}
}
//以上代码能编译并运行
多态就是多种形态:
编译阶段静态绑定引用类型(父类);
运行阶段动态绑定实际对象的类型(子类);
3.分析子类独有的方法在多态中怎么使用?
如何分析一个代码能否执行?
一定要从编译阶段和运行阶段去分析。
编译阶段时静态绑定不通过,没有编译,根本就轮不到运行。
分析以下代码:
1.子类
public class Dog extends Animal{
public void move(){
System.out.println("dog在汪汪叫");
}
public void run(){
System.out.println("dog,run!!");
}
}
2.父类
public class Animal{
public void move{
System.out.println("动物在移动");
}
}
3.测试类
public class Test02{
public static void main(String[] args){
Animal a = new Dog();
//下面能执行吗?
a.run();
}
}
编译阶段:编译器认为a是Animal类的引用变量,会去 Animal. class 字节码文件中寻找run()方法,找不到,静态绑定失败,编译报错!
那么,为了使多态机制下,父类调用子类独有的方法,可以使用向下转型:
public class Test02{
public static void main(String[] args){
Animal a = new Dog();
//下面能执行吗?
//不能
//错误: 找不到符号
//编译器在Animal类中找不到run()方法
//a.run();
((Dog)a).run();
//或者
Dog d = (Dog)a;
d.run();
}
}
4.向下转型有没有什么风险?
举个例子:
1.父类:
public class Animal{
public void move(){
System.out.println("动物在移动");
}
}
2.子类 Dog类:
public class Dog extends Animal{
public void move(){
System.out.println("dog在汪汪叫");
}
public void run(){
System.out.println("dog,run!!");
}
}
3.子类 Cat类:
public class Cat extends Aniaml{
public void move(){
System.out.println("Cat喵喵喵!");
}
}
4.测试类:
public class Test03{
public static void main(String[] args){
Animal a = new Cat();
//下面的代码会不会报错?
//编译阶段:编译器只知道a是Animal类型的,而Animal类
//和类Dog有继承关系,所以下面的向下转型编译通过。
//运行阶段:实际上在堆内存中开辟的对象是Cat,
//在运行时转换成Dog类型的
//对象,因为Cat和Dog没有继承关系,运行阶段会抛出异常:
//java.lang.ClassCastException: Cat cannot be cast to Dog
Dog d = (Dog)a;
d.run();
}
}
以上代码编译通过,但是在运行阶段会抛出类型转换异常:
//java.lang.ClassCastException: Cat cannot be cast to Dog
**这说明向下转型存在风险。怎么避免此类风险的发生?**即怎么避免 ClassCastException 的发生?
使用 instanceof 关键字:(运行阶段动态判断)
1.instanceof可以在运行阶段动态判断引用指向的对象的类型
语法:引用变量 instanceof 类名
Animal a = new Cat();
if(a instanceof Cat){
//...
}
该运算符是一个关系表达式,用于判断引用变量指向的堆内存中的JAVA对象是否属于后面的类,是则返回true,不是则返回false;
public class Test04{
public static void main(String[] args){
Animal a = new Animal();
Animal b = new Dog();
Dog d = new Dog();
System.out.println(a instanceof Animal);//true
System.out.println(b instanceof Animal);//true
System.out.println(b instanceof Dog);//true
System.out.println(b instanceof Cat);//false
System.out.println(b instanceof Object);//true
System.out.println(d instanceof Object);//true
System.out.println(d instanceof Dog);//true
}
}
5.为什么要instanceof判断?
考虑到向下转型存在一定的风险:应该多使用instanceof运算符:
- 不是所有代码都能看出传递的对象在底层上属于什么具体对象。比如:父类的引用指向子类,你只知道父类的引用变量,但是具体指向哪个子类呢?不是所有代码都能一眼看出来的,例如:
public class AnimalTest{
public void f(Animal a){
//这是一个实例方法
/*
对于这个实例方法,别人再调用时传过来一个Animal类型的引用,
实际上该引用可以指向Animal类任意子类的对象,可以是
Dog,也可以是Cat
//此时并不像之前那样可以通过肉眼观察出传递的引用在底层指向了
什么具体对象,所以需要instanceof运算符进行判断,
避免出现类型转换错误
*/
if(a instanceof Dog){
Dog d = (Dog)a;
d.run();
}else if(a instanceof Cat){
Cat c = (Cat)a;
c.sleep();
}
}
}
- 测试类:
public class Test05{
public static void main(String[] args){
AnimalTest t = new AnimalTest();
t.f(new Dog());
t.f(new Cat());
}
}
/*
dog,run!
cat在睡觉!
*/
向下转型一定要使用instanceof 运算符进行判断!!!!