zoukankan      html  css  js  c++  java
  • java基础---24. 多态性

    1 概述

    • 面向对象三大特征:封装性、继承性、多态性。其中,继承性是多态性的前提。

    • 继承性在java中的体现除了extends还有implements。所以extends继承和implements实现都是继承性的前提。

    • 不一定是类与类之间的继承,还有可能是接口与接口之间的继承,还有可能是类与接口之间的实现

    • 注意:多态性指的是对象而不是类

    2 多态的格式与使用

    代码当中体现多态性,其实就是一句话:父类引用指向子类对象。

    • 格式
    //方式1:
    父类名称 对象名 = new 子类名称();//子类对象就被当作父类进行使用,一只猫被当中动物来看待
    //或者
    //方式2
    接口名称 对象名 = new 实现类名称();
    
    对于方式1举例
    • 定义一个父类
    public class Fu {
        public void method(){
            System.out.println("父类方法");
        }
        public void methodFu() {
            System.out.println("父类特有方法");
        }
    }
    
    • 定义一个子类
    public class Zi extends Fu{
        @Override
        public void method() {
            System.out.println("子类方法");
        }
    }
    
    • 调用
    public class Demo01Multi {
        public static void main(String[] args) {
            //使用多态写法
            //左侧父类的引用指向右侧子类的对象
            Fu obj = new Zi();
            obj.method();//子类方法       对于成员方法,new的是谁就调用谁的方法 若没有就往上找
            obj.methodFu();//父类特有方法
        }
    }
    

    3 多态中成员变量的使用(规则不变)

    规则:访问成员变量两种方式

    • 1.直接通过对象名称访问成员变量(对象名称.变量名):看等号左边是谁,优先用谁,没有则向上找。
    • 2.间接通过成员方法访问成员变量:该方法属于谁则优先用谁,没有则向上找。
      注意:成员变量不可以覆盖重写,只有方法可以覆盖重写
    举例
    • 定义一个父类
    public class Fu {
        int num = 10;
        public void showNum(){
            System.out.println(num);
        }
    }
    
    • 定义一个子类
    public class Zi extends Fu {
        int num = 20;
        int age = 18;
    }
    
    • 使用
    public class Demo01MutilField {
        public static void main(String[] args) {
            //使用多态写法,父类引用指向子类对象
            Fu obj = new Zi();//等号左边是Fu
    
            //1.
            System.out.println(obj.num);//10
            //System.out.println(obj.age);//错误写法,Fu中没有age,再向上找是Oject类也不会有age
    
            //2.
            obj.showNum();//10   子类没有对该方法覆盖重写则就向上找父输出10;若子类对该方法覆盖重写那就是子输出20
    
        }
    }
    

    4 多态中成员方法的使用(规则不变)

    规则:看new的是谁,就优先用谁,没有则向上找
    口诀:编译看左边,运行看右边
    对于成员变量的口诀:编译看左边,运行还看左边

    举例
    • 定义一个父类
    public class Fu {
        public void method(){
            System.out.println("父类方法");
        }
        public void methodFu(){
            System.out.println("父类特有方法");
        }
    }
    
    • 定义一个子类
    public class Zi extends Fu {
        @Override
        public void method(){
            System.out.println("子类方法");
        }
    
        public void methodZi(){
            System.out.println("子类特有方法");
        }
    }
    
    • 使用
    public class Demo02MutilMethod {
        public static void main(String[] args) {
            Fu obj = new Zi();//多态
            obj.method();//子类方法
            obj.methodFu();//父类特有方法    子类没有改方法,所以向上找到了父类的该方法
    
            //obj.methodZi();//错误写法
            //编译看左边,左边是Fu,而Fu当中没有methodZi方法,所以出现红线,编译报错
        }
    }
    

    5 多态的好处

    调用方法的时候,编译看左边,运行看右边

    6 对象的向上转型

    举例

    本例用的是类当然也可以用接口

    • 定义一个Animal类
    public abstract class Animal {
        public abstract void eat();
    }
    
    • 定义一个Cat类
    public class Cat extends Animal {
        @Override
        public void eat() {
            System.out.println("猫吃鱼");
        }
    }
    
    • 使用
    public class Demo01Main {
        public static void main(String[] args) {
            //对象的向上转型就是:父类引用指向子类对象
            Animal animal = new Cat();
            animal.eat();//猫吃鱼
            //编译看左:左边Animal有eat方法
            //运行看右:运行右边Cat的eat方法
        }
    }
    
    举例(接口)
    • 定义一个接口
    public interface Animal {
        public abstract void eat();
    }
    
    • 定义一个实现类
    public class Cat implements Animal{
        @Override
        public void eat() {
            System.out.println("猫爱吃鱼鱼");
        }
    }
    
    • 使用
    public class Demo01Main {
        public static void main(String[] args) {
            Animal animal = new Cat();
            animal.eat();//猫爱吃鱼鱼
        }
    }
    

    7 对象的向下转型

    问题引入

    向上转型是安全的,没有问题。但是有弊端:对象一旦向上转型为父类,那么就无法调用子类原本特有的内容。

    • 父类
    public abstract class Animal {
        public abstract void eat();
    }
    
    • 子类
    public class Cat extends Animal {
        @Override
        public void eat() {
            System.out.println("猫吃鱼");
        }
    
        //子类特有方法
        public void catchMouse(){
            System.out.println("猫抓老鼠");
        }
    }
    
    • 使用
    public class Demo01Main {
        public static void main(String[] args) {
            //对象的向上转型就是:父类引用指向子类对象
            Animal animal = new Cat();
            animal.eat();//猫吃鱼
            //编译看左:左边Animal有eat方法
            //运行看右:运行右边Cat的eat方法
    
            //animal.catchMouse();//错误写法  编译报错  Animal中没有这个方法  并不是所有的动物都会猫抓老鼠
        }
    }
    
    解决方案:向下转型【还原】

    向上转型一定安全,向下转型有前提条件

    • 父类
    public abstract class Animal {
        public abstract void eat();
    }
    
    • 子类Cat
    public class Cat extends Animal {
        @Override
        public void eat() {
            System.out.println("猫吃鱼");
        }
    
        //子类特有方法
        public void catchMouse(){
            System.out.println("猫抓老鼠");
        }
    }
    
    • 子类Dog
    public class Dog extends Animal{
        @Override
        public void eat(){
            System.out.println("狗吃骨头");
        }
    
        public void watchHouse(){
            System.out.println("狗狗看家");
        }
    }
    
    • 使用
    public class Demo01Main {
        public static void main(String[] args) {
            //对象的向上转型就是:父类引用指向子类对象
            Animal animal = new Cat();//本来创建的时候是一只猫
            animal.eat();//猫吃鱼
    
    
            //animal.catchMouse();//错误写法  编译报错  Animal中没有这个方法  并不是所有的动物都会猫抓老鼠
    
            //向下转型进行还原动作
            Cat cat = (Cat)animal;
            cat.catchMouse();//猫抓老鼠
    
            //错误的向下转型
            //本来new的时候是一只猫,现在非要当作狗狗
            Dog dog = (Dog)animal;//编译不报错,但是运行异常:java.lang.ClassCastException类转换异常
        }
    }
    
    新的问题:如果知道父类的引入本来是Cat还是Dog呢?
    • 格式
    对象 instanceof 类名称
    

    这将会得到一个boolean值结果,也就是判断前面的对象能不能当作后面类型的实例。

    public class Demo02Instanceof {
        public static void main(String[] args) {
            Animal animal = new Cat();//本来就是一只猫
            animal.eat();//猫吃鱼
            giveMePet(new Cat());
        }
        
        public static void giveMePet(Animal animal){
            //如果希望用子类特有方法,需要向下转型
            //判断传入的参数本来是猫还是狗,再决实施行何种向下转型的方案
            if(animal instanceof Dog) {//判断一下父类引用animal本来是不是狗
                Dog dog = (Dog)animal;
                dog.watchHouse();
            }
            if(animal instanceof Cat){//判断一下父类引用animal本来是不是猫
                Cat cat = (Cat)animal;//猫抓老鼠
                cat.catchMouse();
            }
        }
    }
    
  • 相关阅读:
    leetcode教程系列——Binary Tree
    《Ranked List Loss for Deep Metric Learning》CVPR 2019
    《Domain Agnostic Learning with Disentangled Representations》ICML 2019
    Pytorch从0开始实现YOLO V3指南 part5——设计输入和输出的流程
    Pytorch从0开始实现YOLO V3指南 part4——置信度阈值和非极大值抑制
    Pytorch从0开始实现YOLO V3指南 part3——实现网络前向传播
    Pytorch从0开始实现YOLO V3指南 part2——搭建网络结构层
    Pytorch从0开始实现YOLO V3指南 part1——理解YOLO的工作
    让我佩服的人生 文章
    win8.1配置cordova+ionic等一系列东西
  • 原文地址:https://www.cnblogs.com/deer-cen/p/12247799.html
Copyright © 2011-2022 走看看