Java 继承和多态的作用及好处

2025-06-23 16:50
文章标签 多态 java 好处 继承 作用

本文主要是介绍Java 继承和多态的作用及好处,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《Java继承和多态的作用及好处》文章讲解Java继承与多态的概念、语法及应用,继承通过extends复用父类成员,减少冗余;多态实现方法重写与向上转型,提升灵活性与代码复用性,动态绑定降低圈复杂度...

1. 继承

1.1 什么是继承

生活中有很多继承的例子,如儿子继承父亲的家产,学弟继承学长的宿舍等,而在Java中继承就是子类继承了父类里面的成员,也就相当于在父类的基础上进行拓展的内容放到子类里面,从简单到复杂的过程,而Java里面的继承是通过extends关键字实现的。

1.2 继承的作用和好处

当有两个类dog类和cat类如下:

public class Dog {
    public String name;
    public int age;
    public void eat() {
        System.out.println(this.name + "在吃东西");
    }
    public void map() {
        System.out.println(this.name + "在喵喵叫");
    }
}
public class Cat {
    public String name;
    public int age;
    public void eat() {
        System.out.println(this.name + "在吃东西");
    }
    public void wap() {
        System.out.println(this.name + "在汪汪叫");
    }
}

通过观察会发现上面的dog类和cat类里面有一些共有的成员变量和方法,这时候我们就在想能不能把这些元素放到同一个类里面被其他需要的类使用呢?

这时候就想到了继承,继承就可以实现这一需求,这时我们再创建一个类:

public class Test {
    public String name;
    public int age;
    public void eat() {
        System.out.println(this.name + "在吃东西");
    }
}

此时我们就可以通过关键字extends来实现将Test类里面的成员用到dog和cat类中。

而通过继承可以减少代码的复用。

1.3 继承的语法

修饰符 class 子类 extends 父类 { }

上面的cat和dog类就可以通过extends关键字实现继承,如下图:

public class Dog extends Test {
    public void map() {
        System.out.println(this.name + "在喵喵叫");
    }
}
public class Cat extends Test {
    public void wap() {
        System.out.println(this.name + "在汪汪叫");
    }
}

此时dog类和cat类就包含了父类里面的成员,在内存里也就是:

Java 继承和多态的作用及好处

1.4 子类访问父类里面的成员变量

1. 子类和父类里面的成员变量不同名 

这时候可以直接访问:

//父类
public class Base {
    int a;
    int b;
}
//子类
public class Derived extends Base {
    int c;
    public void test() {
        a = 10;
        b = 20;
        c = 30;
    }
}

子类可以直接使用this访问子类和父类中的成员变量。

2. 子类和父类中存在成员变量同名

//父类
public class Base {
    int a;
    int b;
}
//子类
public class Derived extends Base {
    int c;
    int a;
    phppublic void test() {
        a = 10;
        b = 20;
        c = 30;
    }
}

此时父类和子类都有成员变量a,此时子类会优先访问子类的变量。

  • 当子类和父类有相同名字的成员变量时候时候,会优先访问子类的成员变量。
  • 若子类和父类都没有的成员变量访问则会编译错误。

1.5 子类访问父类里面的成员方法 

1. 子类和父类方法名字不同。

则子类直接访问访问父类的方法。并且子类和父类的方法可以构成方法的重载,如下面代码:

public class Base {
    public void sit() {
        System.out.println("hehe");
    }
}
public class Derived extends Base {
    public void print() {
        System.out.println("haha");
    }
    public void sit(int a) {
        System.out.println("hehe");
    }
    public void test() {
        print();//调用子类的方法
        //构成重载
        sit();//调用父类的方法
        sit(10);//调用子类的方法
    }
}

当子类和父类的方法相同时,则子类调用父类时,则会就近原则优先调用子类。代码如下:

public class Base {
    public void sit() {
        System.out.println("hehe");
    }
}
public class Derived extends Base {
    public void print() {
        System.out.println("haha");
    }
    public void sit() {
        System.out.println("haha");
    }
    public void test() {
        print();//调用子类的方法
        sit();//子类和父类都有该方法会优先调用子类的方法,会打印haha。
    }
}

1.6 super关键字

上面我们发现子类和父类当中如果出现相同的成员变量或者成员方法时,子类调用该成员时会优先调用子类的成员,那如何调用父类的成员呢?

这里就要用到super关键字了,super关键字就是用来在子类中调用父类的成员的,当子类和父类有相同的成员时,就使用super关键字区分。

super关键字用于子类调用父类成员变量:

