day12 多态

多态

1. 介绍

多态:

生活中的多态:同一种事物,由于条件/环境不同,产生的结果也不同

程序中的多态:同一个引用类型,使用不同的实例而执行不同操作

多态的具体描述:父类引用指向子类对象 Pet pet = new Dog()/new Penguin();

多态的前提条件:

​ 1.必须有继承

​ 2.必须有方法重写

多态涉及的转型:

​ 1.向上转型 父类引用指向子类对象

​ 2.向下转型 将指向子类对象的父类引用 强制转换为子类类型

多态的具体表现:

1.父类作为形参 子类作为实参

2.父类作为返回值 实际返回值为子类类型

3.父类类型的数组/集合 实际元素为子类类型

2. 父类作为形参

2.1 宠物系统v1

给宠物添加看病的方法

1.在Pet父类中添加cure方法

2.创建Master类实现携带宠物看病方法

package com.atguigu.test2;

public class Pet {
    protected String name;
    protected int health;
    protected int love;

    public String getName() {
        return name;
    }

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

    public int getHealth() {
        return health;
    }

    public void setHealth(int health) {
        this.health = health;
    }

    public int getLove() {
        return love;
    }

    public void setLove(int love) {
        this.love = love;
    }

    void print(){
        System.out.println("宠物的名字是:" + name + ",健康值是:" + health);
        System.out.println("亲密值是:" + love);
    }

    public Pet(){

    }

    public void cure(){
        System.out.println("宠物看病");
    }
}

package com.atguigu.test2;

public class Dog extends Pet{
    private String strain;

    public String getStrain() {
        return strain;
    }

    public void setStrain(String strain) {
        this.strain = strain;
    }

    public void print(){
        System.out.println("狗狗的品种是:" +  strain);
    }

    public void cure(){
        System.out.println("狗狗看病,吃药,吃骨头,健康值恢复");
        setHealth(100);
        this.setLove(100);
    }
}

package com.atguigu.test2;

public class Penguin extends Pet{
    private String sex;

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public void print(){
        super.print();
        System.out.println("企鹅的性别是:" + sex);
    }

    public void cure(){
        System.out.println("企鹅看病,打针,吃小鱼,健康值恢复");
        this.setHealth(100);
        this.setLove(100);
    }
}

package com.atguigu.test2;
/**
 * 主人类:
 *      属性:
 *          名字
*       方法:
 *          给宠物看病
 *
 *
 * */
public class Master {
    private String name;

    public String getName() {
        return name;
    }

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

    public void toHospitalWithDog(Dog dog){
        dog.cure();
    }

    public void toHospitalWithPenguin(Penguin penguin){
        penguin.cure();
    }

    // 思考问题:
        // 1.目前宠物系统中 只有两种宠物 我们要编写两个方法分别给不同的宠物看病
        // 如果后续有更多的宠物子类,怎么办呢?
}

package com.atguigu.test2;

public class Test {
    public static void main(String[] args) {
        Master zs = new Master();
        zs.setName("赵四");

        Dog dog = new Dog();
        dog.setHealth(50);
        dog.setLove(50);
        dog.setName("大黄");
        dog.setStrain("田园犬");

        System.out.println(dog.getHealth());  // 50

        zs.toHospitalWithDog(dog);  // 狗狗看病,吃药,吃骨头,健康值恢复

        System.out.println(dog.getHealth());  // 100

        System.out.println("-------------------------");

        Penguin p1 = new Penguin();
        p1.setHealth(20);
        p1.setLove(10);
        p1.setName("大白");
        p1.setSex("雌");

        System.out.println(p1.getHealth());  // 20
        zs.toHospitalWithPenguin(p1);  // 企鹅看病,打针,吃小鱼,健康值恢复

        System.out.println(p1.getHealth());  // 100

    }
}

2.2 宠物系统v2

使用多态优化v1版本宠物系统:父类作为形参

package com.atguigu.test3;

public class Master {
    private String name;

    public String getName() {
        return name;
    }

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

    public void toHospitalWithDog(Dog dog){
        dog.cure();
    }

    public void toHospitalWithPenguin(Penguin penguin){
        penguin.cure();
    }

    // 思考问题:
    // 1.问:目前宠物系统中 只有两种宠物 我们要编写两个方法分别给不同的宠物看病
    // 如果后续有更多的宠物子类,怎么办呢?

    // 答:按照目前的写法 每一个宠物子类 都单独编写一个方法 来实现给宠物看病
    // 后续有更多的宠物子类 我们就要编写更多的方法来实现这个功能
    // 这种方式是非常不合理的 这种方式不符合设计模块中的 '开闭原则'
    // 开:开放 编写的程序应该扩展开发
    // 闭:关闭 编写的程序应该对修改源代码关闭


