/**
* 关于java语言中的多态:
* 先掌握两个概念:
* - 向上转型(upcasting): 子类型转换成父类型
* - 向下转型(downcasting): 父类型转换成子类型
*
* (五颗星*****)注意:Java中允许向上转型,也允许向下转型,他的前提条件是两个类之间必须有继承关系,
* 没有继承关系,就没有向上转型和向下转型
* - 多态是面向对象的三大特征之一(三大特征:封装、继承、多态)
* 有了封装才有继承,有了继承才有方法重写和多态
* - 多态体现为:父类型引用指向子类型对象
* 定义格式为:
* 父类类型 引用名 = new 子类类型();
*
* 其特点是:编译看左边,运行看右边
* 编译阶段的绑定称为静态绑定,运行阶段的绑定称为动态绑定(具体解释:看下面程序中的分析 分为编译阶段和运行阶段)
* - 向下转型(downcasting)
* 1、格式:子类类型 引用名 = (子类类型) 父类类型的引用名;
*
* 2、适用时机:当需要调用子类特有的方法时
*
* 3、可能出现的问题:
* 向下转型编译通过,运行可能出错,出现"类型转换异常"英文提示为:java.lang.ClassCastException。
*
* 4、如何避免"类型转换异常"的出现
* 需要使用关键字:"instanceof",
* - instanceof可以在运行阶段动态判断 一个引用所指向的 堆内存中的Java对象 是不是指定的类型
* - 语法格式:父类型引用 instanceof 子类类型
* - 语句执行结果:是布尔类型(true/false)
* true代表:父类型引用指向的对象是子类型对象;
* false代表:父类型引用指向的对象不是子类型对象。
* 注意:程序员都需要养成一个好习惯,那就是:任何时间、任何地点,在对类型进行向下转型时,一定要使用instanceof
* 运算符进行判断。满足条件才能向下转型。这样可以避免出现ClassCastException异常的出现。
*
* - 父类型引用指向子类型对象这种机制导致程序在编译阶段绑定和运行阶段绑定两种不同的形态/状态,这是一种多态语法机制
*
* - 多态在开发中的作用:
* 降低程序的耦合度,提高程序的扩展力
*
* 在今后的开发中能使用多态尽量使用多态,尽量面向抽象编程,不要面向具体编程。
* - 面向对象编程的核心是:先对具体对象进行抽象,定义好类,然后将类实例化成对象,再让各个对象之间协作起来形成一个系统
*/
public class Text多态 {
public static void main(String[] args) {
//创建一个Cat对象,采用父类型引用指向子类型对象
Animal animal1 = new Cat();
//用父类型引用"animal1"调用move()方法
/*
我们来分析animal1.move();这句代码,因为Java程序运行分为编译阶段和运行阶段;
编译阶段:
对于编译器来说,它只知道animal1的类型是Animal,所以编译器在检查语法f的时候,
就会去Animal.class字节码文件中找move()方法,找到后就会绑定上,这种绑定称为静态绑定。(这是编译阶段的绑定)
运行阶段:
在运行阶段,实际上在堆内存中创建的Java对象是Cat对象,在指向move()方法的时候,真正参与者是Cat对象,
所以运行阶段会动态执行Cat对象的move()方法,这个过程属于运行阶段绑定(也叫动态绑定)
多态:就是多种形态/状态
编译的时候一种状态,运行的时候是另一种状态。
多态产生原因就是:父类型引用指向子类型对象,然后用父类型引用去调用被重写的方法,
它分为静态绑定(绑定父类的方法)和动态绑定(绑定子类型对象的方法)两种状态
*/
animal1.move();//运行结果是:猫在走猫步
//下面我们用父类型引用animal1去调用子类特有的方法catchMouse()方法
/*
分析:Java程序运行分为:编译阶段和运行阶段 只有通过了编译阶段的代码才能运行,没有编译就没有运行
编译阶段:
编译阶段会报错:找不到符号
原因:编译器只知道animal1的类型是Animal,就会去Animal.class字节码文件中找catchMouse(),结果没有找到,
所以静态绑定失败,编译器就会报错,无法运行。
*/
//animal1.catchMouse();
//上行代码编译会出错,如果必须要调用子类中特有的方法,这里就需要用到"向下转型",其代码如下
Cat c1 = (Cat)animal1;
//然后用向下转型后的引用再调用子类中特有的方法
c1.catchMouse();//运行结果:猫在抓老鼠
//在向下转型的过程中也会出现错误,比如:new了一个Pig对象,指向了父类型引用
Animal animal2 = new Pig();
//如果将animal2向下转型成Cat对象就会出现"java.lang.ClassCastException"该异常是运行期异常,
//Cat c2 = (Cat) animal2; //这行代码在编译期不会报错
//为了避免出现"java.lang.ClassCastException"异常,需要使用"instanceof"关键字
if (animal2 instanceof Cat){ //该语句的意思是:判断引用"animal2"是否是"Cat"类型,如果是返回true,如果不是返回false
Cat cat = (Cat) animal2;
}
//由于instanceof运算符的应用,上行代码在运行阶段就不会出现"java.lang.ClassCastException"异常
//没有出现异常的原因是animal2 instanceof Cat的运行结果是false
System.out.println(animal2 instanceof Cat);//输出结果:false
}
}
class Animal {
//移动的方法
public void move(){
System.out.println("动物在移动");
}
}
class Cat extends Animal{
//重写父类中的move()方法
public void move(){
System.out.println("猫在走猫步");
}
//子类中特有的方法(因为不是所有的动物都会抓老鼠)
public void catchMouse(){
System.out.println("猫在抓老鼠");
}
}
class Pig extends Animal{
//重写父类中的move()方法
public void move(){
System.out.println("猪在溜达");
}
}