Java继承:抽取相同共性,实现代码复用

news2024/11/29 10:36:07

在这里插入图片描述

  • 👑专栏内容:Java
  • ⛪个人主页:子夜的星的主页
  • 💕座右铭:前路未远,步履不停

目录

  • 一、继承的概念
  • 二、继承的语法
  • 三、父类成员访问
    • 1、子类中访问父类成员变量
      • Ⅰ、子类和父类不存在同名成员变量
      • Ⅱ、子类和父类成员变量同名
    • 2、子类中访问父类的成员方法
      • Ⅰ、成员方法名字不同
      • Ⅱ、成员方法名字相同
    • 3、`super`关键字
    • 4、子类构造方法
    • 5、`super`和`this`的区别
  • 四、继承方式
    • 1、 `final`关键字
    • 2、继承与组合


(。・∀・)ノ゙嗨,今天让我们来一起学习一下Java的继承吧。

Java中使用类对现实世界中实体来进行描述,类经过实例化之后的产物对象,则可以用来表示现实中的实体,但是现实世界错综复杂,可能两个对象之间会存在某种关系。继承在编程中也是类似的概念,其中一个类可以继承另一个类的属性和方法,并且可以在此基础上添加或修改。这样可以使代码更加模块化、可维护,同时提高代码的复用性。
在这里插入图片描述

一、继承的概念

假设我们现在有两个类,一个是猫类,另外一个是狗类。

// Dog.java
public class Dog{
    string name;
    int age;
    float weight;
    public void eat(){
    	System.out.println(name + "正在吃饭");
    }
    public void sleep(){
    	System.out.println(name + "正在睡觉");
    }
    void Bark(){
    	System.out.println(name + "汪汪汪~~~");
    }
}
// Cat.java
public class Cat{
    string name;
    int age;
    float weight;
    public void eat(){
   		System.out.println(name + "正在吃饭");
    }
    public void sleep()
    {
    	System.out.println(name + "正在睡觉");
    }
    void mew(){
    	System.out.println(name + "喵喵喵~~~");
    }
}

通过观察上述代码会发现,猫和狗的类中存在大量重复,如下所示:

image-20230510003440174
那能否将这些共性抽取呢?既然狗和猫都是动物,那么我们就可以将共性的内容进行抽取,然后采用继承的思想来达到共用。
image-20230510004236552
上述图示中,DogCat都继承了Animal类,其中:Animal类称为父类/基类或超类,DogCat可以称为Animal的子类/派生类,继承之后,子类可以复用父类中成员,子类在实现时只需关心自己新增加的成员即可。
在这里插入图片描述
从上面可以看出,继承概念中可以看出继承最大的作用就是:共性的抽取,实现代码复用

二、继承的语法

在Java中如果要表示类之间的继承关系,需要借助extends关键字,具体如下:

修饰符 class 子类 extends 父类 {
// ... 
}

图片描述

让我们对上面的常见用继承重新设计一下吧。
首先,我们可以创建一个父类叫做动物类。

// nimal.java
public class Animal {
    String name; // 动物的名字
    int age; // 动物的年龄
    
    // 动物吃饭的方法
    public void eat() {
        System.out.println(name + "正在吃饭");
    }

    // 动物睡觉的方法
    public void sleep() {
        System.out.println(name + "正在睡觉");
    }
}

然后,让猫类和狗类分别继承动物类。

// Dog.java
public class Dog extends Animal { // Dog类继承自Animal类
    // 狗叫的方法
    void bark() {
        System.out.println(name + "汪汪汪~~~");
    }
}

// Cat.Java
public class Cat extends Animal { // Cat类继承自Animal类
    // 猫叫的方法
    void mew() {
        System.out.println(name + "喵喵喵~~~");
    }
}

在这里插入图片描述
这里,dog类和Cat类中并没有定义任何成员变量,nameage属性肯定是从父类Animal中继承下来的,其使用的eat()sleep()方法也是从Animal中继承下来的。

注意:

  • 子类会将父类中的成员变量或者成员方法继承到子类中了

  • 子类继承父类之后,必须要新添加自己特有的成员,否则就没有必要继承了

三、父类成员访问

在继承体系中,子类将父类中的方法和字段继承下来了,那在子类中能否直接访问父类中继承下来的成员呢?

1、子类中访问父类成员变量

Ⅰ、子类和父类不存在同名成员变量

public class Base {
    int a;
    int b;
}
/
public class Derived extends Base{
    int c;
    public void method(){
        a = 10; // 访问从父类中继承下来的a
        b = 20; // 访问从父类中继承下来的b
        c = 30; // 访问子类自己的c
    }
}

