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

相关文章

Spring Boot项目如何使用外部application.yml配置文件启动JAR包

《SpringBoot项目如何使用外部application.yml配置文件启动JAR包》文章介绍了SpringBoot项目通过指定外部application.yml配置文件启动JAR包的方法,包括... 目录Spring Boot项目中使用外部application.yml配置文件启动JAR包一、基本原理

SpringBoot加载profile全面解析

《SpringBoot加载profile全面解析》SpringBoot的Profile机制通过多配置文件和注解实现环境隔离,支持开发、测试、生产等不同环境的灵活配置切换,无需修改代码,关键点包括配置文... 目录题目详细答案什么是 Profile配置 Profile使用application-{profil

Java中InputStream重复使用问题的几种解决方案

《Java中InputStream重复使用问题的几种解决方案》在Java开发中,InputStream是用于读取字节流的类,在许多场景下,我们可能需要重复读取InputStream中的数据,这篇文章主... 目录前言1. 使用mark()和reset()方法(适用于支持标记的流)2. 将流内容缓存到字节数组

Java慢查询排查与性能调优完整实战指南

《Java慢查询排查与性能调优完整实战指南》Java调优是一个广泛的话题,它涵盖了代码优化、内存管理、并发处理等多个方面,:本文主要介绍Java慢查询排查与性能调优的相关资料,文中通过代码介绍的非... 目录1. 事故全景:从告警到定位1.1 事故时间线1.2 关键指标异常1.3 排查工具链2. 深度剖析:

Springboot项目登录校验功能实现

《Springboot项目登录校验功能实现》本文介绍了Web登录校验的重要性,对比了Cookie、Session和JWT三种会话技术,分析其优缺点,并讲解了过滤器与拦截器的统一拦截方案,推荐使用JWT... 目录引言一、登录校验的基本概念二、HTTP协议的无状态性三、会话跟android踪技术1. Cook

java实现多数据源切换方式

《java实现多数据源切换方式》本文介绍实现多数据源切换的四步方法:导入依赖、配置文件、启动类注解、使用@DS标记mapper和服务层,通过注解实现数据源动态切换,适用于实际开发中的多数据源场景... 目录一、导入依赖二、配置文件三、在启动类上配置四、在需要切换数据源的类上、方法上使用@DS注解结论一、导入

创建springBoot模块没有目录结构的解决方案

《创建springBoot模块没有目录结构的解决方案》2023版IntelliJIDEA创建模块时可能出现目录结构识别错误,导致文件显示异常,解决方法为选择模块后点击确认,重新校准项目结构设置,确保源... 目录创建spChina编程ringBoot模块没有目录结构解决方案总结创建springBoot模块没有目录

Java中的volatile关键字多方面解析

《Java中的volatile关键字多方面解析》volatile用于保证多线程变量可见性与禁止重排序,适用于状态标志、单例模式等场景,但不保证原子性,相较synchronized更轻量,但需谨慎使用以... 目录1. volatile的作用1.1 保证可见性1.2 禁止指令重排序2. volatile的使用

springboot项目中集成shiro+jwt完整实例代码

《springboot项目中集成shiro+jwt完整实例代码》本文详细介绍如何在项目中集成Shiro和JWT,实现用户登录校验、token携带及接口权限管理,涉及自定义Realm、ModularRe... 目录简介目的需要的jar集成过程1.配置shiro2.创建自定义Realm2.1 LoginReal

SpringBoot集成Shiro+JWT(Hutool)完整代码示例

《SpringBoot集成Shiro+JWT(Hutool)完整代码示例》ApacheShiro是一个强大且易用的Java安全框架,提供了认证、授权、加密和会话管理功能,在现代应用开发中,Shiro因... 目录一、背景介绍1.1 为什么使用Shiro?1.2 为什么需要双Token?二、技术栈组成三、环境