Java继承详解

news2024/11/24 5:58:13

目录

继承

为什么需要继承

继承的概念

继承的语法

父类成员的访问

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

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

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

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

1.成员方法名字不同

2.成员方法名字相同

super关键字

子类构造方法

super和this

再谈初始化

protected关键字

继承方式

final关键字

继承与组合


继承

为什么需要继承

Java中使用类对现实世界中实体进行描述,类经过实例化之后的产物对象,则可以用来表示现实世界的实体,但是现实世界错综复杂,事物之间可能有一些关联,那再设计程序时就需要考虑。

比如:狗和猫,他们都是动物。(每个动物都有共性,可以抽取出来它们的共性)

使用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 + "汪汪汪");
    }
}


//创建一个猫类
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 + "喵喵喵~~~");
    }
}

通过狗类和猫类,我们发现大量代码出现重复,如图所示:

那如何能实现共性抽取呢?面向对象的思想中提出了继承的概念,专门用来进行共性抽取,实现代码复用。 (继承可以看作is-a关系,比如Dog is a animal)

继承的概念

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

例如:狗和猫都是动物,那么我们就可以将共性进行抽取,然后采用继承的思想来达到共用。

上述图示中,Dog和Cat都继承了Animal类,其中:Animal类称为父类/基类/超类,Dog和Cat可以称为Animal的子类/派生类,继承之后,子类可以复用父类中的成员,子类在实现时只需关心自己新增加的成员即可。

从继承概念中可以看出继承的最大作用是:实现代码复用,后面也应用于多态

继承的语法

在java中如果要表示类的继承,需要用到extends关键字,具体如下:

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

对之前的Dog类和Cat类通过继承重新设计:

//Animal.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 {
    void bark() {
        System.out.println(name + "汪汪汪");    
    }
}

//Cat.java
public class Cat extends Animal {
    void mew() {
        System.out.println(name + "喵喵喵");    
    }
}

public class TestExtend {
    public static void main(String[] args) {
        Dog dog = new Dog();
        //dog类中并没有定义任何成员变量,name和age属性肯定是从父类Animal中继承下来的
        System.out.println(dog.name);
        System.out.println(dog.age);
        
        //dog访问的eat和sleep方法也是从Animal中继承下来的
        dog.eat();
        dog.sleep();
        //bark是子类新增加的方法
        dog.bark();
    }
}

注意:

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

2.子类继承父类后,建议添加自己特有的成员,体现出与基类的不同,否则没有继承的必要

父类成员的访问

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

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

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

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
    }
}

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

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
        b = 101;//使用的是子类新增的b
        c = 102;//使用从父类继承下来的c
    }
}

通过上述栗子,我们可以得出以下规律:

1.如果访问的成员变量子类中有,则优先访问自己的成员变量

2.如果访问的成员变量子类中没有,则访问从父类继承下来的,如果父类也没有,则报错

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

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

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

1.成员方法名字不同

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

2.成员方法名字相同

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 中的methodA(int) 方法");
    }

    public void methodB() {
        System.out.println("Derived 中的methodB() 方法");
    }

    public void methodC() {
        methodA();//没有传参,访问父类中的methodA()
        methodA(20);//有传参,访问子类中的methodA(int)
        methodB();//直接访问,则永远访问到的都是子类中的methodB(),永远无法访问到基类的
    }

    public static void main(String[] args) {
        Derived d = new Derived();
        d.methodC();
    }
}

 

 

总结:通过派生类对象访问父类与子类相同名的方法时,如果父类和子类同名方法的参数列表不同,根据调用方法时传递的参数选择合适的方法进行访问,如果没有则报错。

那么有的人会问,如果成员的访问遵循就近原则,那么如果想访问父类中同名的成员应该怎么办?

这就需要super关键字

super关键字

使用场景:子类和父类中可能存在相同名称的成员,需要在子类方法中访问与父类同名的成员,这时就需要super,该关键字的主要作用:在子类中访问父类的成员

class Parent {
    int value = 10;
    public void methodA() {
        System.out.println("Parent中的methodA()");
    }
}

class Child extends Parent {
    int value = 20;
    public void methodA() {
        System.out.println("Child中的methodA()");
    }
    void printValues() {
        System.out.println("Child value: " + value);         // 子类字段
        System.out.println("Parent value: " + super.value); // 父类字段
        methodA();//子类方法
        super.methodA();//父类方法
    }

