Java中的继承

文章目录

  • 前言
  • 一、为什么需要继承
  • 二、继承的概念
  • 三、继承的语法
  • 四、父类成员访问
    • 4.1子类中访问父类的成员变量
      • 1.子类和父类不存在同名成员变量
      • 2.子类和父类成员变量同名
    • 4.2子类中访问父类的成员方法
      • 1.成员方法名字不同
      • 2.成员方法,名字相同
  • 五、super和this关键字
  • 六、子类构造方法
  • 七、代码块的执行顺序
  • 八、继承方式
  • 九、final关键字
  • 十、继承和组合


前言

在前面的学习中我们已经讲解了面向对象的三大特性中的封装,接下来我们再来讲解三大特性中的继承


一、为什么需要继承

class Dog {
    String name;
    int age;
    String color;
    public void eat() {
        System.out.println("正在吃");
    }
    public void bark() {
        System.out.println("正在汪汪叫");
    }
}
class Cat {
    String name;
    int age;
    public void eat() {
        System.out.println("正在吃");
    }
    public void sleep() {
        System.out.println("正在睡觉");
    }
}

通过观察以上代码,我们发现在狗和猫这两个类中我们定义了一些重复的代码,那么我们就可以使用继承来进行共性的抽取,实现代码的复用

二、继承的概念

继承(inheritance)机制:是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加新功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构, 体现了由简单到复杂的认知过程。继承主要解决的问题是:共性的抽取,实现代码复用
从继承概念中可以看出继承最大的作用就是:实现代码复用,还有就是来实现多态(后序讲)

三、继承的语法

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

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

子类也可以叫做派生类,父类也可以叫做基类和超类
用继承重新设计狗和猫这两个类

class Animal {
    String name;
    int age;
    public void eat() {
        System.out.println(this.name+"正在吃");
    }
}
class Dog extends Animal {
    String color;
    public void bark() {
        System.out.println(this.name+"正在汪汪叫");
    }
}
class Cat extends Animal {
    public void sleep() {
        System.out.println(this.name+"正在睡觉");
    }
}
public class Test {
    public static void main(String[] args) {
        Dog dog = new Dog();
        //dog类中并没有定义name和age,name和age属性肯定是从父类Animal中继承下来的
        System.out.println("名字:"+dog.name+" 年龄:"+dog.age+" 颜色:"+dog.color);
        //eat方法也是从父类Animal中继承下来的
        dog.eat();
        dog.bark();
        Cat cat = new Cat();
        //cat类中也没有定义任何成员变量,name和age属性肯定是从父类Animal中继承下来的
        System.out.println("名字:"+cat.name+" 年龄:"+cat.age);
        //这里的eat方法也是从父类Animal中继承下来的
        cat.eat();
        cat.sleep();
    }
}

注意:
1.子类会将父类中的成员变量或者成员方法继承到子类中了
2.子类继承父类之后,必须要新添加自己特有的成员,体现出与基类的不同,否则就没有必要继承了

四、父类成员访问

4.1子类中访问父类的成员变量

1.子类和父类不存在同名成员变量

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

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

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

在子类方法中 或者 通过子类对象访问成员时:
1.如果访问的成员变量子类中有,优先访问自己的成员变量
2.如果访问的成员变量子类中无,则访问父类继承下来的,如果父类也没有定义,则编译报错
3.如果访问的成员变量与父类中成员变量同名,则优先访问自己的
成员变量访问遵循就近原则,自己有优先自己的,如果没有则向父类中找

4.2子类中访问父类的成员方法

1.成员方法名字不同

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

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

2.成员方法,名字相同

class Base {
    public void methodA(){
        System.out.println("Base中的methodA()");
    }
    public void methodB(){
        System.out.println("Base中的methodB()");
    }
}
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(),基类的无法访问到
    }
}

说明:
1.通过子类对象访问父类与子类中不同名方法时,优先在子类中找,找到则访问,否则在父类中找,找到则访问,否则编译报错。
2.通过派生类对象访问父类与子类同名方法时,如果父类和子类同名方法的参数列表不同(重载),根据调用方法适传递的参数选择合适的方法访问,如果没有则报错;

五、super和this关键字

super关键字,该关键字主要作用:在子类方法中访问父类的成员

class Base {
    public int a = 1;
    public int b = 2;
}
class Derived extends Base {
    public int a = 10;
    public int c = 20;
    public void print() {
        System.out.println(super.a);
        System.out.println(this.a);
        System.out.println(this.b);
        System.out.println(super.b);
        System.out.println(this.c);
    }
}
public class Test {
    public static void main(String[] args) {
        Derived derived = new Derived();
        derived.print();
    }
}