Ⅱ、子类和父类成员变量同名

public class Base {
    int a;
    int b;
    int c;
}
/
public class Derived extends Base{
    int a; // 与父类中成员a同名,且类型相同
    char b; // 与父类中成员b同名,但类型不同
	public void method(){
        a = 100; // 访问父类继承的a,还是子类自己新增的a?
        b = 101; // 访问父类继承的b,还是子类自己新增的b?
        c = 102; // 子类没有c,访问的肯定是从父类继承下来的c
        // d = 103; // 编译失败,因为父类和子类都没有定义成员变量b
    }
}

在子类方法中 或者 通过子类对象访问成员时

  • 如果访问的成员变量子类中有,优先访问自己的成员变量。

  • 如果访问的成员变量子类中无,则访问父类继承下来的,如果父类也没有定义,则编译报错。

  • 如果访问的成员变量与父类中成员变量同名,则优先访问自己的。

成员变量访问遵循就近原则,自己有优先自己的,如果没有则向父类中找

2、子类中访问父类的成员方法

Ⅰ、成员方法名字不同

public class Base {
    public void methodA(){
        System.out.println("Base中的methodA()");
    }
}
/
public class Derived extends Base{
    public void methodB(){
        System.out.println("Derived中的methodB()方法");
    }
    
    public void methodC(){
        methodB(); // 访问子类自己的methodB()
        methodA(); // 访问父类继承的methodA()
        // methodD(); // 编译失败,在整个继承体系中没有发现方法methodD()
    }
}

总结:成员方法没有同名时,在子类方法中或者通过子类对象访问方法时,则优先访问自己的,自己没有时再到父类中找,如果父类中也没有则报错。

Ⅱ、成员方法名字相同

public class Base {
    public void methodA(){
        System.out.println("Base中的methodA()");
    }
    
    public void methodB(){
        System.out.println("Base中的methodB()");
    }
}
/
public class Derived extends Base{
    public void methodA(int a) {
        System.out.println("Derived中的method(int)方法");
    }
    
    public void methodB(){
        System.out.println("Derived中的methodB()方法");
    }
    
    public void methodC(){
        methodA(); // 没有传参,访问父类中的methodA()
        methodA(20); // 传递int参数,访问子类中的methodA(int)
        methodB(); // 直接访问,则永远访问到的都是子类中的methodB(),基类的无法访问到
    }
}

【说明】

  • 通过子类对象访问父类与子类中不同名方法时,优先在子类中找,找到则访问,否则在父类中找,找到则访问,否则编译报错。

  • 通过派生类对象访问父类与子类同名方法时,如果父类和子类同名方法的参数列表不同(重载),根据调用方法适传递的参数选择合适的方法访问,如果没有则报错。

问题:如果子类中存在与父类中相同的成员时,那如何在子类中访问父类相同名称的成员呢?

在Java中,如果子类中存在与父类中相同名称的成员,那么在子类中默认访问的是子类自己的成员。要在子类中访问父类的同名成员,可以使用super关键字。super关键字用于引用当前子类实例的直接父类。

3、super关键字

由于设计不好,或者因场景需要,子类和父类中可能会存在相同名称的成员,如果要在子类方法中访问父类同名成员时,该如何操作?

直接访问是无法做到的,Java提供了super关键字。该关键字主要作用:在子类方法中访问父类的成员。

public class Base {
    int a; // 基类中的成员变量
    int b; // 基类中的成员变量
    
    public void methodA(){
        System.out.println("Base中的methodA()"); // 打印表明在基类中的语句
    }
    
    public void methodB(){
        System.out.println("Base中的methodB()"); // 打印表明在基类中的语句
    }
}

/

public class Derived extends Base {
    int a; // 使用相同的名称和类型遮蔽了基类中的成员变量'a'
    char b; // 使用相同的名称但不同类型遮蔽了基类中的成员变量'b'
    
    // 重载了基类中的methodA()
    public void methodA(int a) {
        System.out.println("Derived中的method()方法"); // 打印表明在派生类中的语句
    }
    
    // 重写了基类中的methodB()
    public void methodB(){
        System.out.println("Derived中的methodB()方法"); // 打印表明在派生类中的语句
    }
    
