【Java】面向对象之多态超级详解!!

文章目录


前言

为了深入了解JAVA的面向对象的特性,今天继续来学习剖析多态。多态在Java中的应用非常广泛,它在继承的前提下让子类将父类中的方法进行重写,最后通过父类对象访问到子类重写的方法实现。它可以大大优化代码篇幅,让代码整体更清晰整洁,同时更方便使用者进行代码的迭代更新!

提到了很多新内容,本篇文章将会详细介绍Java语言中的多态概念,重写,向上转型和向下转型等相关内容,让我们速速开始吧!!


看前提醒🚨:想要了解多态就先需要学习继承,相关内容在该篇博客:【Java】面向对象之继承超级详解!!有详细讲解,学完再食用本篇博客效果更佳哦🌹🌹

一、多态

1.1 多态的概念

多态的概念:简单来说,就是多种形态,去完成某个行为,当不同的对象去完成时会产生不同的状态。

举个栗子🌰!

自然界有各种动物,它们发出的声音不尽相同,但都是做的发声这个动作

总而言之:同一件事,发生在不同对象身上,就会产生不同的结果。

那语言环境中如何实现多态?

1.2 多态的实现条件

在Java中如要实现多态,则需要满足以下三个条件,缺一不可:

  1. 必须要在继承的条件下
  2. 子类必须要对父类中 的相关成员方法进行重写
  3. 通过父类的引用调用重写的方法

我们来看一段代码:

java 复制代码
class Animal{
    String name;
    int age;
    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public void eat(){
        System.out.println(name+"吃饭....");
    }
}
class Dog extends Animal{
    public Dog(String name,int age){
        super(name,age);
    }
    @Override
    public void eat() {
        System.out.println(name + "正在吃狗粮.....");
    }
}

class Cat extends Animal{
    public Cat(String name, int age) {
        super(name, age);
    }
    @Override
    public void eat() {
        System.out.println(name+"正在吃猫粮......");;
    }
}

//*******************************************
public class test {
    static void eat(Animal A){
        A.eat();
    }
    public static void main(String[] args) {
        Dog dog = new Dog("冲冲",18);
        Cat cat = new Cat("JAVA",90);
        eat(dog);
        eat(cat);
        System.out.println("别忘了点赞三连支持欧o(>ω< )o!!!");
    }
}
//*******************
//运行结果:
//冲冲正在吃狗粮.....
//JAVA正在吃猫粮.....

我们可以看到同样用eat方法最后输出的内容却不同。

当类的调用者在编写eat方法的时候,参数类型为Animal(父类),此时该方法的内部并不知道也不关注 当前的A引用指向的是哪种类型(哪个子类)的实例,则此时A引用调用eat方法时就可能会有不同的结果表现,而这种思想行为就称为多态

1.3 重写

在上面的实现条件中讲到要对父类中的相关方法进行重写,那么重写又是什么呢?

重写(Override):也称之为覆盖,覆写。重写是对子类对父类中类非静态、非private修饰,非final修饰,非构造方法 等的成员方法进行重新编写。返回值,方法名和形参都不能改变! 即外壳不变,核心重写。

重写的好处在于能实现子类的需要,定义特定于自己的行为,也就是说子类能根据需要实现父类的方法。

1.3.1方法重写的规则

  1. 子类在重写父类对应方法时,一般必须与父类对应方法一致:返回值类型、形参(参数列表)、方法名
  2. 访问权限不能 比父类对应重写方法的访问权限更低
    例如:如果父类方法由protected修饰,则重写的子类方法只能由protected或public修饰,否则重写失败😭
  3. 父类被static、private、final修饰 的方法不能被重写
  4. @Override 注解 重写方法时可使用该注解来显式指定,有了该注解可以帮我们在编写代码时进行一些合法性校验。
    例如:编写时不小心将eat打成了aet(出现了拼写错误),那么此时编译器就会发现父类中没有aet方法,就会编译报错,提示无法进行重写。

1.3.2重写和重载的区别

要点 重写(Override) 重载(overload)
参数列表 一定不能修改 必须修改
返回类型 一定不能修改【除非能构成父子关系】 可以修改
访问限定符 一定不能做更严格的限制(可以降低限制) 可以修改

重写的设计原则

对于已经投入使用的类,尽量不要进行修改。最好的方法是:重复利用其中共性的内容,并且添加或者改动新的内容,实现迭代更新。

1.4 向上转型和向下转型

1.4.1向上转型

实际上就是创建一个子类对象 ,将其当成父类对象 来使用。

语法格式:父类类型 对象名 = new 子类类型()

java 复制代码
Animal animal = new Cat("修猫",88);

animal是父类类型,但可以引用子类对象,因为是从大范围中找小范围的效果。

使用方法

  1. 直接赋值

    java 复制代码
    public static void main(String[] args) {
        Dog dog = new Dog("冲冲",18);
        Animal animal1 = dog;//这就属于直接赋值
        animal1.eat();
        Cat cat = new Cat("JAVA",90);
        Animal animal2 = cat;
        animal2.eat();
    }
  2. 方法传参

    java 复制代码
    static void eat(Animal A){
        A.eat();
    }
    public static void main(String[] args) {
        Dog dog = new Dog("冲冲",18);
        Cat cat = new Cat("JAVA",90);
        eat(dog);//这就是传参实现向上转型
        eat(cat);
    }
  3. 通过返回值,进行向上转型

    java 复制代码
    Animal eat(){
        return new Cat("小猫",2);
    }

