zoukankan      html  css  js  c++  java
  • Java基础00-多态19

    1. 多态

    多态

    1.1 多态概述

    代码示例:

    动物类:

    public class Animal {
        public void eat(){
            System.out.println("动物吃东西");
        }
    }

    猫类:

    多态的前提有继承/实现关系,所以猫类要继承动物类。

    public class Cats extends Animal {
    
    }

    多态的前提要有方法重写,所以要重写父类的eat()方法。

    public class Cats extends Animal {
    
        @Override
        public void eat(){
            System.out.println("猫吃鱼");
        }
    }

    实现类:

    要有父类引用指向子类对象

    public class AnimalDemo {
        public static void main(String[] args) {
            //有父类引用指向子类对象
            Animal a = new Cats();  
        }
    }

    这样就满足了多态的前提,这样的现象就称之为多态。

    1.2 多态中成员和成员变量访问特点

    • 访问成员变量的特点:

        直接通过对象名称访问成员变量:看等号左边是谁,优先用谁,没有则向上找。
        间接通过成员方法访问成员变量:看该方法属于谁,优先用谁,没有则向上找。

    • 访问成员方法的特点:

        看new的是谁,就优先用谁,没有则向上找。

    代码示例:

    动物类:

    public class Animal {
        int age = 40;
        public void eat(){
            System.out.println("动物吃东西");
        }
    }

    猫类:
    继承了动物类,并重写的父类的eat()方法

    public class Cats extends Animal {
    
        public int age = 20;
        public int weight = 10;
        
        @Override
        public void eat(){
            System.out.println("猫吃鱼");
        }
    
        public void playGame(){
            System.out.println("猫捉迷藏");
        }
    }

    测试类:
    a.age调用可以使用,但是a.weight调用不可以使用,
    因为,虽热内存中指向的是子类new Cat(),但是外界看到的是Animal a,通过多态的方式在访问成员变量的时候它的编译要看左边(父类),要看左边(父类)中有没有,有就可以调用,没有就不可以调用。

    接下来运行测试类:
    通过多态的形式,去访问成员变量,其实是访问左边(父类)中的,所以,通过多态的形式,访问成员变量,它的编译看右边,运行也看右边。

    接下来调用成员方法:
    调用a.eat()方法成功,a.playGame()失败,
    可见调用成员方法和变量是一样的,编译也要看左边(父类),右边有可以通过,没有就不可以。

    运行测试类:
    运行的是重写后的方法,由此可见
    通过多态的方式,调用成员变方法看的是右边(子类),运行看右边

    1.3 多态的好处和弊端

    代码示例:

    类目录:

    动物类:

    猫类:

    猫类继承了动物类并重写了它的eat()方法。

    动物操作类:

    它的成员方法的形成类型是Cat。

    测试类:

    运行测试类并调用操作类的方法:

    运行:

    ao.useAnimal©,解析:
    调用ao(操作类)中的useAnimal成员方法,
    useAnimal方法的形参类型是Cat(猫类),
    所以将main方法中的Cat c(指向的就是new Cat())(猫类)放入形参中,此时的形参相当于:Cat c = new Cat();

    c.eat()方法调用猫类的eat方法,
    输出“猫吃鱼”。

    再加一个狗类型:
    和上面的一样,先创建一个狗类,再在操作类中添加一个新的useAnimal方法这次的形参类型是狗类,调用的是狗类的eat方法。

    运行:

    这样的方法明显比较麻烦,如果要创建多个动物就要重复上面的每一个步骤,
    所以我们运用多态。
    修改操作类:
    将useAnimal方法中的形参变为Animal(动物类),
    因为不管猫还是狗,它们都继承Animal(动物类),
    所以我们完全可以将Animal(动物类)作为形参。

    接着运行测试类:

    运行结果:

    运行结果和以前一样,解析:
    以Cat(猫)为例,
    ao.useAnimal©,
    调用ao(AnimalOperator)的useAnimal方法,
    useAnimal方法的形参类型是Animal(动物类),Cat(猫)类的父类也是Animal(动物类),
    所以完全可以将Cat c(指向new Cat())赋值给Animal(动物类),这样就变成了,Animal a = new Cat();

    这样就形成了多态,多态调用成员方法时,编译看左边,运行看右边,
    所以我们就是输出“猫吃鱼”
    这样利用多态就可以省去多个步骤,创建一个useAnimal方法即可。
    但是这样不可以调用子类的特有方法,因为编译看左边(父类)。

    1.4 多态中的转型

    代码示例:

    动物类:

    猫类:

    测试类:

    向上转型:
    父类引用指向子类对象,子类对象赋值给父类引用

    运行:

    但是通过向上转型的方法,访问不了子类特有的方法。

    所以要用到向下转型
    父类引用a转为子类对象Cat,赋值给Cat

    运行:

    1.5 多态转型内存图解

    从main方法开始执行,加载到栈内存

    Animal a加载到栈内存

    在堆内存中new一片空间(001),Cat继承了Animal,所以将地址001赋值给Animal a。

    这也叫向上转型

    多态中执行方法,编译看左边,执行看右边,将Cat类加载到栈内存中,调用eat方法,输出“猫吃鱼”。

    调用完毕后从栈内存消失

    执行Cat c = (Cat) a;,将Cat c加载到栈内存中

    a指向的是堆内存中的001,而001就是Cat,所以001完全可以赋值给Cat
    这就是向下转型

    c指向地址001(Cat),所以可以调用Cat中的方法。

    执行a = new Dog();,先在堆内存中new一片空间002,Dog也继承了Animal,所以也可以赋值给a,这时a的地址值变了002。

    执行a.eat():,调用方法执行看右边,进入Dog类执行eat()方法,输出“狗吃骨头”。
    方法执行结束从栈内存消失。

    Cat cc = (Cat) a;,将Cat cc加载到栈内存,(Cat) a,a的地址值是002,002对应的是Dog方法。

    虽然猫和狗都继承自Animal但是两者是没有任何关系的,所以他们相互之间是不能进行转换的。

    所以这个地方是错误的,如果强制执行就会报错,类型转换异常。

    1.6 使用多态的好处

     1.7 案例

    public class Animal {
        private String name;
        private int age;
    
        public void setName(String name){
            this.name=name;
        }
    
        public String getName(){
            return name;
        }
    
        public void setAge(int age){
            this.age = age;
        }
    
        public int getAge(){
            return age;
        }
    
        public Animal(){
            System.out.println("无参构造方法");
        }
    
        public Animal(String name, int age){
            System.out.println("有参构造方法");
        }
    
        public abstract void eat();
    }
    public class Cat extends Animal {
        public Cat(){
            super();
            System.out.println("猫子类无参构造方法");
        }
    
        public Cat(String name, int age){
            super(name, age);
            System.out.println("猫子类有参构造方法");
        }
    
        public void eat(){
            System.out.println("猫吃鱼");
        }
    }
    public class Dog extends Animal {
        public Dog(){
            System.out.println("狗子类无参构造方法");
        }
    
        public Dog(String name, int age){
            System.out.println("狗子类有参构造方法");
        }
    
        public void eat(){
            System.out.println("狗吃骨头");
        }
    }
    public class AnimalDemo {
        public static void main(String[] args) {
            Animal a = new Cat();
            a.eat();//猫吃鱼
    
            Animal b = new Dog();
            b.eat();//狗吃骨头
        }
    }
  • 相关阅读:
    Linux启动网卡时出现RTNETLINK answers: File exists错误解决方法
    在Linux系统中应用su和sudo
    Python模块常用的几种安装方式
    安装php并使用nginx连接PHP
    查看Chrome浏览器扩展程序源码的两种方法
    angularJs中缓存数据,免去重复发起请求的几种写法
    underscore.js 分析6 map函数
    AngularJS中Directive指令系列
    $watch和$observe的使用
    $watch, $watchCollection, $watchGroup的使用
  • 原文地址:https://www.cnblogs.com/ajing2018/p/14665194.html
Copyright © 2011-2022 走看看