关于java语言中的多态机制(多种形态:编译期形态,运行期形态)
1、Animal、Bird、Cat三个类之间的关系
Cat继承Animal
Bird继承Animal
Cat和Bird之间没有任何继承关系
2、面向对象三大特征:封装、继承、多态
3、关于多态中涉及到的几个概念
向上转型(upcasting)
子类型——>父类型
又被称为:自动类型转换
向下转型(downcasting)
父类型——>子类型
又被称为:强制类型转换【需要加强制类型转换符】
无论是向上转型还是向下转型,两种类型之间必须要有继承关系。
没有继承关系,编译无法通过
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
//常规写法:
Animal a = new Animal();
a.move();
Cat c = new Cat();
c.move();
c.catchMouse();
Bird b = new Bird();
b.move();
//使用多态:
/**
* 1、Animal和Cat之间存在继承关系,Animal是父类,Cat是子类
*
* 2、Cat is a Animal【合理的】
*
* 3、new Cat()创建的对象的类型是Cat,aa这个引用的数据类型是Animal,可见他们进行了类型转换
* 子类型转换成父类型,称为向上转型/upcasting,或者称为自动类型转换。
*
* 4、Java中允许这种语法:父类型引用指向了一个子类型对象。
*/
Animal aa = new Cat();//父类型引用指向子类型对象,向上类型转换(子类型Cat转换为父类型Animal)
/**
* 1、编译阶段:编译器检查aa这个引用的数据类型为Animal,由于Animal.class字节码
* 当中有move()方法,所以编译通过。这个过程我们称为静态绑定,编译阶段绑定。
* 只有静态绑定成功之后才有后续的运行
*
*2、 在程序运行阶段,JVM堆内存当中真实创建的对象是Cat对象,程序在运行阶段一定会
* 调用Cat对象的move()方法,此时发生了程序的动态绑定,运行阶段绑定。
*
* 3、等号右边,在堆内存中创建了Cat的对象,将cat对象的地址赋给一个Animal类型的变量aa
*
* 运行的时候运行的是cat类型的对象,所以aa.move()运行的是Cat中的move()方法
*
* 4、无论是Cat类有没有重写move方法,运行阶段一定调用的是cat对象的move方法,
* 因为底层真实对象就是Cat对象
* 5、父类型引用指向子类型对象这种机制导致程序存在编译阶段绑定和运行阶段绑定两种不同的形态/状态,
* 这种机制可以称为一种多态语法机制。
*/
aa.move();
/**
* 分析一下程序为什么不能调用?
* 因为编译阶段编译器检查到aa的类型是Animal类型
* 从Animal.class字节码文件当中查找catchMouse()方法,
* 最终没有找到该方法,导致静态绑定失败,没有绑定成功,也就是说编译失败了。更无法运行。
*/
//aa.catchMouse();//父类型引用指向子类型对象的时候,引用无法调用父类型中没有的方法
/**
* 需求:想让以上的对象执行catchMouse()方法,怎么办?
* aa是无法直接调用的,因为aa引用的类型是Animal,Animal中没有catchMouse()方法。
* 我们可以将aa强制类型转换为cat类型的引用
* aa的类型 Animal(父类),转换成cat类型(子类),被称为向下转型/downcasting/强制类型转换。
*
* 注意:向下转型也需要两种类型之间必须有继承关系,不然编译报错。
* 强制类型转换需要加强制类型转换符。
*
* 什么时候需要向下转型?
* 当调用的方法是子类型中特有的,在父类型中不存在。
* 必须进行向下转型。
*/
Cat c2 = (Cat)aa;//向下类型转换,转换回去了,子类型(Cat)转换为父类(Animal),父类(Animal)又转换为子类(Cat)
c2.catchMouse();
//父类型引用指向子类型对象【多态】
Animal a3 = new Bird();//子类型Bird转换为父类型Animal,(向上类型转换)
/**
* 1、以下程序编译是没有问题的,因为编译器检查到a3的数据类型是Animal
* Animal和Cat之间存在继承关系,并且Animal是父类型,Cat是子类型,
* 父类型转换成子类型叫做向下转型,语法合格
* 2、程序虽然编译通过了,但是程序在运行阶段会出现异常,因为JVM堆内存
* 当中真实存在的对象是Bird类型,Bird对象无法转换成Cat对象,因为两种
* 类型之间不存在任何继承关系,此时出现了著名的异常:
* java.lang.ClassCastException:
* 类型转换异常,这种异常总是在“向下转型”(强制类型转换)的时候发生。
*
*/
//Cat c3 = (Cat)a3;//父类型引用(Animal)赋值给子类型引用(Cat),父类型引用(Animal)指向另一个子类型对象(Bird),将父类型(Animal)强制转换为子类型(Cat)
//但两个子类(对象Bird、引用Cat之间并无继承关系)
//一个父类(Animal)继承出两个毫无关系的子类(Bird,Cat),一子类(Bird)向上类型转换为父类(Animal),之后父类转换为零一子类(Cat)
//会出错
/**
* 1、以上异常只有在强制类型转换的时候会发生,也就是说“向下转型”存在隐患(编译过了,但是运行错了)
* 2、向上转型只要编译通过,运行一定不会出问题。
* 3、向下转型编译通过,运行可能错误:
* 4、怎么避免?
* 使用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运算符进行判断,避免ClassCastException异常的发生。
*/
//当a3指向的对象确实是一个cat的时候,才进行强制类型转换
if(a3 instanceof Cat){//a3是一个Cat类型的对象
Cat c3 = (Cat)a3;
//调用子类对象中特有的方法才需要用到强制类型转换
c3.catchMouse();
}else if(a3 instanceof Bird){//a3是一个Bird类型的对象
Bird b2 = (Bird)a3;
b2.fly();//调用子类中特有的方法,才用到强制类型转换
}
}
}
多态在实际开发中的作用,
1、分析:需要进行类型的抽象
——主任【类】
——主人可以喂养宠物,所以主人有【喂养的动作】
——宠物【类】
——宠物可以吃东西,所以宠物有吃【东西的动作】
2、面向对象核心:定义好类,然后将类实例化对象,给一个环境驱使以下,让各个对象之间协作起来形成一个系统
3、多态的作用?
降低程序的耦合度,提升程序的扩展力。
能使用多态尽量使用多态。
父类型引用指向子类型对象(向下类型转换)
核心:面向对象编程,尽量不要面向具体编程。
主人类:public void feed(Pet pet){//当测试类调用时,传入的是子类对象时,就是多态,父类型引用指向子类型对象
pet.eat();
}
//主人类
//这种方式没有使用java语言当中的多态机制,存在缺点:Master的扩展力很差,因为只要加一个新的宠物,Master类就需要添加新的方法。
/*public class Master {
//喂猫的方法(动作)
public void feed(Cat c){//面向具体编程
c.eat();
}
public void feed(Dog d){//面向具体编程
d.eat();
}
}
//Master和Cat、Dog这两个类型的关联程度很强,耦合度很高,扩展力差
*/
//降低程序的耦合度【解耦合】,提高程序的扩展力【软件开发的一个很重要的目标,高扩展力:软件在升级的时候不改java代码】
public class Master {
//Master不应该面向Cat、Dog
//应该面向Pet
//面向Pet之后传入对象的时候可以传入Cat、Dog
//相当于:
/**
*public void feed(Pet pet = new Cat){//父类型引用指向子类型对象【向上类型转换】
*pet.eat();
*}
*
*/
//主人面向的是一个抽象的pet,不再面向具体的宠物
//提倡面向抽象编程,不要面向具体编程
//面向抽象编程的好处是,耦合度低,扩展力强
public void feed(Pet pet){//当测试类调用时,传入的是子类对象时,就是多态,父类型引用指向子类型对象
pet.eat();
}
}
宠物类
//宠物
public class Pet {
public void eat(){
}
}
测试类:
public class Test2 {
public static void main(String[] args) {
// TODO Auto-generated method stub
//创建主人对象
Master m = new Master();
//创建猫对象
Cat c = new Cat();
//主人喂养猫
m.feed(c);
Dog husky = new Dog();
m.feed(husky);
}
}