你真的懂Java的继承吗?你知道什么时候用继承吗?设计继承是为了什么?

本文详细阐述了Java中的封装意义,继承的必要性,如何使用以及其带来的好处。讲解了继承的设计原则、特点,包括单继承和多层继承,以及子类继承父类的内容(构造方法、成员变量和方法)。特别强调了继承中成员变量的访问顺序和方法重写的要求,以及构造方法的特殊性。
摘要由CSDN通过智能技术生成

目录

1. 封装的意义是什么?

2. 为什么需要继承?

3. 继承是什么?如何使用?

4. 继承的好处是什么?

5. 设计继承需要注意什么?

6. 继承的特点

7. 子类到底继承了父类的哪些内容

7.1 继承内容

7.2 虚方法表的概念

8. 继承中成员变量的访问特点

8.1 例子1:方法优先访问局部变量

8.2 例子2:方法其次访问成员变量

8.3 例子3:方法最后访问父类变量

8.4 例子4:this 主动调用成员变量,super 主动调用父类变量

8.5 总结

9. 继承中方法的重写详情

9.1 重写的目的与注意事项

9.2 虚方法表再重写中的作用

10.  继承中构造方法的访问特点


1. 封装的意义是什么?

封装想必大家都不陌生,众所周知,Java最重要的一个特点就是面向对象,对象代表什么,就要封装什么样的数据,并提供数据对应的内容。

我们可以利用封装将以前的一些零散的属性信息,方法封装在一起,组成一个整体,这个整体就是我们常说的对象。在没有封装以前,我们就需要对这个零三的属性和方法分别做处理,将它们作为参数传入到方法中,有了封装之后,我们只需要一个对象传入方法中,就可以解决以前的所有烦恼,非常的方便。

举个最简单的例子,现在有一个打印学生信息的类,类中有一些属性和方法

现在有一业务需求,要求打印学生的相关信息。

(1)没有封装加入:我们就需要把学生的信息全部作为参数传递到方法中,有多少个属性就要传递多少个参数;

(2)有了封装的加入:我们将学生的信息封装到学生对象中,将 get 和 set 方法也封装到学生对象中,然后将对象作为参数传递到方法中,这样当我们需要打印学生信息时,只需要通过对象的 get 方法获取到属性信息,非常的方便,极大的简化的业务的复杂度。

2. 为什么需要继承?

我来说明一个场景引出一个问题,同学们或许就会明白了。

假设现在的业务中,我有两个类,一个是学生类 Student,一个是老师类 Teacher。它们的属性和方法如下图所示,已经封装完成

仔细观察上面两个类,有没有发现什么问题。其实很显然,这两个类重复的代码太多了,那么有没有什么办法能够解决这个问题呢?我们可不可以把重复的代码抽取出来重复使用呢?就像封装一样。

当然是可以的,那么接下来就需要用到今天的重点——继承。

3. 继承是什么?如何使用?

在实际开发过程中,基本都会无法避免的产生一些重复相似的属性和方法,为了避免让我们项目代码过于冗余和臃肿,就引入了继承这么一个概念。

Java 中关于继承提供了一个关键字 extend,我们使用这个关键字,就可以让一个类与另一个类之间产生继承关系,使用方法也比较简单,只需要在一个类的后面写上 "extend 被继承类类名"。

使用继承,我们就可以对上面的 Student 类和 Teacher 类进行优化,将两个类的公共部分抽取出来,生成第三个类,让 Student 类和 Teacher 类都继承这第三个类。

如下图所示,Student 类和 Teacher 类都被称为子类(也叫派生类),Person 类被称为父类(也可以叫超类或基类)。

4. 继承的好处是什么?

(1)通过继承,我们可以将一些类中相同的属性和方法,即重复的代码抽取到一个类中作为父类,提高了代码的复用性,降低的代码的冗余。

(2)青出于蓝而胜于蓝。子类在继承了父类之后,得到了父类中的所有属性和方法(私有属性和方法除外),还可以在自己的类中添加额外的属性和方法,扩展自己的功能,让子类更加强大,以便于满足更多更复杂的业务需求。