向上转型的优点 :让代码实现的更简单灵活。
向上转型的缺陷:不能调用到子类的特有方法。

1.4.2向下转型

将一个子类对象进行向上转型之后当成父类方法使用,在无法调用子类的方法,但有时候如果需要去调用子类特有的方法,我们就需要用到向下转型:将父类引用再还原成子类对象即可。

我们再看一段代码:

java 复制代码
class Animal{
    String name;
    int age;
    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public void eat(){
        System.out.println(name+"吃饭....");
    }
}
class Dog extends Animal{
    public Dog(String name,int age){
        super(name,age);
    }
    @Override
    public void eat() {
        System.out.println(name + "正在吃狗粮.....");
    }
    public void bark(){
        System.out.println(name+"正在旺旺叫.....");
    }
}

class Cat extends Animal{
    public Cat(String name, int age) {
        super(name, age);
    }
    @Override
    public void eat() {
        System.out.println(name+"正在吃猫粮......");;
    }
    public void mew(){
        System.out.println(name+"正在喵喵叫.....");
    }
}

public class down {
    public static void main(String[] args) {
        Cat cat = new Cat("小黑",2);
        Dog dog = new Dog("小橘", 1);

        Animal animal1 = cat;
        animal1.mew();
        Animal animal2 = dog;
        animal2.bark();
    }
}
//最后会编译失败,编译时编译器将animal当成Animal对象处理
//而Animal类中没有bark和mew方法,因此编译失败

我们可以通过向下转型操作实现:

java 复制代码
//程序可以通过编程,但运行时抛出异常
//现在要强制还原
public class down {
    public static void main(String[] args) {
        Cat cat = new Cat("小黑",2);
        Dog dog = new Dog("小橘", 1);

        Animal animal1 = cat;
        //animal1.mew();
        cat = (Cat)animal1;//进行强制还原
        cat.mew();
        Animal animal2 = dog;
        //animal2.bark();
        dog = (Dog)animal2;
        dog.bark();
    }
}

//编译结果:
//小黑正在喵喵叫.....
//小橘正在旺旺叫.....

因向下转型用的比较少,而且不安全,万一转换失败,运行时就会抛出异常。Java为了提高向下转型的安全性,引入了关键词instanceof,如果表达式为true,则可安全转换。

java 复制代码
public class down {
    public static void main(String[] args) {
        Cat cat = new Cat("小黑",2);
        Dog dog = new Dog("小橘", 1);

        Animal animal1 = cat;
        Animal animal2 = dog;
        //animal1.mew();
        if(animal1 instanceof Cat){
            cat = (Cat)animal1;
            cat.mew();
        }
        //animal2.bark();
        if(animal2 instanceof Dog){
            dog = (Dog)animal2;
            dog.bark();
        }
    }
}

instanceof关键词官方介绍:https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.20.2

1.5 多态的优缺点

1.5.1 使用多态的好处

  1. 能够降低代码的"圈复杂度",避免使用大量的if-else

什么叫"圈复杂度"?

圈复杂度是一种描述一段代码复杂程度的方式。一段代码如果是平铺直叙,那么就比较简单容易理解,但如果有很多的条件分支或循环语句,则认为理解起来更复杂。

因此我们可以简单粗暴的计算一段代码中条件语句和循环语句出现的个数,结果个数就称为"圈复杂度"。如果一个方法的圈复杂度台稿,就需要考虑重构。

  1. 可扩展能力更强
    如果要增加一种新的功能,使用多态方式的代码改动成本也比较低。

1.5.2 使用多态的缺陷

代码的运行效率降低

  1. 属性没有多态性
    当父类和子类都有同名属性的时候,通过父类引用,只能引用父类自己的成员属性
  2. 构造方法没有多态性
    尽量不要在构造器中调用方法,如果该方法被子类重写,就会触发动态绑定,而此时子类对象还没构造完成,可能会出现一些隐藏而又极难发现的问题。

结语

好了那么以上就是本篇"【Java】面向对象之多态超级详解!!"博客的全部内容啦,感谢各位的阅读=v=,如有不足之处欢迎在评论区指出哦!!

觉得不错的话别忘了点赞三连支持一下欧!拜托啦这对我真的很重要o(>ω< )o!!!

相关推荐
500佰4 分钟前
AI手办,Gemini 2.5 Flash Image 可一键制作高一致性人物手办
算法
华仔啊16 分钟前
别再问了!Java里这几种场景,用抽象类就对了
java·后端
明天过后012233 分钟前
PDF文件中的相邻页面合并成一页,例如将第1页和第2页合并,第3页和第4页合并
java·python·pdf
黑客影儿34 分钟前
在Godot中为您的游戏添加并控制游戏角色的完整技术指南
开发语言·游戏·游戏引擎·godot·gdscript·游戏开发·3d游戏
tingting011935 分钟前
Spring Boot 外部配置指定不生效的原因与解决
java·spring boot·后端
愚润求学43 分钟前
【贪心算法】day3
c++·算法·leetcode·贪心算法
用户0332126663671 小时前
Java 设置 Excel 行高列宽:告别手动调整,拥抱自动化高效!
java·excel
2501_909686701 小时前
基于SpringBoot的网上点餐系统
java·spring boot·后端
neoooo1 小时前
Spring Boot 3 + Kafka 实战指南
java·spring boot·kafka
天天摸鱼的java工程师1 小时前
聊聊线程池中哪几种状态,分别表示什么?8 年 Java 开发:从业务踩坑到源码拆解(附监控实战)
java·后端