    public void methodC(){
        // 对于同名的成员变量,直接访问时,访问的是派生类中的变量
        this.a = 100; // 等价于:this.a = 100;
        this.b = 101; // 等价于:this.b = 101;
        
        // 注意:'this' 是当前对象的引用
        
        // 要访问基类中的成员变量,需要使用'super'关键字
        super.a = 200;
        super.b = 201;
        
        // 在基类和派生类中都存在重载的方法,可以通过参数列表区分访问基类还是派生类的方法
        methodA(); // 没有传参,访问基类中的methodA()
        methodA(20); // 传递int参数,访问派生类中的methodA(int)
        
        // 要访问重写的基类方法,需要使用'super'关键字
        methodB(); // 直接访问,则永远访问派生类中的methodB(),无法访问基类的
        super.methodB(); // 访问基类的methodB()
    }
}

在子类方法中,如果想要明确访问父类中成员时,借助super关键字即可。

注意事项

  • 只能在非静态方法中使用。

  • 在子类方法中,访问父类的成员变量和方法。

4、子类构造方法

在这里插入图片描述
子类对象构造时,需要先调用基类(父类)构造方法,然后执行子类的构造方法。

public class Base {
    public Base(){
        System.out.println("Base()"); // 打印在基类构造方法中的语句
    }
}

public class Derived extends Base{
    public Derived(){
        // super(); 注意:子类构造方法中默认会调用基类的无参构造方法:super()
        // 当用户没有显式写出时,编译器会自动添加super(),并且super()必须是子类构造方法中的第一条语句,
        // 并且只能出现一次
        System.out.println("Derived()"); // 打印在派生类构造方法中的语句
    }
}

public class Test {
    public static void main(String[] args) {
        Derived d = new Derived(); // 创建派生类的实例
    }
}
结果打印:
    Base()
    Derived()

在子类的构造方法中,如果没有显式地写出关于基类构造的代码,那么在构造子类对象时会先执行基类的构造方法,然后再执行子类的构造方法。

这是因为子类对象的成员由两部分组成:继承自基类的部分和子类新增加的部分。

因此,在构造子类对象时,需要先调用基类的构造方法来构造继承下来的成员,然后再调用子类自己的构造方法来初始化子类新增加的成员。

需要注意以下几点:

  1. 如果父类显式地定义了无参或默认的构造方法,那么在子类的构造方法中,默认会隐含地调用super(),即调用基类的构造方法。
  2. 如果父类的构造方法带有参数,那么在子类中就需要显式地定义构造方法,并在子类的构造方法中选择适合的父类构造方法进行调用,否则会导致编译错误。
  3. 在子类的构造方法中,通过super(...)调用父类构造方法时,必须作为子类构造函数中的第一条语句。
  4. super(...)只能在子类的构造方法中出现一次,并且不能与this同时出现。

5、superthis的区别

superthis都可以在成员方法中用来访问:成员变量和调用其他的成员函数,都可以作为构造方法的第一条语句,那他们之间有什么区别呢?

相同点

  • 都是Java中的关键字

  • 只能在类的非静态方法中使用,用来访问非静态成员方法和字段

  • 在构造方法中调用时,必须是构造方法中的第一条语句,并且不能同时存在

不同点

  • this是当前对象的引用,当前对象即调用实例方法的对象,super相当于是子类对象中从父类继承下来部分成员的引用

image-20230511174010010

  • 在非静态成员方法中,this用来访问本类的方法和属性,super用来访问父类继承下来的方法和属性

  • 在构造方法中:this(...)用于调用本类构造方法,super(...)用于调用父类构造方法,两种调用不能同时在构造方法中出现

  • 构造方法中一定会存在super(...)的调用,用户没有写编译器也会增加,但是this(...)用户不写则没有

四、继承方式

在Java中只支持以下几种继承方式:

image-20230512151133518
注意:Java中不支持多继承。

时刻牢记, 我们写的类是现实事物的抽象. 而我们真正在公司中所遇到的项目往往业务比较复杂, 可能会涉及到一系列复杂的概念, 都需要我们使用代码来表示, 所以我们真实项目中所写的类也会有很多,类之间的关系也会更加复杂。

但是即使如此, 我们并不希望类之间的继承层次太复杂。 一般我们不希望出现超过三层的继承关系. 如果继承层次太多, 就需要考虑对代码进行重构了。如果想从语法上进行限制继承, 就可以使用final 关键字。

在这里插入图片描述

1、 final关键字

final关键可以用来修饰变量、成员方法以及类。

final修饰符大多数应用于基本类型域,或不可变类的域。

将方法或者类的声明为final的主要目的是:确保它们不会在子类中改变语义。

  • 修饰变量或字段,表示常量(即不能修改)
final int a = 10;
a = 20; // 编译出错
  • 修饰类:表示此类不能被继承
final public class Animal {
	...
}
public class Bird extends Animal {
	...
}
// 编译出错
Error:(3, 27) java: 无法从最终com.bit.Animal进行继