5. 设计继承需要注意什么?

请同学们牢牢记住下面这句话,"当类与类之间,存在相同(共性)的内容,并且满足子类是父类中的一种,就可以考虑使用继承优化代码"

使用继承要满足两点:(1)存在相同内容;(2)一定一定要满足子类是父类中的一种,这一点不能忽视!!!

Student 类和 Teacher 类都是人的一种,所以他们都可以继承 Person 类;

Manager 管理者类和 Coder 程序猿类都是员工的一种,他们可以同时继承 Employee 员工类;

而大家看下面这种情况,我能否让 Coder 程序猿类和 Phone 手机类继承一个 Goods 商品类呢?它们都有相同的属性 id 和名字属性。

答案是不能的,因为在常规认知中,它们两个之间是没有直接或者间接的联系的。

所以再强调一遍,使用继承时一定要满足两点。

第一点:类与类之间有共同的内容;

第二点:子类必须是父类中的一种,如果不是一种就算有相同的属性也不能使用继承;

6. 继承的特点

"Java只支持单继承,不支持多继承,但支持多层继承"。

我们拆分成三句话来说明。

Java 只支持单继承:在 Java 中,一个类只能继承一个类,不能再继承其它的类;可以理解为一个儿子只能有一个父亲,不能有两个;

Java 不支持多继承:假设我现在的 A类 继承了 B类,而且B类中有一个方法 method 打印一句话"我继承了B类",A类 也继承了 C类,C类中也有一个方法 method 打印一句话 "我继承了C类",那么当A类的对象调用 method 方法时,它就蒙了,它不知道该调用哪个,所以为了避免这种情况的发生,Java 中不支持多继承。

Java 支持多层继承:这个意思就是A类继承了B类,B类也可以继承C类,C类也可以继承D类,类似于套娃,无限循环。也好比是四世同堂,儿子——>爸爸——>爷爷——>姥爷。

还有一点需要注意,Java 中的所有类都直接或者间接的继承了 Object 类。

7. 子类到底继承了父类的哪些内容

7.1 继承内容

在一个类中,我们通常会定义 成员变量,构造方法,成员方法。

子类继承父类时,它所能继承的内容如下图所示

(1)父类中的构造方法,不管是不是私有的,子类都继承不了;

(2)父类中的成员变量,不管是不是私有的,子类都能继承,只是父类中的成员变量被锁定了,子类可以继承,但是子类访问不了;

(3)父类中的成员变量,非私有的方法可以继承下来;

7.2 虚方法表的概念

在成员方法的继承中,我要补充一个额外的点,子类在继承父类中的成员方法时,实际上会继承父类的虚方法表

Java 在最顶级的父类开始,会设立一个虚方法表,它会把这个类当中经常可能会用到的方法单独的抽取出来,放到虚方法表中。

判断一个方法是否会经常被使用需要满足以下三点:

(1)不被 private 修饰;

(2)不被 static 修饰;

(3)不被 final 修饰;

满足这三点的方法,就会被存放到虚方法表中。

在继承的过程中,父类就会把这个虚方法表交给子类,子类在父类虚方法表的基础上,添加自己的虚方法,然后继续交给后续的子类,层层传递,如下图所示。

8. 继承中成员变量的访问特点

在继承中,成员变量的访问特点遵循 "就近原则,谁离我近,我就用谁"。

8.1 例子1:方法优先访问局部变量

如下代码

public class Test9 {
    // 定义 main 方法做测试
    public static void main(String[] args) {
        // 定义 Son 类对象
        Son son = new Son();
        // 调用方法
        son.ziShow();
    }
}
// 定义一个父类
class Futher{
    // 定义一个字符串
    String name = "父类字符串";
}
// 定义一个儿子类继承父类
class Son extends Futher{
    // 定义一个字符串
    String name = "子类字符串";
    // 定义一个方法
    public void ziShow(){
        String name = "方法内部的字符串";
        System.out.println(name);
    }
}

运行上述代码,就可以在控制台看到,打印出的是方法内部的 name 字符串,因为方法内部的字符串距离方法最近

