深入浅出java多态性,为什么要有多态

本文深入探讨Java中的多态性,通过代码示例解析多态的实现机制,包括方法覆盖、虚拟方法调用、向下转型及instanceof的使用,阐述多态在提高代码灵活性和复用性方面的重要作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我们都知道面向对象三大特征:封装,继承,多态
相比于封装和继承,多态似乎理解起来更加抽象,这篇文章将彻底揭露java多态性,文章内容较多,请耐心观看

1.通过代码体现多态性

我们先准备好三个类,男人类male,女人类female,以及他们共同的父类person

public class person {
    public void eat(){
        System.out.println("person中的eat方法");
    }
}

public class female extends person {
    @Override
    public void eat() {
        System.out.println("细嚼慢咽");
    }

    public void walk() {
        System.out.println("female中的的walk");
    }
}


public class male extends person {
    @Override
    public void eat() {
        System.out.println("狼吞虎咽");
    }

    public void walk() {
        System.out.println("male中的的walk");
    }
}

先通过最熟悉的创建对象的方式来测试这些类

public class test {
    public static void main(String[] args) {
        person p = new person();
        male man = new male();
        female woman = new female();
        p.eat();
        man.eat();
        woman.eat();
    }
}

输出结果:

person中的eat方法
狼吞虎咽
细嚼慢咽

这些都是非常正常的,毫无疑问,我们改一下test中的代码

public class test {
    public static void main(String[] args) {
        person p = new person();
        person man = new male();
        person woman = new female();
        p.eat();
        man.eat();
        woman.eat();
    }
}

请看输出结果:

person中的eat方法
狼吞虎咽
细嚼慢咽

通过上面的代码,可以提出几个疑问:

  • person man = new male();为什么没有报错?
  • person man = new male();中,man属于person类声明的对象引用,为什么不调用person中的方法

2.解释

在本例中,malefemale都是person的子类,我们做的测试都是基于这个条件,这就引出了多态成立的一个条件:

  • 类和类存在继承关系,父类引用指向子类对象,子类重写父类的方法

通过代码我们可以发现,通过person声明的引用可以是male,也可以是female,这就体现了多态性,即一个引用可以有不同的实现方式,说简单就是,只要是person的子类,都可以赋给person声明的引用

  • 需要注意的是:person man = new male();,中,虽然man是person的引用,但实际指向的是male在堆上创建的male对象,所以man调用的方法就是male中重写父类的方法了,这就涉及到了一个概念:虚拟方法调用,文章下面会讲

不要忘了我们male类中还有一个方法,这不是白写的,我们在person man = new male();前提下来调用这个方法:
在这里插入图片描述
发现报错了,无法通过编译!,为什么会这样,请接着往下看:

3.多态条件下的同名同参的方法调用

在编译期,也就是将我们写好的java代码编译成class文件的时候,在编译器编译
person man = new male();这段代码的时候,因为manperson类型的,所以man在本质上一个person类型的引用,这是我们写代码的时候声明的,毫无疑问,所以所以man只能调用在person类中出现过的方法,请记住下面四句话:

  • 如果子类重写了这个方法,且这两个方法同名同参,就调用子类的
  • 如果子类没有这个方法,就调用父类的,
  • 不能调用子类中存在而父类中不存在的方法!
  • 编译器看左边,运行期执行的是子类重写父类的方法

4.为什么要有多态

在我们编写代码的时候,尽量本着高内聚,低耦合的原则去编写,不理解这个概念没关系,我们通过一个案例来表示

public class test {
    public static void main(String[] args) {
        fun(new Dog());
        fun(new Cat());
    }

    public static void fun(Animal animal){
        animal.action();
    }
}
class Animal{
    public void action(){}
}
class Dog extends Animal{
    @Override
    public void action() {
        System.out.println("狗类的动作");
    }
}
class Cat extends Animal{
    @Override
    public void action() {
        System.out.println("猫类的动作");
    }
}

test类中定义的fun方法中,参数并没有写死,而是只要是Animal的子类都可以调用,这样就避免课将参数写死而导致代码的难以服用,在以后的框架以及其他技术中,这类的写法非常之多,如果没有多态,那么抽象类和接口也就没有意义,体现不了java的面向对象编程思想

5.关于属性的多态

我们来一个骚操作

public class test {
    public static void main(String[] args) {
        Animal a = new Dog();
        System.out.println(a.id);
    }


}
class Animal{
    int id = 1;
}
class Dog extends Animal{
   int id = 2;
}

输出:1
到这里就看不懂了,a是animal类型的,调用的是animal中的属性,不做多解释,我们抛出一个结论:

  • 多态只适用于方法,不适合属性

6.虚拟方法调用

在这里插入图片描述
这只是一个粗糙的管你虚拟方法的介绍,更加具体深入的介绍请看:重载重写的本质

7.向下转型

有了对象的多态性之后,内存中其实是加载了子类的属性和方法的,但由于声明的引用是父类类型,导致编译时只能调用父类中的属性和方法,子类特有的属性和方法无法调用,仔细看这句话,如何才能调用子类特有的属性和方法?
解决办法:强转
用我们第一步的代码来看:

public class test {
    public static void main(String[] args) {
       person p = new male();
       male m =(male)p;
    }
}

这和基本数据类型的强转差不多,这样就可以使用子类特有的属性和方法了,这就是向下转型
类比基本数据类型,可以看一下这张图:
在这里插入图片描述
是不是容易理解多了,这里多提一下向上转型,说白了向上转型就是多态,我们之前的案例都是向上转型,即父类引用指向子类对象
在进行类型转换的时候可能会出现很多问题,在这里有必要说一下:

  • 问题一:编译时通过,运行时报错
    看代码:用第一步的代码来做演示:
    person p = new male();
    female f = (female)p;
    f.walk();
    
    在运行的时候会抛出:Exception in thread "main" java.lang.ClassCastException类型转换错误异常
  • 问题二:编译时通过,运行时通过
     	 Object o = new male();
         person p1 = (person)o;
         p1.eat();
    

8.instanceof

在做类型转换的时候要使用instanceof

  • a instanceof A:判断a是不是A的一个实例,返回truefalse

拿第一部的代码来演示:

public class test {
    public static void main(String[] args) {
        person p = new male();
        System.out.println(p instanceof male);
        female f = new female();
        System.out.println(f instanceof person);
        
    }
}

输出结果:

true
true

9.关于向下转型的碎碎念

还是第一步的代码,对比两段代码

		person p2 = new person();
        if(p2 instanceof male){
            male m1 = (male)p2;
        }else{
            System.out.println("wrong");
        }


		person p3 = new male();
        if(p3 instanceof male){
            male m2 = (male)p3;
            m2.eat();
        }else{
            System.out.println("wrong");
        }

结果输出的是wrong狼吞虎咽,父类不可以强转为子类,如果强转成功,就失去了父类的特性,多态也就丧失了意义

10.总结

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值