    public static void main(String[] args) {
        Child c = new Child();
        c.printValues();
    }
}

执行结果:

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

 注意事项:

1.只能在非静态的方法中使用

2.在子类方法中,调用父类的成员变量和方法

子类构造方法

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

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

    public static void main(String[] args) {
        Derived d = new Derived();
    }
}

执行结果:

在子类构造方法中,并没有写任何关于基类构造的代码,但是在构造子类对象时,先执行基类的构造方法,然后执行子类的构造方法,因为:子类对象中成员是由两部分组成的,基类继承下来的以及子类新增加的部分。父子父子必是先有父后有子,所以在构建子类对象的时候,先要调用基类的构造方法将从基类继承下来的成员构造完整,然后再调用子类自己的构造方法,将子类自己新增加的成员初始化完整。 

注意:

1.若父类显式定义无参或者默认的构造方法,在子类构造方法第一行默认有隐含的super()调用,即调用基类的构造方法

2.如果父类构造方法是带有参数的,此时需要用户为子类显式定义的构造方法,并在子类构造方法中选择合适的父类构造方法调用,否则编译失败

3.在子类的构造方法中,super(..)调用父类构造时,必须是子类构造方法中的第一条语句

4.super(...)只能在子类构造中出现一次,并且不能和this同时出现

super和this

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

相同点:

1.都是Java的关键字

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

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

不同点:

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

2.在非静态成员方法中,this用来访问本类的方法和属性,super用来访问父类的方法与属性

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

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

再谈初始化

还记得之前讲过的代码块吗?我们来简要回顾一下几个重要的代码块:实例代码块和静态代码块。

我们之前讲过在没有继承关系下的执行顺序。

1.静态代码块先执行,并且只执行一次,在类的加载阶段执行

2.当有对象创建时,才会执行实例代码块,实例代码块执行完成后,构造方法执行

那么如果现在有继承关系,那它们的执行顺序又是什么?让我们看看下面的代码:

class Person {
    public String name;
    public int age;
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("Person:构造方法执行");
    }

    {
        System.out.println("Person:实例代码块执行");
    }

    static {
        System.out.println("Person:静态代码块执行");
    }
}

public class Student extends Person {

    public Student(String name, int age) {
        super(name, age);
        System.out.println("Student:构造方法执行");
    }

    {
        System.out.println("Student:实例代码块执行");
    }

    static {
        System.out.println("Student:静态代码块执行");
    }
}

public class Test1 {
    public static void main(String[] args) {
        Student s1 = new Student("zhangsan",19);
        System.out.println("-----------------------------");
        Student s2 = new Student("lisi",22);
    }
}

执行结果:

通过分析结果,得出以下结论:

1.父类静态代码块优先于子类静态代码块执行,而且是最早执行

2.父类实例代码块和父类构造方法紧接着执行

3.子类实例代码块和子类构造方法紧接着执行

4.第二次实例化子类对象时,父类盒子类的静态代码块都将不会执行 

protected关键字

  1. 成员可见性: 使用 protected 关键字修饰的成员(字段或方法)可以被同一个包内的其他类访问,以及继承自该类的子类访问。

  2. 访问权限范围: protected 修饰的成员在同一个包内是可见的,同时也对继承关系中的子类可见,即使子类位于不同的包内。

  3. 使用举例:

    package com.example; // 包名
    
    public class Parent {
        protected int value;
        
        protected void printValue() {
            System.out.println("Value: " + value);
        }
    }
    
    
    package com.example; // 同一个包
    
    public class Child extends Parent {
        void accessParent() {
            value = 10;         // 访问父类字段
            printValue();       // 调用父类方法
        }
    }
    
    
    package otherpackage; // 不同包
    
    import com.example.Parent;
    
    public class OtherChild extends Parent {
        void accessParent() {
            value = 20;         // 访问父类字段
            printValue();       // 调用父类方法
        }
    }
    

继承方式

在Java中有以下几种继承方式:

注意:java不支持多继承

我们写的类是现实事物的抽象,而我们真正在公司中所遇到的项目往往业务比较复杂,也会涉及到一系列复杂的概念,都需要我们用代码表示,所以在实际项目中写的类比较多,类之间的关系也十分复杂 

但是即使如此,我们并不希望类之间的继承层次太复杂,一般我们不希望超出三层的继承关系,如果继承层数过多,就考虑对代码进行重构了。