在这里插入图片描述
在这里插入图片描述
优先访问子类的,子类没有再访问父类
在这里插入图片描述
注意:
1.只能在非静态方法中使用
2.在子类方法中,访问父类的成员变量和方法
相同点:
1.都是Java中的关键字
2.只能在类的非静态方法中使用,用来访问非静态成员方法和字段
3.在构造方法中调用时,必须是构造方法中的第一条语句,并且不能同时存在
不同点:
1.this是当前对象的引用,当前对象即调用实例方法的对象,super相当于是子类对象中从父类继承下来部分成员的引用
2.在非静态成员方法中,this用来访问本类的方法和属性,super用来访问父类继承下来的方法和属性
3.在构造方法中:this(…)用于调用本类构造方法,super(…)用于调用父类构造方法,两种调用不能同时在构造方法中出现
4.构造方法中一定会存在super(…)的调用,用户没有写编译器也会增加,但是this(…)用户不写则没有

六、子类构造方法

父子父子,先有父再有子,即:子类对象构造时,需要先调用基类构造方法,然后执行子类的构造方法

class Base {
    public Base() {
        System.out.println("Base::test()");
    }
}
class Derived extends Base {
    public Derived() {
        System.out.println("Derived::test()");
    }
}
public class Test {
    public static void main(String[] args) {
        Derived derived = new Derived();
    }
}

在这里插入图片描述
在子类构造方法中,并没有写任何关于基类构造的代码,但是在构造子类对象时,先执行基类的构造方法,然后执行子类的构造方法,因为:子类对象中成员是有两部分组成的,基类继承下来的以及子类新增加的部分 。父子父子肯定是先有父再有子,所以在构造子类对象时候 ,先要调用基类的构造方法,将从基类继承下来的成员构造完整,然后再调用子类自己的构造方法,将子类自己新增加的成员初始化完整
注意:
1.若父类显式定义无参或者默认的构造方法,在子类构造方法第一行默认有隐含的super()调用,即调用基类构造方法
2.如果父类构造方法是带有参数的,此时需要用户为子类显式定义构造方法,并在子类构造方法中选择合适的父类构造方法调用,否则编译失败。
3.在子类构造方法中,super(…)调用父类构造时,必须是子类构造函数中第一条语句。
4.super(…)只能在子类构造方法中出现一次,并且不能和this同时出现

七、代码块的执行顺序

在没有继承关系时的执行顺序

class Person {
    public Person() {
        System.out.println("构造方法");
    }
    static {
        System.out.println("静态方法");
    }
    {
        System.out.println("实例方法");
    }
}
public class Test {
    public static void main(String[] args) {
        Person person1 = new Person();
        System.out.println("********");
        Person person2 = new Person();
    }
}

在这里插入图片描述
1.静态代码块先执行,并且只执行一次,在类加载阶段执行
2.当有对象创建时,才会执行实例代码块,实例代码块执行完成后,最后构造方法执行
有继承关系时的执行顺序

class Person {
    public Person() {
        System.out.println("Person:构造方法");
    }
    static {
        System.out.println("Person:静态方法");
    }
    {
        System.out.println("Person:实例方法");
    }
}
class Student extends Person {
    public Student() {
        System.out.println("Student:构造方法");
    }
    static {
        System.out.println("Student:静态方法");
    }
    {
        System.out.println("Student:实例方法");
    }
}
public class Test {
    public static void main(String[] args) {
        Student student1 = new Student();
        System.out.println("********");
        Student student2 = new Student();
    }
}    

在这里插入图片描述
结论如下:
1、父类静态代码块优先于子类静态代码块执行,且是最早执行
2、父类实例代码块和父类构造方法紧接着执行
3、子类的实例代码块和子类构造方法紧接着再执行
4、第二次实例化子类对象时,父类和子类的静态代码块都将不会再执行

八、继承方式

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

class A {
    ......
}
class B extends A {
    ......
}

多层继承

class A {
    ......
}
class B extends A {
    ...... 
}
class C extends B {
    ......
}

不同类继承同一个类

class A {
    ...... 
}
class B extends A {
    ......
}
class C extends A {
    ......
}

注意:Java中不支持多继承
时刻牢记, 我们写的类是现实事物的抽象. 而我们真正在公司中所遇到的项目往往业务比较复杂, 可能会涉及到一系列复杂的概念, 都需要我们使用代码来表示, 所以我们真实项目中所写的类也会有很多. 类之间的关系也会更加复杂.
但是即使如此, 我们并不希望类之间的继承层次太复杂. 一般我们不希望出现超过三层的继承关系. 如果继承层次太多, 就需要考虑对代码进行重构了.
如果想从语法上进行限制继承, 就可以使用 final 关键字

