多态
package com.company.duotai;
/*
* 关于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();
c1.catchMouse();
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();
/*
* 1、java程序永远都分为编译阶段和运行阶段
*
* 2、先分析编译阶段,再分析运行阶段,编译无法通过,根本是无法运行的
*
* 3、编译阶段编译器检查a2这个引用的数据类型为Animal,由于Animal.class
* 字节码中有move()方法,所以编译通过了。这个过程称为静态绑定,编译阶段绑定。
* 只有静态绑定成功之后才有后续的运行。
*
* 4、在程序运行阶段,JVM堆内存中真实创建的对象是Cat对象,那么以下程序员在运行阶段
* 一定会调用Cat对象的move()方法,此时发生了程序的动态绑定,运行阶段的绑定。
*
* 5、无论Cat类有没有重写move()方法,运行阶段一定调用的是Cat对象的move方法,因为
* 底层真实对象就是Cat对象。
*
* 6、父类型引用指向子类型对象这种机制导致程序存在编译阶段绑定和运行阶段绑定两种不同的形态/状态,
* 这种机制可以成为一种多态语法机制。
* */
a2.move(); // 猫在走猫步
//a2.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;
/*
* 1、以上异常只有在强制类型转换的时候发生,也就是说“向下转换”存在隐患(编译通过了,但是运行错了)
*
* 2、向上转型只要编译通过,运行就一定不会出问题。
*
* 3、怎么避免向下转型出现的ClassCastException呢?
* 使用instanceof运算符可以避免出现以上的异常。
*
* 4、instanceof运算符怎么使用?
* 4.1、语法格式
* (引用 instanceof 数据类型名)
* 4.2、以上运算符的执行结果类型是布尔类型,结果可能是true/false
* 4.3、关于运算结果true/false:
* 假设:(a instanceof Animal)
* true表示:
* a这个引用指向的对象是一个Animal类型
* false表示:
* a这个引用指向的对象不是一个Animal类型
*
* 5、java规范中要求:在进行强制类型转换的时候,建议采用instanceof运算符进行判断,避免ClassCastException异常的发生。
* */
Animal a4 = new Cat();
System.out.println(a4 instanceof Cat); // true
System.out.println(a4 instanceof Animal); // true
if(a4 instanceof Cat){
Cat c4 = (Cat)a4;
}
}
}
package com.company.duotai;
public class Cat extends Animal {
public void move(){
System.out.println("猫在走猫步");
}
public void catchMouse(){
System.out.println("猫爪老鼠");
}
}
package com.company.duotai;
public class Bird extends Animal {
public void move(){
System.out.println("鸟儿在飞翔!");
}
}
package com.company.duotai;
public class Cat extends Animal {
public void move(){
System.out.println("猫在走猫步");
}
public void catchMouse(){
System.out.println("猫爪老鼠");
}
}
多态在实际应用中的作用
package com.company.duotai01;
/*
* 多态在实际开发中的作用,以下以主人喂养宠物为例说明多态的作用:
* 1、分析:主人喂养宠物这个场景要实现需要进行类型的抽象。
* - 主人【类】
* - 主人可以喂养宠物,所以主人有喂养的这个动作
* - 宠物【类】
* - 宠物可以吃东西,所以宠物有吃东西这个动作
*
* 2、面向对象编程的核心:定义好类,然后将类实例化为对象,给一个环境驱使一下,让各个对象之间协作起来形成一个系统。
*
* 3、多态的作用是什么?
* 降低程序耦合度,提高程序扩展力
* 能使用多态尽量使用多态。
* 父类型引用指向子类型对象
*
* 核心:面向抽象编程,不要面向具体编程。
*
* */
public class Test {
public static void main(String[] args) {
// 创建主人对象
Master zhangsan = new Master();
// 创建猫对象
Cat tom = new Cat();
// 创建狗对象
Dog erHa = new Dog();
// 主人喂养猫
// feeg传进去的实际上是父类型引用指向子类型的对象 Pet p = new Cat();
// 编译阶段调用的是Master的feed方法里面的 p.eat()方法
// 运行阶段调用的是实际对象Cat里面的eat()方法
zhangsan.feed(tom);
//主人喂养狗
zhangsan.feed(erHa);
}
}
package com.company.duotai01;
/*
* 主人
* */
// 这种方式没有使用java语言多态机制,存在缺点:Master的扩展力很差,因为只要加一个新的宠物,Master类就需要添加新的方法。
/*
public class Master {
// 喂养宠物的方法
public void feed(Cat c){
c.eat();
}
public void feed(Dog d){
d.eat();
}
}
*/
// Master和Cat、Dog这两个类型的关联度很强,耦合度很高,扩展力差。
// 降低程序的耦合度【解耦合】,提高程序的扩展力【软件开发的一个很重要的目标】
// Master主人类面向的是一个抽象的Pet,不再面向具体的宠物
// 提倡:面向抽象编程,不要面向具体编程
// 面向抽象编程的好处是:耦合度低,扩展力强。
public class Master{
// 喂养宠物的方法
public void feed(Pet p){
p.eat();
}
}
package com.company.duotai01;
public class Pet {
public void eat(){
}
}
package com.company.duotai01;
/*
* 宠物小猫
* */
public class Cat extends Pet{
public void eat(){
System.out.println("小猫爱吃鱼");
}
}
package com.company.duotai01;
/*
* 小狗
* */
public class Dog extends Pet{
public void eat(){
System.out.println("小狗爱吃骨头");
}
}