Java语言中的多态
package com.company.test01;
public class Animal {
public void move(){
System.out.println("动物在移动!");
}
}
package com.company.test01;
//鸟儿类
public class Bird extends Animal {
//重写 重父类中继承过来的方法
public void move() {
System.out.println("鸟儿在飞翔");
}
public void fly(){
System.out.println("I can fly,I can touch the sky! ");
}
}
package com.company.test01;
//猫类
public class Cat extends Animal {
//重写 重父类中继承过来的
public void move(){
System.out.println("猫在走猫步!");
}
//不是从父类中继承过来的方法
//这个方法是子类对象特有的行为
public void catchMouse(){
System.out.println("猫抓老鼠");
}
}
package com.company.test01;
/*
* 关于java语言当中的多态语法机制:
* 1、Animal、Cat、Bird三个类之间的关系:
* Cat继承Animal
* Bird继承Animal
* Cat和Bird之间没有任何继承关系
* 2、面向对象的三大特征:封装、继承、多态
* 3、关于多态中涉及的几个概念:
* *向上转型(upcasting)
* 子类型 --> 父类型
* 有被称为:自动类型转换
* *向下转型(downcasting)
* 父类型 --> 子类型
* 有被称为:强制类型转换【需要加强制类型转换符】
* *需要注意!!!:
* 无论是向上转型还是向下转型,两者类型之间必须要有继承关系。
* 没有继承关系,程序是无法编译通过的。
* */
public class Test {
public static void main(String[] args) {
//以前编写的程序
Animal a1 = new Animal();
a1.move();
Cat c1 = new Cat();
c1.move();
Bird b1 = new Bird();
b1.move();
//使用多态语法机制
/*
* 1、Animal和Cat之间存在继承关系,Animal是父类,Cat是子类
* 2、Cat is a Animal
* 3、new Cat()创建的是对象类型是Cat,a2这个引用的数据类型是Animal,可见他们进行了类型转换,
* 子类型转换成父类型,称为向上转型/upcasting,或者称为自动类型转换。
* 4、Java中允许这种语法:父类型引用指向子类型对象。
* */
Animal a2 = new Cat();
a2.move(); //猫在走猫步!
/*
* 1.编译阶段编译器检查a2这个引用的数据类型为Animal,由于Animal.class字节码当中有move()方法,所以编译通过了。
* 这个过程我们称之为静态绑定,编译阶段绑定。只有静态绑定成功之后才有后续的运行。
*
* 2.在运行阶段,JVM堆内存当中真实创建的对象是Cat对象,那么程序在运行阶段一点会调用Cat对象的move()方法,此时
* 发生了程序的动态绑定,运行阶段绑定。
*
* 3.注意:
* 无论Cat类有没有重写move方法,运行阶段一定调用的是Cat对象的move方法,因为底层真实的对象就是Cat对象。
*
* 4.父类型引用指向子类型对象这种机制导致程序存在编译阶段绑定和运行阶段绑定两种不同的形态/状态,这种机制可以称为一种
* 多态语法机制。
* */
//a2.catchMouse();
/*
* 分析a2.catchMouse()程序为什么不能调用?
* 因为编译阶段编译器检查到a2的类型是Animal类型,而Animal.class字节码文件当中没有查找到catchMouse()方法。
* 导致静态绑定失败。
*
* 需求:
* 假设想让以上的对象执行catchMouse()方法,怎么办?
* a2是无法直接调用的,因为a2的类型Animal,Animal中没有catchMouse()方法。
* 我们可以将a2强制类型转换为Cat类型。
* a2的类型是Animal(父类),转换成Cat类型(子类),被称为向下转型/downcasting/强制类型转换。
*
* 注:向下转型也需要两种类型之间有继承关系。不然编译会报错。强制类型转换需要加强制类型转换符。
*
* 那么什么时候需要使用向下转换呢?
* 当调用的方法是子类中特有的,在父类中不存在,必须使用强制类型转换。
* */
Cat c2 = (Cat)a2;
c2.catchMouse(); //猫抓老鼠
Animal a3 = new Bird();
/*
* 1、下面程序编译是没有问题的,因为编译器检查a3的数据类型是Animal,Animal和Cat之间存在继承关系,
* 并且Animal是父类型,Cat是子类型,父类型转换成子类型叫做向下类型,语法合格。
*
* 2、程序虽然编译通过了,但是程序在运行阶段会出现异常,因为JVM堆内存当中真实存在的对象是Bird类型,
* Bird对象无法转换成Cat对象,因为两种类型之间不存在任何继承关系,此时出现了著名的异常。
* java.lang.ClassCastException
* 类型转换异常,这种异常总是在“向下转换的时候”会发生
*
* */
//Cat c3 = (Cat) a3; //此句运行出现java.lang.ClassCastException异常
/*
* 1、以上异常只有在强制转换的时候发生,也就是说“向下转型”存在隐患(编译过了,但是运行错了)
* 2、向上转型只要编译通过,运行一点不会出现问题。
* 3、向下转型编译通过,运行可能错误。
* 4、怎么避免向下转型出现的ClassCastException呢?
* 使用instanceof运算符可以避免出现以上的异常。
* 5、instanceof运算符怎么用?
* 5.1、语法格式:
* (引用 instanceof 数据类型名)
* 5.2、以上运行结果类型是布尔类型,结果可能是true/false
* 5.3、关于运算结果true/false:
* 假设:( a instanceof Animal)
* true表示:
* a这个引用指向的对象是一个Animal类型。
* false表示:
* a这个引用指向的对象不是一个Animal类型。
* 6、Java规范中要求:在进行强制类型转换之前,建议采用instanceof运算符,避免异常发生。
* */
if(a3 instanceof Cat){
Cat c3 = (Cat)a3;
c3.catchMouse();
}else if(a3 instanceof Bird){
Bird b2 = (Bird)a3;
b2.fly();
}
}
}
package com.company.test01;
public class Test02 {
public static void main(String[] args) {
//父类型引用指向子类型对象
//向上转型
Animal a1 = new Cat();
Animal a2 = new Bird();
//向下转型【只有当访问子类对象当中特有的方法】
if(a1 instanceof Cat){
Cat c1 = (Cat)a1;
}
if(a2 instanceof Bird){
Bird b1 = (Bird) a2;
}
}
}