如果想从语法上限制继承,就可以使用final关键字

final关键字

final关键字可以用来修饰变量,成员方法和类。

1.修饰变量和字段,表示常量(即不能修改)

final int a = 10;

a = 20;//编译出错

2.修饰类,表示此类不能被继承

final public class Animal {

...

}

public class Bird extends Animal {

...

}

//编译出错

我们平时用的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;//可以复用车载系统类中的属性和方法
    //。。。
}
public class Benz extends Car {
    //将汽车中的轮胎,发动机,车载系统全部继承下来
}

组合和继承都可以实现代码的复用,应该使用继承还是组合,需要根据应用场景来选择。一般建议:能用组合尽量用组合

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

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

相关文章

【Redis】Redis三种集群模式-主从、哨兵、集群各自架构的优点和缺点对比

文章目录 前言1. 单机模式2. 主从架构3. 哨兵4. 集群模式总结 前言 如果Redis的读写请求量很大,那么单个实例很有可能承担不了这么大的请求量,如何提高Redis的性能呢?你也许已经想到了,可以部署多个副本节点,业务采用…

FPGA应用学习笔记------系统复位一(同异复位)

要满足复位恢复时间才能正常复位,不然会产生输出准稳态,输出逻辑错误 复位恢复时间只会存在复位释放时刻,不会出现在确立时刻,则不推荐完全异步复位 完全同步复位,肯定是同步于时钟滴,并将总是满足时钟条件…

视觉SLAM十四讲---【第三讲-三维空间刚体运动】