下面代码可以发现当子类和父类都有同一个成员变量a时,就可以通过super关键字来调用父类的成员变量a,当然this既可以调用子类里面的成员变量,又可以调用父类的成员变量,只有子类和父类有相同名字成员变量时,才用super加以区分。

public class Base {
    int a;
    int b;
}
public class Derived extends Base {
    int a;
    int c;
    public void test1() {
        this.a = 10;//this调用的父类的成员变量。
        super.a = 20;//super调用的子类的成员变量。
        this.b = 40;//this调用的子类的成员变量。
    }
}

在堆区里是这样的:

Java 继承和多态的作用及好处

super关键字用于子类调用父类成员方法:

这里和成员变量一样,用super关键字来区分相同的成员方法,这里的成员方法支持方法的重载:

public class Base {
    public void print() {
        System.out.println("nihao");
    }
    public void sit() {
        System.out.println("hehe");
    }
}
public class Derived extends Base {
    public void print() {
        System.out.println("buhao");
    }
    public void sit(int a) {
        System.out.println("hehe");
    }
    public void test() {
        this.print();//调用子类的方法
        super.print();//调用父类的方法
        //构成重载
        this.sit();//调用父类的方法
        this.sit(10);//调用子类的方法
    }
}

 super关键字用于调用构造方法:

当父类有构造方法的时候,子类也必须有构造方法,因为子类要通过super关键字先调用父类的构造方法,再设置自己的成员方法,并且通过super(父类的构造方法名)的形式调用,并且必须放在构造方法的第一行。所以super和this不能共用,代码如下:

public class Base {
    int a;
    int b;
    //父类的构造方法
    public Base(int a, int b) {
        this.a = a;
        this.b = b;
    }
}
public class Derived extends Base {
    int c;
    int d;
    //子类的构造方法
    public Derived(int a, int b, int c, int d) {
        super(a,b);//第一行先调用父类的构造方法
        this.c = c;
        this.d = d;
    }
}

当子类和父类都没有构造方法是,Java中提供了隐藏的构造方法在子类和父类中。

当父类中定义了无参的 构造方法时,子类的构造方法第一行不写调用父类的代码时,Java也会隐藏的在第一行写上该代码,但如果是有参的,则需要自己书写。

1.7 this和super

相同:

  • 都不能在静态方法中使用。
  • 都是关键字。
  • 在构造方法中调用时必须是第一条语句。

不同:

  • this是对当前对象的成员调用,而super是对从父类中继承下来的成员的调用。
  • 在构造方法中,this用于对本类构造方法的访问,而super是对父类的构造方法的访问。两者不能同时存在。

1.8 继承关系上的调用顺序

我们之前说过,在初始化成员变量时,在实例化一个类时,调用优先级是:静态代码块>实例代码块>构造方法。

而在同时调用一个继承父类的子类和该父类时,调用优先级是:父类的静态代码块优于子类的静态代码块,父类的实例和构造优于子类的实例和构造,而在第二次实例化对象时,静态初始化不再调用,因为类只加载一次。

1.8 protected

我们知道protected是访问限定符,而它修饰的变量使用的范围是同一包下的都可以访问,再加上不同包下的子类可以访问,代码如下:

这里变量d是用protected修饰的在demo2包里,而在demo3里面继承Base,可以调用d变量。

package demo2;
public class Base {
    protected int d;
}
package demo3;
import demo2.Base;
public class Derived extends Base {
    public void test() {
        super.d = 20;
    }
}

1.9 继承方式

Java里面提供了以下继承方式:

单继承:就是A 继承 B。

多层继承:A 继承 B ,B 继承 C

不同类继承同一个类,A 继承 C,B 继承 C;

但是不支持一个类继承多个类:A 继承 B, A 继承 C;这种是错误的继承方式。

1.10 final关键字

finai关键字有三种用法:

1. 密封类 当一个类被final修饰后,则该类不能再被其他的类继承了。

2.修饰变量:final修饰变量后该变量就变成常量了。

3.密封成员方法:表示该方法不能被重写。

1.11 组合

        组合就是说对象之间是包含关系,它可以把类与类之间分开成独立的个体,需要的时候相互调用,不同于继承的思想,当父类改变时一定会影响子类,相当于汽车这个对象是由轮胎,发动机等对象组成的,而汽车这个类里面可以调用轮胎和发动机类里面的一些属性和方法来使用,如果这时加入一个电池类,但是油车没有电池,电车才有,如果是继承关系那么油车就不能继承Car这个类了,但如果是组合思想,电车就可以单独调用这个电池类。