image-20230512151520650

我们平时是用的String 字符串类, 就是用 final修饰的, 不能被继承

  • 修饰方法:表示该方法不能被重写

2、继承与组合

和继承类似,组合也是一种表达类之间关系的方式,也是能够达到代码重用的效果。组合并没有涉及到特殊的语法(诸如 extends 这样的关键字),仅仅是将一个类的实例作为另外一个类的字段。

组合(或聚合)是另一种关系,表示一个类是由其他类的对象组成的。换句话说,一个类的对象包含了其他类的对象。这种关系通常被描述为“有-a"关系。例如,一辆汽车有一个引擎和四个轮子,因此汽车类可以包含一个引擎对象和四个轮子对象。
image-20230512151945967
组合的主要优点是它提供了更好的封装,更强的代码灵活性,更易于修改和维护。由于类之间的关系不再像继承那样紧密,所以修改一个类的实现不太可能影响到使用该类的其他类。此外,组合也使得代码更加模块化,每个类都专注于一个单一的职责。

然而,组合也有一些缺点。它可能导致更复杂的代码,因为你需要管理和协调多个对象之间的交互。选择使用继承还是组合通常取决于具体的需求和上下文。

一般来说,如果两个类之间存在"is-a"(是一个)的关系,比如“苹果是一种水果”,那么继承可能是最好的选择;如果两个类之间存在"has-a"(有一个)的关系,比如“汽车有一个引擎”,那么组合可能是更好的选择。

更详细的可以看这个大佬的文章:深入理解Java中的组合和继承


继承就先说到这里了,我们下次再见!

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1172084.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

泄漏检测与修复(LDAR)过程管控平台(销售出租)VOCs便携式总烃分析仪(销售出租)

LDAR是Leak Detection and Repair(泄漏检测与修复)的缩写,也是国际上较先进的化工废气检测技术。LDAR主要通过检测化工企业原料输送管道、泵、阀门、法兰等易产生易产生挥发性有机物(简称VOCs)泄漏的部位,并…

turn.js 模版简单使用

turn.js 不修改添加原功能仅 替换、修改图片格式使用模版 HTML文件 turn.js官网:http://www.turnjs.com/# 第一步 1.点击链接去到官网 2.点击下载按钮 下载左侧示例压缩包 3.解压完成拿到示例文件 turnjs4 4.在samples目录下案例中查看意向使用的模版样式 …

OpenGL_Learn05(纹理)

1. 纹理贴图 wall.jpg (512512) (learnopengl-cn.github.io) 纹理过滤分为:邻近和线性,这跟opencv图像处理一样。 多级渐远纹理 四种采样方式: 代码实现: std_image.h https://github.com/nothings/stb/blob/master/stb_image.…

【数据结构】冒泡排序 (码源实现)