    // 2.问:不能使用每个宠物单独编写一个方法这种方式实现给宠物看病 那应该如何实现呢?
    // 答:应该编写一个方法 实现给所有的宠物子类看病

    public void toHospitalWithPet(Pet pet){
        pet.cure();
    }
}

3. 父类作为返回值

2.抽奖送宠物

一等奖:送企鹅一只

二等奖:送狗狗一只

三等奖:送猫咪一只

幸运奖:送成年东北虎一只

package com.atguigu.test4;

/**
 * 主人类:
 *      属性:
 *          名字
 *      方法:
 *          给宠物看病
 *          抽奖送宠物
 *              一等奖:送企鹅一只
 *              二等奖:送狗狗一只
 *              三等奖:送喵咪一只
 * */

public class Master {
    private String name;

    public String getName() {
        return name;
    }

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

    public void toHospitalWithPet(Pet pet){
        pet.cure();
    }

    public Penguin givePenguin(){
        Penguin penguin = new Penguin();
        penguin.setName("大白");
        penguin.setHealth(100);
        penguin.setSex("雌性");
        penguin.setLove(100);

        return penguin;
    }

    public Dog giveDog(){
        Dog dog = new Dog();
        dog.setStrain("田园犬");
        dog.setHealth(100);
        dog.setLove(100);
        dog.setName("小黄");

        return dog;
    }

    public Cat giveCat(){
        return new Cat();
    }

    // 分析问题
    // 目前我们针对每一种奖项都单独编写一个方法来对应奖项
    // 后续再有更多的奖项 那么我们需要编写更多的方法来实现 这种方式非常不合适/不灵活
    // 我们应该编写一个方法用于实现所有的奖项抽奖功能

    public Pet givePet(String str){
        if (str.equals("一等奖")){
            Penguin penguin = new Penguin();
            penguin.setName("大白");
            penguin.setHealth(100);
            penguin.setSex("雌性");
            penguin.setLove(100);

            return penguin;
        }else if(str.equals("二等奖")){
            Dog dog = new Dog();
            dog.setStrain("田园犬");
            dog.setHealth(100);
            dog.setLove(100);
            dog.setName("小黄");

            return dog;
        }else if(str.equals("三等奖")){
            return new Cat();
        }
        return null;
    }

    public static double m1(){
        int a = 20;
        return a;
    }
}

package com.atguigu.test4;

public class Pet {
    protected String name;
    protected int health;
    protected int love;

    public String getName() {
        return name;
    }

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

    public int getHealth() {
        return health;
    }

    public void setHealth(int health) {
        this.health = health;
    }

    public int getLove() {
        return love;
    }

    public void setLove(int love) {
        this.love = love;
    }

    void print(){
        System.out.println("宠物的名字是:" + name + ",健康值是:" + health);
        System.out.println("亲密值是:" + love );
    }

    public Pet(){
    }

    public void cure(){
        System.out.println("宠物看病");
    }
}

package com.atguigu.test4;

public class Dog extends Pet{
    private String strain;

    public String getStrain() {
        return strain;
    }

    public void setStrain(String strain) {
        this.strain = strain;
    }

    @Override
    public void print(){
        System.out.println("狗狗的品种是:" + strain);
    }


    public void cure(){
        System.out.println("狗狗看病,吃药,吃骨头,健康值恢复");
        setHealth(100);
        this.setLove(100);
    }

    @Override
    public String toString() {
        return "Dog{" +
                "strain='" + strain + '\'' +
                ", name='" + name + '\'' +
                ", health=" + health +
                ", love=" + love +
                '}';
    }
}

package com.atguigu.test4;



public class Cat extends Pet {

    public String toString(){
        return "Cat class toString()";
    }
}

package com.atguigu.test4;

public class Penguin extends Pet{
    private String sex;

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }


    public void print(){
        super.print();
        System.out.println("企鹅的性别是:" + sex);
    }

    public void cure(){
        System.out.println("企鹅看病,打针,吃小鱼,健康值恢复");
        this.setHealth(100);
        this.setLove(100);
    }

    @Override
    public String toString() {
        return "Penguin{" +
                "sex='" + sex + '\'' +
                ", name='" + name + '\'' +
                ", health=" + health +
                ", love=" + love +
                '}';
    }
}