class Tire {
    //轮胎类
}
class Engine {
    //发动机类
}
public class Car {
    public Tire tire;
    public Engine engine;
}

2. 多态

2.1 方法的重写

       方法的重写是在继承的条件下,子类对父类的方法进行重写,子类重写的方法,返回值类型,方法名,参数都完全相同,并且子类的访问修饰符的范围要大于等于父类的访问修饰符的范围。

      当然参数之间也可以不用相同,参数之间构成父子类关系也可以实现方法的重写,这种叫做协变类型。

可以使用@Override来判断该方法是否重写,如果重写错误,还会显示报错。

private修饰的方法,构造方法,静态方法,final修饰的方法等都能重写。

下面代码中,Cat继承Animal,子类对父类的eat方法进行了重写。

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

 2.2 向上转型

向上转型就是父类的引用指向子类的对象。下面代码父类Animal引用指向子类Cat对象。

Animal animal = new Cat();
animal.eat();

这时候父类的引用可以调用父类的成员,但是只能调用子类重写父类的方法子类的其他成员都不能调用。

常见的向上转型的方式:

  • 直接赋值
  • 参数传递
  • 返回值传递

2.3 动态绑定和静态绑定

        动态绑定就是,在父类的引用调用重写的方法时,编译过程调用的是父类的方法,但在运行时绑定的却是子类的方法,最后运行结果调用的是子类的方法。

        这是因为在Java虚拟机中存在一块空间为方法区,这里的每个方法都绑定一个地址,在运行时父类方法绑定的地址变为子类方法绑定的地址,所以调用的子类的方法。

动态绑定的条件 :

  • 发生向上转型
  • 存在方法的重写
  • 父类引用调用了重写的方法

静态绑定是在编译时候绑定的,比如说方法的重载,根据参数来选择调用的方法。

2.4 多态

多态就是同一种类型的引用调用同一种方法,该引用所指的对象不同,得到的结果也不同。

下面代码中,父类的引用分别指向子类Bird和子类Cat,都调用eat方法,产生的结果也不相同,这就是多态。

pandroidublic class Animal{
    public String name;
    public int age;
    public Animal() {
    }
    public void eat() {
        System.out.println("正在吃食物");
    }
}
public class Cat extends Animal{
    public Cat() {
        super();
    }
    @Override
    public void eat() {
        System.out.println("正在吃猫粮");
    }
}
public class Bird exteends Animal {
    public Bird() {
        super();
    }
    @Override
    public void eat() {
        System.out.println("正在吃鸟粮");
    }
    public void fly() {
        System.out.println("正在飞");
    }
}
public class Test {
    public static void main(String[] args) {
        Animal animal1 = new Cat();
        animal1.eat();
        Animal animal2 = new Bird();
        animal2.eat();
    }
}

结果:

Java 继承和多态的作用及好处

 多态产生的条件:

  • 子类对父类的方法进行重写
  • 继承的条件下
  • 通过父类的引用调用子类的重写的方法。

2.5 向下转型

向下转型就是说将父类引用的类型强制类型转换为子类的类型。

    Animal animal = new Cat();
    Cat cat = (Cat)animal;

向下转型后,就可以调用子类的成员。

        但是如果父类引用所指的对象的类型,与强制类型转换的类型不一样就会编译报错,产生ClassCastExceprion(类型转换异常),所以向上转型也不太安全,所以不经常使用。

为了解决这种安全性,Java引入了关键字 instanceof 用来判断强android制类型转换是否安全。

        Animal animal = new Cat();
        if(animal instanceof Cat) {
            Cphpat cat = (Cat)animal;
        }else {
            System.out.println("不安全");
        }

2.6 圈复杂度

圈复杂度是用来描述代码的复杂程度的,一般跟条件语句和循环语句的数量有关,而多态就可以很好的解决圈复杂度过大的问题。

假设要打印形状,就可以利用多态来实现:

public class Shape {
    public Shape() {
    }
    public void print() {
        System.out.println("画一个图案");
    }
}
public class Rect extends Shape {
    public Rect() {
        super();
    }
    public void print() {
        System.out.println("画一个矩形");
    }
}
public class Cycle extends Shape {
    public Cycle() {
        super();
    }
    public void print() {
        System.out.println("画一个圆形");
    }
}
public class Test {
    public static void drawShapes(Shape shape) {
        shape.print();
    }
    public static void main(String[] args) {
        Rect rect = new Rect();
        Cycle cycle = new Cycle();
        Shape[] shapes = {rect,cycle,rect,cycle};
        for(Shape shape : shapes) {
            drawShapes(shape);
        }
    }
}

打印结果如下:

