多态

package duotai.javase.test001;

public class Animal {

    public void move(){
        System.out.println("动物在移动");
    }
}
package duotai.javase.test001;

//鸟儿类
public class Bird extends Animal {

    //重写父类中继承过来的方法
    public void move(){
        System.out.println("鸟儿在飞翔");
    }
    
    //子类对象特有的行为
    public void fly(){
        System.out.println("Bird fly!");
    }
}
package duotai.javase.test001;

//猫类
public class Cat extends Animal {
    
    //重写父类中继承过来的方法
    public void move(){
        System.out.println("猫在走猫步");
    }
    
    //不是从父类中继承过来的方法
    //这个方法是子类对象特有的行为
    public void catchMouse(){
        System.out.println("猫捉老鼠");
    }
}
package duotai.javase.test001;

/**
 * 关于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,
         * 可见它们进行了类型转换,子类型转换成父类型,
         * 称为向上转型,或者称为自动类型转换
         * 
         * 4、java中允许这种语法:父类型引用指向子类型对象
         */
        Animal a2 = new Cat();
        //Bird b2 = 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的类型是Anima1类型,
         *         从Animal.class字节码文件当中查找catchMouse()方法,
         *         最终没找到该方法,导致静态绑定失败,没有绑定成功,
         *         也就是说编译失败了。别谈运行了。
         */
        //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、向上转型只要编译通过,
         *    运行一定不会出问题:Animal a = new Cat()
         * 
         * 3、向下转型编译通过,运行可能错误:
         *    Anima1 a3 = new Bird(); Cat c3 = (cat)a3;
         * 
         * 4、怎么避免向下转型出现的ClassCastException呢?
         *         使用 instanceof运算符可以避免出现以上的异常。
         * 
         * 5、 instanceof运算符怎么用?
         * 
         *         5.1、语法格式:
         *             (引用 instanceof 数据类型名)
         *         
         *         5.2、以上运算符的执行结果类型是布尔类型,
         *            结果可能是true/false
         * 
         *         5.3、关于运算结果true/false:
         *             假设:(a instanceof Animal)
         *             true表示:
         *                 a这个引用指向的对象是一个Animal类型。
         *             fa1se表示:
         *                 a这个引用指向的对象不是一个Animal类型。
         * 
         * 6、Java规范中要求:在进行强制类型转换之前,
         *     建议采用instanceof运算符进行判断,
         *    避免ClassCastException异常的发生。这是一种编程好习惯
         */
        if(a3 instanceof Cat){
            Cat c3 = (Cat)a3;
            c3.catchMouse();
        }else if(a3 instanceof Bird){
            Bird b2 = (Bird)a3;
            b2.fly();
        }
    }
}
package duotai.javase.test001;

public class Test2 {

    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;
        }
    }
}

 

 

package duotai.javase.test002;

/**
 * 宠物
 */
public class Pet {

    /**
     * 所有的宠物都可以吃东西
     */
    public void eat(){
        
    }
}
package duotai.javase.test002;

/**
 * 宠物小猫
 */
public class Cat extends Pet{

    //小猫爱吃鱼
    public void eat(){
        System.out.println("小猫正在吃鱼!");
    }
}
package duotai.javase.test002;

/**
 * 宠物小狗
 */
public class Dog extends Pet{

    //宠物小狗爱吃骨头
    public void eat(){
        System.out.println("小狗爱啃骨头!");
    }
}
package duotai.javase.test002;

/**
 * 主人
 */

//这种方式没有使用java语言当中的多态机制,
//存在的缺点:Master的扩展力很差,
//因为只要加一个新的宠物,Master类就需要添加新的方法。

/*
public class Master {

    //喂养宠物的方法
    public void feed(Cat c){
        c.eat();
    }
    
    public void feed(Dog d){
        d.eat();
    }
}
//Master和Cat、Dog这两个类型的关联程度很强,耦合度很高,扩展力差。
*/

//降低程序的耦合度【解耦合】,
//提高程序的扩展力【软件开发的一个很重要的目标】
public class Master {

    //Master主人类面问的是一个抽象的Pet,不再面向具体的宠物
    //提倡:面向抽象编程,不要面向具体编程。
    //面向抽象编程的好处是,耦合度低,扩展力强。
    public void feed(Pet pet){ //Pet pet 是一个父类型的引用
        pet.eat();
    }
}
package duotai.javase.test002;

/**
 * 多态在实际开发中的作用,以下以主人喂养宠物为例说明多态的作用:
 * 
 *     1、分析:主人喂养宠物这个场景要实现需要进行类型的抽象:
 *         - 主人【类】
 *         - 主人可以喂养宠物,所有主人有喂养的这个动作
 *         - 宠物【类】
 *         - 宠物可以吃东西,所有宠物有吃东西的这个动作
 * 
 *     2、面向对象的核心:定义好类,然后将类实例化为对象,
 *     给一个环境驱使一下,让各个对象之间协作起来形成一个系统。
 *
 *    3、多态的作用是什么?
 *        降低程序的耦合度,提高程序的扩展力。
 *        能使用多态尽量使用多态。
 *        父类型引用指向子类型对象。
 *
 *    核心:面向抽象编程,尽量不要面向具体编程。
 */
public class Test {

    public static void main(String[] args){
        //创建主人对象
        Master zhangsan = new Master();
        //创建猫对象
        Cat tom = new Cat();
        //主人喂猫
        zhangsan.feed(tom); //小猫正在吃鱼!
        //创建小狗对象
        Dog erHa = new Dog();
        //主人喂养小狗
        zhangsan.feed(erHa); //小狗爱啃骨头!
    }
}

 

posted @ 2020-08-12 23:24  Lerresino  阅读(89)  评论(0)    收藏  举报