Java面向对象进阶—多态

目录:

1.认识多态

2.多态中调用成员的特点

3.多态的优势和弊端

1.认识多态:

多态是面向对象的三大特征的最后一个,那么我们先了解什么是多态呢?实际上就是同类型的对象,表现出不同形态。

多态的表现形式:父类类型 对象名称=子类对象。

多态的前提:

1.有继承关系

2.有父类引用指向子类对象 Fu f=new Zi();

3.有方法重写

可以先体会一下多态,当我们在IDEA中创建了四个类,Administrator,Person,Student,Teacher这四个类之后,Person类中有基础的show方法,然后每个类中都重写一个新的show方法,当我们在Test类中进行测试的时候

java 复制代码
public class Test {
    public static void main(String[] args) {
        //创建三个对象,并调用register方法
        Student s = new Student();
        s.setName("张三");
        s.setAge(18);

        Teacher t = new Teacher();
        t.setName("王建国");
        t.setAge(30);

        Administrator admin = new Administrator();
        admin.setName("管理员");
        admin.setAge(35);

        register(s);
        register(t);
        register(admin);
    }

    //这个方法既能接收老师,又能接收学生,还能接收管理员
    //只能把参数写成这三个类型的父类
    public static void register(Person p){
        p.show();
    }
}
/*
运行输出:
学生的信息为:张三, 18
老师的信息为:王建国, 30
管理员的信息为:管理员, 35
*/

2.多态中调用成员的特点

变量调用:编译看左边,运行也看左边

方法调用:编译看左边,运行看右边

java 复制代码
public class Test {
    public static void main(String[] args) {
        //创建对象(多态方式)
        //Fu f=new Zi();
        Animal a = new Dog();
        //调用成员变量:编译看左边,运行也看左边
        //编译看左边:Javac在编译代码的时候,会先看左边的父类中有没有这个变量,如果有,编译成功,如果没有,则编译失败
        //运行也看左边:Java运行代码的时候,实际获取的是左边父类中成员变量的值
        System.out.println(a.name);//动物

        //调用成员方法:编译看左边,运行看右边
        //编译看左边:同上
        //运行看右边:运行代码的时候是运行子类中的方法
        a.show();//Dog --- show方法
    }
}

class Animal{
    String name = "动物";
//    public void show(){
//        System.out.println("Animal --- show方法");
//    }
}

class Dog extends Animal{
    String name = "狗";

    //@Override
    public void show() {
        System.out.println("Dog --- show方法");
    }
}

class Cat extends Animal{
    String name = "猫";

    //@Override
    public void show() {
        System.out.println("Cat --- show方法");
    }
}

对于这个该怎么理解呢?

Animal a =new Dog();现在是用a去调用变量和方法的,而a是Animal类型的,所以默认都会从Animal这个类中去找。

对于成员变量来说,由于子类和父类是继承关系,在子类的对象中,会把父类的成员变量也继承下的。比如说父:name;子:name的情况下,a.name是调用的父,因为在Animal类中。

对于成员方法来说:如果子类对方法进行了重写,那么在虚方法表中,会把父类的方法进行覆盖,但是实际上还是调用的父方法,所以是编译看右边了。

我们可以再通过内存图来加深一下想法,首先我们是先创建了Animal类,然后又用Dog类继承了Animal。然后我们代码往下走,在方法区写了Test的主函数。

先利用多态,创建a,是Animal类的,然后在方法区加载了Animal和Dog类。注意在Dog类中的show是重写过后的。然后我们会在堆中创建空间,分别存父类和子类的name,因为这个我们是Animal类的a嘛,所以当a.name的时候就会去打印出来"动物"。如果我们是之前的方法,Dog d=new Dog()这种方法,那我们就是直接访问的子类的name,如果子类中没有,才会去看父类中的name。

当利用show的时候,根据调用方法的特点,也是直接看重写的show。