Java 继承和多态的作用及好处

利用多态实现的代码的圈复杂度相对来说就很低。

注意:尽量不要在构造方法中调用重写的方法,构造方法也支持动态绑定。减少使ZzvoUpBtRE用实例化的方法。否则可能会出现问题。可以使用finlal和private修饰的方法,它们不支持方法的重写。 

到此这篇关于Java 继承和多态的作用及好处的文章就介绍到这了,更多相关Java 继承和多态内容请搜索China编程(www.chinasem.cn)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程China编程(www.chinasem.cn)!

这篇关于Java 继承和多态的作用及好处的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1155182

相关文章

SpringBoot中4种数据水平分片策略

《SpringBoot中4种数据水平分片策略》数据水平分片作为一种水平扩展策略,通过将数据分散到多个物理节点上,有效解决了存储容量和性能瓶颈问题,下面小编就来和大家分享4种数据分片策略吧... 目录一、前言二、哈希分片2.1 原理2.2 SpringBoot实现2.3 优缺点分析2.4 适用场景三、范围分片

Java实现自定义table宽高的示例代码

《Java实现自定义table宽高的示例代码》在桌面应用、管理系统乃至报表工具中,表格(JTable)作为最常用的数据展示组件,不仅承载对数据的增删改查,还需要配合布局与视觉需求,而JavaSwing... 目录一、项目背景详细介绍二、项目需求详细介绍三、相关技术详细介绍四、实现思路详细介绍五、完整实现代码

一文详解Java Stream的sorted自定义排序

《一文详解JavaStream的sorted自定义排序》Javastream中的sorted方法是用于对流中的元素进行排序的方法,它可以接受一个comparator参数,用于指定排序规则,sorte... 目录一、sorted 操作的基础原理二、自定义排序的实现方式1. Comparator 接口的 Lam

SpringBoot开发中十大常见陷阱深度解析与避坑指南

《SpringBoot开发中十大常见陷阱深度解析与避坑指南》在SpringBoot的开发过程中,即使是经验丰富的开发者也难免会遇到各种棘手的问题,本文将针对SpringBoot开发中十大常见的“坑... 目录引言一、配置总出错?是不是同时用了.properties和.yml?二、换个位置配置就失效?搞清楚加

Java Stream的distinct去重原理分析

《JavaStream的distinct去重原理分析》Javastream中的distinct方法用于去除流中的重复元素,它返回一个包含过滤后唯一元素的新流,该方法会根据元素的hashcode和eq... 目录一、distinct 的基础用法与核心特性二、distinct 的底层实现原理1. 顺序流中的去重

SpringBoot集成LiteFlow工作流引擎的完整指南

《SpringBoot集成LiteFlow工作流引擎的完整指南》LiteFlow作为一款国产轻量级规则引擎/流程引擎,以其零学习成本、高可扩展性和极致性能成为微服务架构下的理想选择,本文将详细讲解Sp... 目录一、LiteFlow核心优势二、SpringBoot集成实战三、高级特性应用1. 异步并行执行2

python如何调用java的jar包

《python如何调用java的jar包》这篇文章主要为大家详细介绍了python如何调用java的jar包,文中的示例代码简洁易懂,具有一定的借鉴价值,有需要的小伙伴可以参考一下... 目录一、安装包二、使用步骤三、代码演示四、自己写一个jar包五、打包步骤六、方法补充一、安装包pip3 install

怎么用idea创建一个SpringBoot项目

《怎么用idea创建一个SpringBoot项目》本文介绍了在IDEA中创建SpringBoot项目的步骤,包括环境准备(JDK1.8+、Maven3.2.5+)、使用SpringInitializr... 目录如何在idea中创建一个SpringBoot项目环境准备1.1打开IDEA,点击New新建一个项

Java Web实现类似Excel表格锁定功能实战教程

《JavaWeb实现类似Excel表格锁定功能实战教程》本文将详细介绍通过创建特定div元素并利用CSS布局和JavaScript事件监听来实现类似Excel的锁定行和列效果的方法,感兴趣的朋友跟随... 目录1. 模拟Excel表格锁定功能2. 创建3个div元素实现表格锁定2.1 div元素布局设计2.

Spring如何使用注解@DependsOn控制Bean加载顺序

《Spring如何使用注解@DependsOn控制Bean加载顺序》:本文主要介绍Spring如何使用注解@DependsOn控制Bean加载顺序,具有很好的参考价值,希望对大家有所帮助,如有错误... 目录1.javascript 前言2. 代码实现总结1. 前言默认情况下,Spring加载Bean的顺