冒泡排序 前言一、冒泡排序运行图例二、算法实现基本思路三、算法实现步骤四、算法码源详解五、冒泡排序效率分析(一)时间复杂度——O(N^2)(二)空间复杂度——O(1)(三&am…

【PC电脑windows-学习样例tusb_serial_device-ESP32的USB模拟串口程序+VScode建立工程+usb组件添加+-基础样例学习】

【PC电脑windows-学习样例tusb_serial_device-ESP32的USB模拟串口程序-基础样例学习】 1、概述2、实验环境3-1、 物品说明3-2、所遇问题:ESP32 cannot open source file "tinyusb.h"或者“tinyusb.h:No such file or directory ....”3-3、解决问题&#…

【多线程】静态代理

当使用静态代理模式时,我们会有一个真实的对象(RealSubject),一个代理对象(ProxySubject),代理对象将请求转发给真实对象,并可以在请求前后执行额外的操作。 真实对象和代理对象要实…

【LeetCode刷题-队列】--2073.买票需要的时间

2073.买票需要的时间 方法一&#xff1a;使用队列 class Solution {public int timeRequiredToBuy(int[] tickets, int k) {Queue<TicketBuyer> queue new LinkedList<>();for(int i 0;i<tickets.length;i){TicketBuyer buyer new TicketBuyer();buyer.inde…

Linux----------------Shell重定向输入输出

&#xff08;一&#xff09; 标准输入 以键盘读取用户输入的数据&#xff0c;然后再把数据拿到 Shel程序中使用。 标准输出 Shell 程序产生的数据&#xff0c;这些数据一般都是呈现到显示器上供用户浏览查看 输入输出重定向 输入方向就是数据从哪里流向程序。数据默认从键…

【LeetCode刷题-队列与栈】--225.用队列实现栈

225.用队列实现栈 class MyStack {Queue<Integer> queue1;Queue<Integer> queue2;public MyStack() {queue1 new LinkedList<Integer>();queue2 new LinkedList<Integer>();}public void push(int x) {queue2.offer(x);while(!queue1.isEmpty()){que…

SpringBoot + Vue2项目打包部署到服务器后,使用Nginx配置SSL证书,配置访问HTTP协议转HTTPS协议

配置nginx.conf文件&#xff0c;这个文件一般在/etc/nginx/...中&#xff0c;由于每个人的体质不一样&#xff0c;也有可能在别的路径里&#xff0c;自己找找... # 配置工作进程的最大连接数 events {worker_connections 1024; }# 配置HTTP服务 http {# 导入mime.types配置文件…

typeScript基础使用与进阶

typeScript基础使用与进阶 一、初始typeScript1.1 js的超集1.2 编译器编译为js代码1.3 完全兼容js1.4 静态类型检查器 二、ts的安装与编译2.1 ts的安装2.2 ts编译成js2.2.1 手动编译2.2.2 自动编译 三、ts基础使用3.1 类型声明3.1.1 基础类型3.1.2 数组3.1.3 对象3.1.4 any类型…

bug: https://aip.baidubce.com/oauth/2.0/token报错blocked by CORS policy

还是跟以前一样&#xff0c;我们先看报错点&#xff1a;&#xff08;注意小编这里是H5解决跨域的&#xff0c;不过解决跨域的原理都差不多&#xff09; Access to XMLHttpRequest at https://aip.baidubce.com/oauth/2.0/token from origin http://localhost:8000 has been blo…

错误号码2058 Plugin caching_sha2_password could not be loaded:vX八白白白白白令自砸

sqlyog连接数据库时报错&#xff1a; 错误号码2058 Plugin caching_sha2_password could not be loaded:vX八白白白白白令自砸 网上查了资料&#xff0c;是MySQL 从 8.0 版本开始加密方式改变导致的原因。具体的咋也不再这里分析了&#xff0c;就直说如何解决这个问题。下面三…

由于找不到vcruntime140.dll无法继续执行代码

在计算机使用过程中&#xff0c;我们可能会遇到一些错误提示&#xff0c;其中之一就是“vcruntime140.dll丢失”。这个错误通常发生在运行某些程序或游戏时&#xff0c;它会导致程序无法正常运行。那么&#xff0c;如何解决vcruntime140.dll丢失的问题呢&#xff1f;本文将介绍…

基于单片机的滚筒洗衣机智能控制系统设计

收藏和点赞&#xff0c;您的关注是我创作的动力 文章目录 概要 一、系统整体设计方案2.1控制系统的功能2.2设计的主要内容 二、硬件设计3.1 控制系统整体框图3.2 电源电路 三 软件设计主程序设计仿真设计 四、 结论 概要 因此我们需要一个完善的智能系统来设计一个全自动滚筒洗…

【unity实战】教你轻松构建对话系统

先来看最终效果 前言 欢迎来到我们的Unity实战教程系列。在这一篇中&#xff0c;我们将深入探讨如何在Unity中构建一个对话系统。 对话系统是许多游戏中不可或缺的一部分&#xff0c;特别是在角色扮演游戏&#xff08;RPG&#xff09;、冒险游戏和视觉小说等类型中。它们为玩…

selenium自动化测试入门 —— python unittest单元测试框架

unittest又名PyUnit&#xff0c; Python单元测试框架&#xff08;The Python unit testing framework&#xff09;&#xff0c;简称为PyUnit。自从 Python 2.1 版本后&#xff0c;PyUnit成为 Python标准库的一部分。 为什么需要使用unittest单元测试框架&#xff1f; 当我们写…

MIT6.5830 Lab1-GoDB实验记录(五)

MIT6.5830 Lab1-GoDB实验记录&#xff08;五&#xff09; – WhiteNights Site 标签&#xff1a;Golang 完成了Exercise 1&#xff0c;还有四个Exercise在等着我&#xff0c;慢慢来吧。 实验准备 了解缓冲池 缓冲池&#xff0c;俗称BP。相关的概念还有数据页和缓存页。页&…

VueX 模块化和namespace

当我们的项目很大的时候&#xff0c;VueX中的代码会越来越多&#xff0c;会有处理数据的&#xff0c;处理人员列表的&#xff0c;处理订单的... 如果我们将这些东西都写在一个state、actions和mutations中的话&#xff0c;就非常不方便后期的维护。 所以我们引入了VueX的模块…