九、final关键字

final关键可以用来修饰变量、成员方法以及类
1.修饰变量或字段,表示常量(即不能修改)
2.修饰类:表示此类不能被继承
String字符串类,就是用final修饰的,不能被继承
3.修饰方法:表示该方法不能被重写(后序介绍)

十、继承和组合

和继承类似, 组合也是一种表达类之间关系的方式, 也是能够达到代码重用的效果。组合并没有涉及到特殊的语法(诸如 extends 这样的关键字), 仅仅是将一个类的实例作为另外一个类的字段。简单说组合其实是一种思想
继承表示对象之间是is-a的关系,比如:狗是动物,猫是动物
组合表示对象之间是has-a的关系,比如:汽车

class Tire{
// ...
} /
/ 发动机类
class Engine{
// ...
} /
/ 车载系统类
class VehicleSystem{
// ...
}
class Car{
    private Tire tire; // 可以复用轮胎中的属性和方法
    private Engine engine; // 可以复用发动机中的属性和方法
    private VehicleSystem vs; // 可以复用车载系统中的属性和方法
    // ...
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

场景图形管理 - (2)

裁剪平面示例(二) 裁剪平面(osg::Scissor)示例(二)的代码如程序清单8-2所示 // 裁剪平面测试&#xff08;2&#xff09; void scissor_8_2(const string strDataFolder) { osg::ref_ptr<osgViewer::Viewer> viewer new osgViewer::Viewer(); osg::ref_ptr<osg::Gra…

FPGA电平标准的介绍

对FPGA的管脚进行约束的时候&#xff0c;常常看到这样的电平标准&#xff0c;例如LVCOM18&#xff0c;LVCOS25&#xff0c;LVDS&#xff0c;LVDS25等等&#xff0c;其实这些都是一系列的电平标准。 针对数字电路而言&#xff0c;数字电路表示电平的只有1和0两个状态&#xff0c…

11.15 知识总结(模板层、模型层)

一、 模板层 1.1 过滤器 1.什么是过滤器&#xff1f; 过滤器类似于python的内置函数&#xff0c;用来把变量值加以修饰后再显示。 2. 语法 1、 {{ 变量名|过滤器名 }} 2、链式调用&#xff1a;上一个过滤器的结果继续被下一个过滤器处理 {{ 变量名|过滤器1|过滤器2 }} 3、有的过…

栈与队列:用队列实现栈

目录 题目&#xff1a; 栈和队列的数据模型对比&#xff1a; 思路分析&#xff1a; 代码分析&#xff1a; 一、定义栈 二、初始化栈 三、入栈 四、出栈⭐ 代码解析&#xff1a; 五、获取栈顶元素 六、 判断栈是否为空 七、销毁栈 完整代码&#xff1a; 需…

在webstorm中配置sass编译环境

1.下载ruby 下载地址&#xff1a;ruby下载 2.安装ruby 下载之后&#xff0c;有一个exe安装包 双击exe文件 &#xff0c;并选择自己的安装位置&#xff08;这个位置一定要记得&#xff0c;需要在webstorm中使用&#xff09;。其他的步骤默认安装即可。 3.安装sass ruby安装成功后…

记feign调用第三方接口时header是multipart/form-data

1.请求第三方接口&#xff0c;用feign请求 请求第三方接口&#xff0c;用feign请求&#xff0c;header不通&#xff0c;feign的写法不同 调用时报错Could not write request: no suitable HttpMessageConverter found for request type [com.ccreate.cnpc.mall.dto.zm.ZMPage…

程序员的护城河:技术深度、创新精神与软实力的完美结合

文章目录 1. 技术深度&#xff1a;建立坚实的技术基石2. 创新精神&#xff1a;应对变革的利器3. 软实力&#xff1a;沟通协作构筑团队防线4. 结合三者构筑完美护城河 &#x1f389;程序员的护城河&#xff1a;技术深度、创新精神与软实力的完美结合 ☆* o(≧▽≦)o *☆嗨~我是I…

【Maven】进阶

文章目录 1. 聚合2. 继承3. 属性变量定义与使用4. 版本管理5. 资源配置6. 多环境配置7. 跳过测试&#xff08;了解&#xff09; 1. 聚合 为了防止某个模块&#xff08;dao&#xff09;更新了&#xff0c;重新编译了&#xff0c;导致和其他模块不兼容&#xff0c;需要用一个roo…

移植freertos到qemu上运行

1、freertos源码下载 参考博客&#xff1a;《freertos源码下载和目录结构分析》&#xff1b; 2、编译freertos 2.1、选择合适的Demo freertos官方已经适配过qemu&#xff0c;所以我们并不需要做源码级别的移植&#xff0c;只需要选择合适的Demo文件夹。 2.2、修改Makefile 2.3…

sCrypt 发布零知识证明精选列表

sCrypt 发布了与零知识证明相关的精选列表&#xff0c;包括&#xff1a;教程&#xff0c;编程语言&#xff0c;工具&#xff0c;书籍&#xff0c;社区&#xff0c;证明系统。欢迎收藏 github 代码仓&#xff1a;https://github.com/sCrypt-Inc/awesome-zero-knowledge-proofs。…

spark与scala的对应版本查看

仓库地址 https://mvnrepository.com/artifact/org.apache.spark/spark-core 总结 spark3.0 以后&#xff0c;不再支持 scala2.11spark3.0 以后&#xff0c;只能用 scala2.12以上

Zabbix邮箱告警

1.在邮箱中获取授权码 2.zabbix配置 agengt配置 添加以下配置 [rootserver03 ~]# visudo zabbix ALL(ALL) NOPASSWD: ALL [rootserver03 ~]# vim /etc/zabbix/zabbix_agentd.conf EnableRemoteCommands1 #允许接收远程命令 修改原有的值&#xff0c;不要在末…

Windows系统CMake+VS编译protobuf

目录 一些名词CMake构建VS工程下载protobuf源码下载CMake编译QT中使用 方案二失败&#xff1a;CMakeQT自带的Mingw编译参考链接 一些名词 lib dll lib库实际上分为两种&#xff0c;一种是静态链接lib库或者叫做静态lib库&#xff0c;另一种叫做动态链接库dll库的lib导入库或称…

.Net8 Blazor 尝鲜

全栈 Web UI 随着 .NET 8 的发布&#xff0c;Blazor 已成为全堆栈 Web UI 框架&#xff0c;可用于开发在组件或页面级别呈现内容的应用&#xff0c;其中包含&#xff1a; 用于生成静态 HTML 的静态服务器呈现。使用 Blazor Server 托管模型的交互式服务器呈现。使用 Blazor W…

LINMP搭建wordpress-数据库不分离

目录 一、nginx部署 1.安装nginx前的系统依赖环境检查 2.下载nginx源代码包 3.解压缩源码包 4.创建普通的nginx用户 5.开始编译安装nginx服务 6.创建一个软连接以供集中管理 7.配置nginx环境变量 二、mysql 1.创建普通mysql用户 2.下载mysql二进制代码包 3.创建mys…

吴恩达《机器学习》8-5->8-6:特征与直观理解I、样本与值观理解II

8.5、特征与直观理解I 一、神经网络的学习特性 神经网络通过学习可以得出自身的一系列特征。相对于普通的逻辑回归&#xff0c;在使用原始特征 x1​,x2​,...,xn​ 时受到一定的限制。虽然可以使用一些二项式项来组合这些特征&#xff0c;但仍然受到原始特征的限制。在神经网…

【MySQL】MVCC(多版本并发控制)详解

MVCC MVCC概述 MVCC&#xff0c;全称 Multi-Version Concurrency Control &#xff0c;即多版本并发控制。MVCC 是一种并发控制的方法&#xff0c;一般在数据库管理系统中&#xff0c;实现对数据库的并发访问&#xff0c;在编程语言中实现事务内存。 MVCC就是在ReadCommitte…

echarts实现不展示X轴Y轴轴线、刻度

今日工作中需要实现折线图的简图&#xff0c;就是只看个大概趋势不展示具体坐标&#xff0c;查阅了文档记录一下。 initCharts(_id, _name, yAxisData, _unit){if(this[_id]) this[_id].clear();this[_id] $echarts.init(document.getElementById(_id));const options {grid…

基于Springboot+Vue的社区医院管理系统

基于SpringbootVue的社区医院管理系统的设计与实现~ 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringBootMyBatisVue工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 主页 医生列表 医生详情 登录界面 管理员界面 医生界面 摘要 社区医院管…

van-dialog弹窗异步关闭-校验表单

van-dialog弹窗异步关闭 有时候我们需要通过弹窗去处理表单数据&#xff0c;在原生微信小程序配合vant组件中有多种方式实现&#xff0c;其中UI美观度最高的就是通过van-dialog嵌套表单实现。 通常表单涉及到是否必填&#xff0c;在van-dialog的确认事件中直接return是无法阻止…