package com.atguigu.test4;

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

        Master zs = new Master();
        zs.setName("赵四");

        Pet pet1 = zs.givePet("一等奖");  // Penguin 对象
        System.out.println(pet1);  // Penguin{sex='雌性', name='大白', health=100, love=100}

        Pet pet2 = zs.givePet("二等奖");  // Dog 对象
        System.out.println(pet2);  // Dog{strain='田园犬', name='小黄', health=100, love=100}

        Pet pet3 = zs.givePet("三等奖");  // Cat 对象
        System.out.println(pet3);  // Cat class toString()

        Pet pet4 = zs.givePet("四等奖");
        System.out.println(pet4);  // null
    }
}

4. 父类类型集合/数组

父类类型的数组/集合 元素为子类类型

package com.atguigu.test5;

import com.atguigu.test4.Cat;
import com.atguigu.test4.Dog;
import com.atguigu.test4.Penguin;
import com.atguigu.test4.Pet;

/**
 * 父类类型的数组/集合 元素为子类类型
 * */
public class Test1 {
    public static void main(String[] args) {
        Pet[] pets = new Pet[3];

        pets[0] = new Dog();
        pets[1] = new Cat();
        pets[2] = new Penguin();


    }
}

5. 向下转型和instanceof关键字

父类引用指向子类对象 向上转型:此时可以访问子类重写父类或者继承父类(父类中)的方法

不能访问子类独有的方法 如需访问子类独有的方法 必须向下转型

向下转型:将指向子类对象的父类引用 强制转换为子类类型

不能直接将一个 指向父类对象的父类引用 强制转换为子类类型

在引用数据类型强制类型转换之前 务必使用instanceof关键字进行类型判断

package com.atguigu.test6;
/**
 * 父类引用指向子类对象 向上转型:此时可以访问子类重写父类或者继承父类(父类中)的方法
 * 不能访问子类独有的方法 如需访问子类独有的方法 必须向下转型
 *
 * 向下转型:将指向子类对象的父类引用 强制转换为子类类型
 * 不能直接将一个指向父类对象的父类引用 强制转换为子类类型
 *
 * 在引用数据类型强制类型转换之前 务必使用instanceof关键字进行类型判断
 * */
public class Test1 {
    public static void main(String[] args) {
        Pet pet1 = new Dog();  // 父类引用指向子类对象 向上转型
//        pet1.playWithMaster();  // 无法访问子类独有的方法

        Dog dog = (Dog)pet1;
        dog.playWithMaster();  // 狗狗和主人玩耍~

        System.out.println("---------------------");

        Pet pet2 = new Pet();

        // 实际开发中 为了避免引用数据类型强制转换错误
        // 我们可以使用instanceof关键字进行类型判断
        // 语法: 对象名 instanceof 类名
        // 作用: 表示判断 左侧的对象 是否属于 右侧的类型 属于返回true 不属于返回false

        if (pet2 instanceof Penguin){
            Penguin penguin = (Penguin)pet2;
            penguin.skating();
        }else{
            System.out.println("类型不匹配");  // 类型不匹配
        }

        String str = "abc";
        System.out.println(str.equals(dog));  // false

    }
}

6. 多态实现原理

我们可以通过 javap -verbose class文件名来反编译查看java文件编译之后的内容

关于多态的虚方法和非虚方法(实方法)

问:1.什么是虚方法?

​ 答:在编译期间无法确定具体调用哪个方法的这些方法 可以被子类重写的方法 称之为虚方法

​ 底层通过invokevirtual指令实现方法调用

问:2.什么是非虚方法?

​ 答:在编译期间可以确定具体调用方法的这一类方法 属于非虚方法

​ 比如 静态方法 private修饰的方法 构造方法

​ 底层通过invokespecial指令实现方法调用

虚方法和动态绑定

​ 每个类信息文件中都维护一个方法表,此方法表被实现为一个数组

​ 在编译期间 只能确定等号左侧类型可以访问的方法 以及 左侧类型继承父类的方法

​ 只有在程序执行过程中 才能确定右侧的子类类型 具体是哪个子类

​ 确定具体子类之后才可以确定具体调用哪个子类中重写父类的方法

​ 这个过程就是动态绑定

多态实现原理

7. this关键字的实现

this关键字底层被Java实现为一个隐式参数 隐藏式的参数

通过 javap -verbose class文件名称 查看class文件

8. final关键字

final关键字: 单词 - 最终

适用场景:可以修饰

属性:

​ 被final修饰的属性称之为常量 其值只能被赋值一次 且不能被改变

​ 通常(99%)在定义的时候赋值 或者 在构造方法中赋值

​ 以上两种赋值方式都是为了保证当前常量 在使用之前是有值的

​ 常量名称全部大写(醒目) 多个单词使用下划线分割

静态常量:

​ 被final修饰的属性称之为常量 常量作为一个固定值的数据 通常(99%)也没有必要存在多份