坐标系和位姿变换 坐标系 在三维空间中,三根不共面的轴,坐标系能用他的基来表示。 机器人中各种坐标系: 世界系、惯性系机体系传感器参考系 点、向量、坐标系 坐标系分为左左手系和右手系 下面讨论有关向量的运算: 内积(对应坐…

6.物联网操作系统信号量,二值信号量,计数信号量

一。信号量的概念与应用 信号量定义 FreeRTOS信号量介绍 FreeRTOS信号量工作原理 1.信号量的定义 多任务环境下使用,用来协调多个任务正确合理使用临界资源。 2.FreeRTOS信号量介绍 Semaphore包括Binary,Count,Mutex; Mutex包…

jmeter返回值中的中文显示为????问号处理解决方案

jmeter返回值中的中文显示为????问号 查找解决方案时,发现了以下两种解决方案: 一、1.打开jmter配置文件bin/jmeter.properties 2.修改配置文件,查找“sampleresult.default.encoding”将其改为utf8,注意要去掉“#”号 sample…

el-table实现静态和动态合并单元格 以及内容显示的问题

实现效果图 <el-tablev-loading"loading":data"tableData"style"width: 100%":row-class-name"tableRowClassName"size"small"><el-table-column fixed label"序号" width"50"><el-tab…

ad+硬件每日学习十个知识点(33)23.8.13 (导出gerber,PCB加工工艺)

文章目录 1.第一次制造输出2.第二次制造输出3.第三次制造输出4.嘉立创加工工艺信息5.PCB板材分类6.PCB的板子厚度和内外层铜厚1.板子厚度2.内外层铜厚 7.什么是PCB喷锡&#xff1f;8.PCB喷锡的主要作用。9.有铅喷锡和无铅喷锡的区别。 1.第一次制造输出 答&#xff1a; 2.…

首批通过!曙光云多款产品通过信通院可信云认证

7月25日&#xff0c;由中国信通院主办的2023可信云大会在北京举行&#xff0c;中科曙光Cloudview云计算操作系统和StackCube-K超融合系统获得可信云首批认证&#xff0c;并分别通过《一云多芯稳定性度量评估模型》增强级要求和《可信云超融合面向信创场景的评估》标准。 为响应…

R语言画图的-- ggplot2(实现图的精细修改)

文章目录 1. theme函数实现图的全局修改2. 图的精确修改3. 其他修改1. 坐标轴的排序&#xff1a;2. 实现一页多图 4. 具体作图中的参数修改(某些特殊的参数)柱状图的参数修改 写在最后 ggplot2是R中用来作图的很强的包&#xff0c;但是其用法比较多且各种参数比较复杂&#xff…

司徒理财:8.15早盘黄金1905多,最新操作建议

黄金昨日虽然再次新低&#xff0c;但是在司徒所强调的1902位置企稳&#xff0c;反弹即将开启&#xff0c;早盘依托1902的支撑低多看涨&#xff0c;1905现价可以直接多&#xff01;黄金本次的下跌已经接近尾声&#xff0c;弱不再弱必转强&#xff01;长时间大幅度的下跌后必将迎…

电脑剪辑用哪个软件比较好?电脑视频剪辑软件分享

在电脑上剪辑视频可以让您更容易地编辑和组织素材&#xff0c;以及添加音频、标题和其他效果。此外&#xff0c;电脑上的剪辑软件通常比手机上的应用程序更强大&#xff0c;使我们可以进行更精细的编辑&#xff0c;并获得更好的最终产品。那么电脑剪辑视频哪个软件比较好用呢&a…

创建maven的Springboot项目出现错误:Cannot access alimaven

创建maven的Springboot项目出现错误&#xff1a;Cannot access alimaven 1&#xff09;问题2) 分析问题3&#xff09;解决问题 1&#xff09;问题 创建maven的Springboot项目出现错误&#xff1a; Cannot access alimaven (http://maven.aliyun.com/nexus/content/groups/p…

开学季电容笔怎么选?iPad第三方电容笔了解下

不少的学生党开学必备清单里都少不了电容笔&#xff0c;可见其的重要性。自从苹果发布了ipad的原装电容笔以来&#xff0c;这款电容笔在目前市面上就一直很受欢迎&#xff0c;不过由于Apple Pencil的售价实在是太贵了&#xff0c;使得大部分人都买不起。于是&#xff0c;市面上…

【LeetCode】242 . 有效的字母异位词

242 . 有效的字母异位词&#xff08;简单&#xff09; 方法&#xff1a;哈希表 思路 首先判断两个字符串长度是否相等&#xff0c;不相等直接返回 false&#xff1b;接下来设置一个长度为26 的哈希表&#xff0c;分别对应26个小写字母&#xff1b;遍历两个字符串&#xff0c;…

【虚幻引擎】UE5数字人的创建

安装插件 在插件里面找到MetaHuman&#xff0c;设置激活&#xff0c;然后重启引擎 找到bridge&#xff0c;并开启&#xff0c;这个需要我们制作完成的metahuman需要在这个插件里下载&#xff0c;unreal5自动安装 创建metahuman 首先添加一个metahuman本体&#xff0c;如果你的插…

log4net使用

一. Log4Net简介 Log4net是从Java中的Log4j迁移过来的一个.Net版的开源日志框架&#xff0c;它的功能很强大&#xff0c;可以将日志分为不同的等级&#xff0c;以不同的格式输出到不同的存储介质中&#xff0c;比如&#xff1a;数据库、txt文件、内存缓冲区、邮件、控制台、ANS…

02.用户信息UserDetails相关入门

1. 前言 前一篇介绍了 Spring Security 入门的基础准备。从这篇开始我们来一步步窥探它是如何工作的。我们又该如何驾驭它。本篇将通过 Spring Boot 2.x 来讲解 Spring Security 中的用户主体UserDetails。以及从中找点乐子。 2. Spring Boot 集成 Spring Security 这个简直…

同比增长近4倍!5G智能座舱爆发

5G智能座舱&#xff0c;正在进入爆发期。 高工智能汽车研究院监测数据显示&#xff0c;2023年1-6月中国市场&#xff08;不含进出口&#xff09;乘用车前装标配5G智能座舱交付63.18万辆&#xff08;含选装&#xff09;&#xff0c;同比增长370.09%。 同时&#xff0c;5G与车载智…

透镜天线的分类、特点及龙伯球透镜天线原理

透镜天线&#xff0c;一种能够通过电磁波&#xff0c;将点源或线源的球面波或柱面波转换为平面波从而获得笔形、扇形或其他形状波束的天线。通过合适设计透镜表面形状和折射率n&#xff0c;调节电磁波的相速以获得辐射口径上的平面波前。透镜天线吸收了许多光信息工程技术&…

蓝牙资讯|苹果智能戒指可以通过多个戒指控制用户界面

近日&#xff0c;美国专利商标局公布了苹果公司的一项专利申请&#xff0c;涉及使用单个或多个智能指环来控制用户界面的各个方面。Apple Vision Pro 使用眼动追踪和摄像头来监控用户手指的空中手势控制 visionOS&#xff0c;就像鼠标使用 Mac 一样&#xff0c;戒指专利有一个明…