第十三章、JavaSE专题之面向对象的三大特性之多态详细剖析

1、多态引入

  • 含义:方法或对象具有多种形态,是面向对象的第三大特征,多态建立在封装和继承的基础之上的;

  • 业务场景:请编写一个程序,master类中有一个feed(喂食方法),可以完成主任给动物喂食物的信息;

  • 思路分析

/*
1、创建父类 - 食物类Food
	1.1、创建子类 - Fish继承Food;
	1.2、创建子类 - Bone继承Food;

2、创建父类 - 动物类Animal
	2.1、创建子类 - Dog继承Animal
	2.2、创建子类 - Cat继承Animal
	
3、创建Master类 - feed方法
*/
  • 范例
//1、创建父类 - 动物类Animal
package com.taobao;

public class Animal {
    private String name;

    public Animal(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

//1.1、创建子类 - Dog继承Animal
package com.taobao;

public class Dog extends Animal {
    public Dog(String name) {
        super(name);
    }
}

//1.2、创建子类 - Cat继承Animal
package com.taobao;

public class Cat extends Animal{
    public Cat(String name) {
        super(name);
    }
}

//2、创建父类 - 食物类Food
package com.taobao;

public class Food {

    //1、食物名称属性定义
    private String name;

    //2、构造器
    public Food(String name) {
        this.name = name;
    }

    //3、生成一个get/set方法
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

//2.1、创建子类 - Fish继承Food;
package com.taobao;

public class Fish extends Food {
    public Fish(String name) {
        super(name);
    }
}

//2.2、创建子类 - Bone继承Food;
package com.taobao;

public class Bone extends Food {
    public Bone(String name) {
        super(name);
    }
}

//3、创建Master类
package com.taobao;

public class Master {
    private String name;

    public Master(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    //1、主人给小狗喂食骨头

    public void feed(Dog dog,Bone bone){
        System.out.println("主人" + name + "给" + dog.getName() + "吃" + bone.getName());
    }
    //2、主人给小狗喂食骨头

    public void feed(Cat cat,Fish fish){
        System.out.println("主人" + name + "给" + cat.getName() + "吃" + fish.getName());
    }
}

//4、创建main方法调用上述类
package com.taobao;

public class hello {
    public static void main(String[] args) {

        //1、主人汤姆给大黄吃大棒骨
        Master tom = new Master("汤姆");
        Dog dog = new Dog("大黄");
        Bone bone = new Bone("大棒骨");

        tom.feed(dog,bone);

        //2、主人汤姆给小花猫吃黄花鱼
        Cat cat = new Cat("小花猫");
        Fish fish = new Fish("黄花鱼");

        tom.feed(cat,fish);
    }
}
  • 运行结果

在这里插入图片描述

  • 传统方法
    • 问题:代码复用性不高,而且不利于代码维护
    • 解决方案:多态解决

2、多态概述

  • 含义:方法或对象具有多种形态,是面向对象的第三大特征;
2.1、编译时多态 - 方法的多态

①编译期间决定目标方法

②通过overloading重载实现

方法名相同,参数不同

public int sum(int n1,int n2){
	return n1 + n2;
}
public int sum(int n1,int n2,int n3){
	return n1 + n2 + n3;
}
2.2、运行时多态 - 对象的多态(重点)

一个对象的编译类型和运行类型可以不一致

编译类型在定义对象时,就确定了,不能改变

运行类型可以变化的;

编译类型看定义时 = 号的左边运行类型= 号的右边

// 可以让父类的引用指向子类的对象
Animal animal = new Dog()
// animal的编译类型是Animal,运行类型是Dog
animal = new Cat()
// animal的编译类型仍然是Animal,运行类型变成了Cat
2.3、多态内存剖析

①编译时多态内存剖析

后续更新

②运行时多态内存剖析

后续更新

2.4、多态使用
  • 前提:多态类型把自己看做是父类类型

①成员变量:使用的是父类的

②成员方法:由于存在重写现象,使用的是子类的

③静态成员:随着类的加载而加载,谁调用返回谁

3、方法的多态

  • 场景:重写和重载就体现方法多态
3.1、方法重载体现多态
  • 范例
package com.taobao;

public class hello {
    public static void main(String[] args) {
        // 重载体现多态:传入不同方法,会调用不同sum方法,体现多态
        B b = new B();
        System.out.println("两个参数的和:"+b.sum(1,2));
        System.out.println("三个参数的和:"+b.sum(1,2,3));
    }
}

class A { // 父类
    public void say(){
        System.out.println("A say() 方法被调用...");
    }
}

class B extends A{ // 子类
    public int sum(int n1,int n2){
        return n1 + n2;
    }
    public int sum(int n1,int n2,int n3){
        return n1 + n2 + n3;
    }
}
  • 运行结果

在这里插入图片描述

3.2、方法重写体现多态
  • 范例
package com.taobao;

public class hello {
    public static void main(String[] args) {
        // 方法重写体现多态:因方法重写,会自动定位相应的方法
        A a = new A();
        B b = new B();
        a.say();
        b.say();
    }
}

class A { // 父类
    public void say(){
        System.out.println("A say() 方法被调用...");
    }
}

class B extends A{ // 子类
    public void say(){
        System.out.println("B say() 方法被调用...");
    }
    public int sum(int n1,int n2){
        return n1 + n2;
    }
    public int sum(int n1,int n2,int n3){
        return n1 + n2 + n3;
    }
}
  • 运行结果

在这里插入图片描述

4、对象的多态(核心)

4.1、多态的入门案例
  • 范例
//1、定义父类 - Animal类
public class Animal {
    public void cry(){
        System.out.println("动物在叫");
    }
}

//2、定义子类 - Dog类继承Animal
public class Dog extends Animal {
    public void cry(){
        System.out.println("小狗汪汪叫...");
    }
}

//3、定义子类 - Cat继承Animal
public class Cat extends Animal {
    public void cry(){
        System.out.println("小猫喵喵叫...");
    }
}

//4、main方法引用
public class poly2 {
    public static void main(String[] args) {
        //体验对象多态特点

        //animal 编译类型为Animal , 运行类型 Dog
        Animal animal = new Dog();
        //因为运行时,这时就执行到该行时,animal运行类型是Dog,所以cry就是Dog的cry
        animal.cry();

        //animal 编译类型Animal,运行类型就是Cat,指针指向猫
        animal = new Cat();
        animal.cry();
    }
}
  • 运行结果

在这里插入图片描述

  • 注意事项:
    • P:为什么编译类型和运行类型不一致
    • A:一针多用:
      • 个父类的引用既可以指向父类,也可以指向子类;
      • 如果animal中没有定义cry方法,则编译报错;
4.2、多态应用场景
  • 多态前提:两个对象(类)存在继承关系

  • 范例:使用多态可以统一的管理主人喂食问题,简化feed方法

//1、使用多态机制
public class Mater {
    private String name;

    public Mater(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    //多态:使用多态机制,可以统一的管理主人喂食的机制
    //animal 编译类型是Animal,可以指向(接收)Animal子类的对象
    //animal 编译类型是Food,可以指向(接收)Food子类的对象
    public void feed(Animal animal,Food food){
        System.out.println("主人" + name + "给" + animal.getName() + "吃" + food.getName());
    }
}


//2、创建子类 - Pig继承Animal类
public class Pig extends Animal {
    public Pig(String name) {
        super(name);
    }
}

//3、创建子类 - Rice继承Food类
public class Rice extends Food {
    public Rice(String name) {
        super(name);
    }
}

//4、编写main方法
public class Poly01 {
    public static void main(String[] args) {

        //1、主人汤姆给大黄吃大棒骨
        Mater tom = new Mater("汤姆");
        Dog dog = new Dog("大黄");
        Bone bone = new Bone("大棒骨");

        tom.feed(dog,bone);

        //2、主人汤姆给小花猫吃黄花鱼
        Cat cat = new Cat("小花猫");
        Fish fish = new Fish("黄花鱼");

        tom.feed(cat,fish);

        //3、主人汤姆给花花吃米饭
        Pig pig = new Pig("花花");
        Rice rice = new Rice("米饭");
        tom.feed(pig,rice);
    }
}
  • 运行结果 : 使用多态后的父类引用变量调用子类方法时,会调用子类重写后的方法;

在这里插入图片描述

4.3、向上转型
  • 从子类开始找方法,如果有则调用,如果没有则向父类找
    • 总而言之:可以调用子类的,可以调用父类的,不能调用子类的特有成员&方法

在这里插入图片描述

本质:父类的引用指向了子类的对象;
语法:父类类型 引用名 = new 子类类型()  //把子类提上为父类
特点:编译类型看左边,运行类型看右边。
    可以调用父类的所有成员(须遵守访问权限);
    不能调用子类中的特有成员;
	最终运行效果看子类的具体体现;
  • 范例
//1、创建父类Animal
public class Animal {
    String name = "动物";
    int age = 10;
    public void sleep(){
        System.out.println("睡觉!");
    }
    public void run(){
        System.out.println("跑");
    }
    public void eat(){
        System.out.println("吃");
    }
    public void show(){
        System.out.println("hello world!");
    }
}

//2、创建子类 - Cat继承Animal
public class Cat extends Animal {
    public void eat(){ //方法重写
        System.out.println("猫吃鱼");
    }
    public void catchMouse(){//cat特有方法
        System.out.println("猫抓老鼠");
    }
}


//3、编写main方法实现多态
public class poly {
    public static void main(String[] args) {
        //向上转型:父类的引用指向子类的对象
        Animal animal = new Cat();
        System.out.println("ok~~");

        //注意事项:可以调用父类的所有成员(须遵守访问权限),但是不能调用子类特有的成员
        //	因为在编译阶段能调用那些成员是由编译类型来决定的;
        //	最终的运行效果看子类(运行类型)的具体实现,即调用方法,按照从子类开始查找方法然后调用,与继承规则一致
        animal.eat();
        animal.run();
        animal.show();
        animal.sleep();
    }
}
  • 运行结果 : 调用eat,run,show先找Cat子类,Cat子类中有eat方法调用,run和show子类中没有则向上找,子类中的特有方法无法使用

在这里插入图片描述

4.4、向下转型
  • 使用场景:之前向上转型过的子类对象想要执行子类的特有功能,重新恢复成子类对象。

  • 注意事项

    • ①语法: 子类类型 引用名 = (子类类型) 父类引用;
    • ②只能强转父类的引用,不能强转父类的对象;
      必须先定义父类的引用,之后进行父类应用的强转
    • ③要求父类的引用必须指向的是当前目标类型的对象;
      猫类的动物类引用不能指向狗类
    • 当向下转型后可以调用子类类型中所有的成员;
  • 范例

public class hello {
    public static void main(String[] args) {
        //向上转型:父类的引用指向子类的对象
        Animal animal = new Cat();
        //向下转型:cat的编译类型Cat,运行类型是Cat
        Cat cat = (Cat) animal;
        //Object animal1 = new Cat();  //可以定义,Object是animal的父类
        System.out.println("ok~~");
        animal.eat();
        animal.run();
        animal.show();
        animal.sleep();
        cat.catchMouse();
    }
}
  • 运行结果

在这里插入图片描述

4.5、动态绑定机制(非常重要)
  • Java动态绑定机制

    • 1、当调用对象方法的时候,该方法回合该对象的内存地址/运行类型
    • 2、当调用对象属性时,没有动态绑定机制,哪里声明哪里使用
  • 引入案例

package com.taobao;

public class hello {
    public static void main(String[] args) {
        A a = new B();  //向上转型
        System.out.println(a.sum());
        System.out.println(a.sum1());
    }
}

class A {
    public int i = 10;
    public int sum(){
        return get1() +10;
    }

    public int sum1(){
        return i+10;
    }

    public int get1(){
        return i;
    }
}

class B extends A {
    public int i = 20;
    public int sum(){
        return i + 20;
    }
    public int sum1(){
        return i+10;
    }
    public int get1(){
        return i;
    }
}
  • 运行结果

在这里插入图片描述

  • 范例:当把子类的sum(),sum1()方法注释
package com.taobao;

public class hello {
    public static void main(String[] args) {
        A a = new B();  //向上转型
        System.out.println(a.sum());
        System.out.println(a.sum1());
    }
}

class A {
    public int i = 10;
    public int sum(){
        return get1() +10;   //get1调用父类还是子类的,动态绑定机制,调用子类的get1
    }

    public int sum1(){
        return i+10;
    }

    public int get1(){
        return i+1;
    }
}

class B extends A {
    public int i = 20;
    public int get1(){
        return i;
    }
}
  • 运行结果

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

随缘清风殇

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值