3.多态的优势和弊端:

优势:

1.在多态形势下,右边对象可以实现解耦合,便于扩展和维护

java 复制代码
Person p=new Student();
p.work();//当业务逻辑发生改变时,后续代码无需修改

当我这个不想执行student的work了,我想改成teacher的,就只需要把第一行后边这个new student改成teacher就可以了。

2.定义方法的时候,使用父类型作为参数,可以接收所有子类对象,体现多态的扩展性与便利。

可以看在StringBuilder里,sb.append就可以直接添加object类型,方便了使用。

那么多态有什么弊端呢?

多态弊端:父类引用不能直接调用子类独有的方法

java 复制代码
public class Test {
    public static void main(String[] args) {
        //创建对象,多态:父类引用指向子类对象
        Animal a = new Dog();
        //编译看左边,运行看右边,执行Dog重写后的eat()
        a.eat();

        //多态弊端:父类引用不能直接调用子类独有的方法
        //a.lookHome(); //这行会编译报错,Animal类中没有lookHome方法

        //如果要调用子类特有方法,需要向下转型
        Dog d = (Dog) a;
        d.lookHome();

        //多态,父类引用指向Cat子类对象
        Animal c = new Cat();
        c.eat();
        //向下转型调用猫特有方法
        Cat cat = (Cat) c;
        cat.catchMouse();
    }
}

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

class Dog extends Animal{
    @Override
    public void eat() {
        System.out.println("狗吃骨头");
    }

    //Dog子类特有方法
    public void lookHome(){
        System.out.println("狗看家");
    }
}

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

    //Cat子类特有方法
    public void catchMouse(){
        System.out.println("猫抓老鼠");
    }
}

还是刚才的代码,如果我想转换类型,该怎么做?

利用instanceof这个关键字进行判断,在Java17+之后,就可以不用再单独写转换,直接在判断框内写就可以了

java 复制代码
public class Test {
    public static void main(String[] args) {
        Animal a = new Dog();
        a.eat();

        // ---------------- 旧版本写法 ----------------
        /*
        if(a instanceof Dog){
            Dog d = (Dog) a;
            d.lookHome();
        }else if(a instanceof Cat){
            Cat c = (Cat) a;
            c.catchMouse();
        }else{
            System.out.println("没有这个类型,无法转换");
        }
        */

        // ---------------- Java17+ 新模式匹配写法 ----------------
        if(a instanceof Dog d){
            d.lookHome();
        }else if(a instanceof Cat c){
            c.catchMouse();
        }else{
            System.out.println("没有这个类型,无法转换");
        }


        // 测试Cat对象
        Animal b = new Cat();
        b.eat();
        if(b instanceof Dog d){
            d.lookHome();
        }else if(b instanceof Cat c){
            c.catchMouse();
        }else{
            System.out.println("没有这个类型,无法转换");
        }
    }
}
相关推荐
冯汉栩1 小时前
Swift Control RollingCountdownView(动画倒计时)
开发语言·ios·swift
lisanmengmeng1 小时前
搭建elk环境并接入frostmourne,实现监控报警效果(五)
开发语言·elk·日志·日志监控
qq_185198692 小时前
SpringBoot-五-AOT
java·spring boot·后端
王大大的刀2 小时前
Spring AI 重试引起的 LLM 重复调用
java·人工智能
oh,huoyuyan2 小时前
JS 动态网页抓不到?试试火车采集器网页抓取工具
开发语言·javascript·ecmascript
风流 少年2 小时前
hutool
java·服务器·开发语言
H_oRIZoN_3 小时前
Linux入门DAY27(文件IO(系统调用)详解|open/read/write/lseek)
java·linux·服务器
延凡科技3 小时前
从 “人管机器“ 到 “数据管人“:智慧矿山综合管控落地实践
java·后端·struts
一木 之林3 小时前
四、STL容器与数据结构
开发语言·数据结构·c++