8.2 例子2:方法其次访问成员变量

刚才的演示中,方法内部本身就有 name 字符串,那么现在我将方法内部的字符串删除,再重新运行,就可以在控制台中看到如下结果,

所以可以得出结论,当继承中方法访问的变量在方法内部存在时,会优先就近访问方法内部的局部变量;当方法内部没有变量可以访问时,会去访问类中的成员变量。 

8.3 例子3:方法最后访问父类变量

我们对上面的方法再做修改,将方法类中的成员变量 name 也删除,再次运行,得出如下结果

结合上面三个例子:可以得出结论,当方法内部没有变量可以访问时,方法会先去访问了当前类的成员变量,如果当前方法类的成员变量也没有可以访问时,会最后去访问父类中的成员变量。 

8.4 例子4:this 主动调用成员变量,super 主动调用父类变量

当我们不想访问方法内部的变量时,就可以通过 this 关键字主动调用方法的成员变量,或通过 super 关键字调用父类变量,在原本的代码基础上稍微做修改;

如下所示

8.5 总结

OK,下面我们几句句话来总结上面四个例子得出的结论。

(1)在继承中,方法调用变量时会采用就近原则,当方法想要访问的变量方法内部本身就有时,会最优先访问方法内部的局部变量;

(2)当方法内部访问不到变量时,会去方法当前类成员变量中寻找访问;

(3)如果成员变量中也访问不到时,会最后去父类成员变量访问;

(4)如果想要主动访问当前方法类成员变量,可以通过 this 关键字,如果想要调用父类成员变量,则可以通过 super 关键字。

9. 继承中方法的重写详情

9.1 重写的目的与注意事项

子类在继承了父类的方法之后,可以使用父类中的方法,但在实际开发过程中,子类继承父类之后,通常会扩展自己额外的功能,那么此时从父类继承下来的方法可能就满足不了业务需求了,这个时候就需要用到方法的重写,我们可以在子类冲重新编写重复类那里继承下来的方法的执行逻辑,高造成满足我们业务需求的方法。

再进行重写时,需要满足以下几点需求。

(1)重写方法的名称,形参列表必须与父类保持一致;

(2)重写的方法上方要加上 @Override 注解;

(3)子类在重写父类方法时,方法的访问权限必须大于等与父类,方法的返回值范围必须小于等于父类;

(4)父类中的私有方法不能被重写;

(5)父类中的静态方法,即被 static 修饰的方法不能被重写,重写会报错;

9.2 虚方法表再重写中的作用

回顾我们上面提到的虚方法表,父类会把常用的方法存放到虚方法表中交给子类,其实在子类重写父类方法的过程中,其实重写的就是虚方法表中从父类那里得到的方法。

如下图所示,如果B类中重写了从父类C那里继承过来的 method2 方法,那么被重写后的 method2 方法就会被加入到虚方法表中,此时A类从B类那里继承过来的 method2 方法,实际上并不是最原始的C类中的方法,而是已经被B类重写后的 method2 方法。

10.  继承中构造方法的访问特点

(1)在继承体系中,子类不能继承父类的构造方法,但是可以通过 super 调用;

(2)子类在执行构造方法之前,会先去执行父类的无参构造;

我们用一段代码来证明一下

public class Test9 {
    // 定义 main 方法做测试
    public static void main(String[] args) {
        // 无参定义 Son 类对象
        Son son = new Son();
        // 带参定义 Son 类对象
        Son son2 = new Son("张三");
    }
}
// 定义一个父类
class Futher{
    String name;
    public Futher(){
        System.out.println("父类无参构造方法先执行");
    }

    public Futher(String name) {
        System.out.println("父类带参构造");
        this.name = name;
    }
}
// 定义一个儿子类继承父类
class Son extends Futher{
    String name;
    public Son(){
        System.out.println("无参构造创建 Son 对象");
    }

    public Son(String name) {
        System.out.println("带参构造创建 Son 对象");
        this.name = name;
    }
}

运行得出如下结果

从运行结果我们可以看出,创建子类时不管我们使用无参构造还是带参构造,父类的无参构造方法都会执行。

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值