​ 所以我们再加上static关键字修饰 静态常量

​ 通常(99%)在定义的时候赋值 或者 在静态代码块中赋值

​ 以上两种方式 依然是为了保证 使用静态常量之前 是有值的

方法:

​ 被final修饰的方法可以被继承 但是不能被重写

类:

​ 被final修饰的类不能被继承

package com.atguigu.test10;


import com.atguigu.test8.Test;

/**
 *
 * final关键字: 单词 - 最终
 * 使用场景: 可以修饰
 *      属性:被final修饰的属性称之为常量 其值只能被赋值一次 并不能被改变
 *          通常(99%)在定义的时候赋值 或者 在构造方法赋值
 *          以上两种赋值方式都是为了保证当前常量 在使用之前是有值的
 *          常量名称全部大写 多个单词使用下划线分割
 *      方法:被final修饰的方法可以被继承 但是不能被重写
 *      类:被final修饰的类不能被继承
 *
 * */
public class Test1 {
    final double PI = 3.14;  // 圆周率
    final String COUNTRY_NAME;
    double radius;  // 半径

    public Test1(){
        COUNTRY_NAME = "中华人民共和国";
    }

    public Test1(double radius){
        this.radius = radius;
        COUNTRY_NAME = "中国";
    }

    public static void main(String[] args) {
        Test1 t1 = new Test1();

//        t1.PI = 3.2;  // 无法被修改
        t1.radius = 10;

        System.out.println(t1.PI * t1.radius * t1.radius);  // 314.0
    }
}

package com.atguigu.test10;



/**
 * final关键字: 单词 - 最终
 *
 * 常量: 被final修饰的属性称之为常量 常量作为一个固定值的数据 通常也没有必要存在多份 所以我们再加上static关键字修饰 静态常量
 *
 * 静态常量:
 *      通常在定义的时候赋值 或者 在静态代码块中赋值
 *      以上两种方式 依然是为了保证 使用静态常量之前是有值的
 * */
public class Test2 {

    static final double PI = 3.14;  // 圆周率
    static final String COUNTRY_NAME;

    static {
        COUNTRY_NAME = "中华人民共和国";
    }

    public static void main(String[] args){

        Test2 t1 = new Test2();
        System.out.println(t1.PI);  // 3.14

        Test2 t2 = new Test2();
        System.out.println(t2.PI);  // 3.14
    }
}

package com.atguigu.test10;

public class A {
    public void m1(){
        System.out.println("A class m1方法");
    }

    public final void m2(){
        System.out.println("A class m2方法");
    }
}

package com.atguigu.test10;

public class A1 extends A{

    public void m1(){
        super.m1();
    }

    // 无法重写final修饰的方法
//    public void m2(){
//        super.m2();
//    }

    public static void main(String[] args) {
        A1 a1 = new A1();

        a1.m1();
        a1.m2();
    }
}

package com.atguigu.test10;

public final class B {
    
}

// 无法继承final修饰的类
//class C extends B{
//    
//}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,根据您的要求,我来为您编写代码: ```c++ #include <iostream> #include <string> using namespace std; // 定义Person类作为基类 class Person { protected: string name; // 姓名 int year; // 出生年份 int month; // 出生月份 int day; // 出生日期 public: Person(string n, int y, int m, int d) : name(n), year(y), month(m), day(d) {} virtual void Print() { cout << "姓名:" << name << endl; cout << "出生日期:" << year << "年" << month << "月" << day << "日" << endl; } }; // 定义Teacher类,继承自Person类 class Teacher : public Person { private: string Tno; // 工号 public: Teacher(string n, int y, int m, int d, string t) : Person(n, y, m, d), Tno(t) {} virtual void Print() { Person::Print(); // 调用基类的Print函数,打印姓名和出生日期 cout << "工号:" << Tno << endl; } }; // 定义Student类,继承自Person类 class Student : public Person { private: string Sno; // 学号 public: Student(string n, int y, int m, int d, string s) : Person(n, y, m, d), Sno(s) {} virtual void Print() { Person::Print(); // 调用基类的Print函数,打印姓名和出生日期 cout << "学号:" << Sno << endl; } }; int main() { // 定义基类指针和派生类对象 Person* ptr; Teacher t("张三", 1980, 3, 14, "1001"); Student s("李四", 2000, 9, 1, "20180101"); // 通过基类指针指向派生类对象,实现多态 ptr = &t; ptr->Print(); // 调用Teacher类的Print函数 cout << endl; ptr = &s; ptr->Print(); // 调用Student类的Print函数 cout << endl; return 0; } ``` 希望这段代码能够满足您的要求,如果有任何问题,欢迎继续提问!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值