栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Java重要知识点详细讲解——继承

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java重要知识点详细讲解——继承

⭐️继承⭐️

继承的基本语法继承快速入门案例继承的深入讨论/细节问题继承的本质分析(重要)

本文节选自我的另一篇文章,大家有兴趣可以看一看:Java总结六:面向对象编程(中)
继承可以解决代码复用,让我们的编程更加靠近人类思维.当多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过 extends 来声明继承父类即可。

继承的基本语法
class 子类 extends 父类 {
}

其中:
子类就会自动拥有父类定义的属性和方法
父类又叫超类,基类。
子类又叫派生类。

继承快速入门案例

☕️Extends01类:

package com.hj.第八章面向对象编程.继承;
public class Extends01 {
    public static void main(String[] args) {
        Pupil pupil = new Pupil();
        pupil.name = "银角大王~";
        pupil.age = 11;
        pupil.testing();
        pupil.setScore(50);
        pupil.showInfo();
        System.out.println("=======");
        Graduate graduate = new Graduate();
        graduate.name = "金角大王~";
        graduate.age = 23;
        graduate.testing();
        graduate.setScore(80);
        graduate.showInfo();
    }
}

☕️Student类:(父类,是 Pupil 和 Graduate 的父类)

package com.hj.第八章面向对象编程.继承;
//父类,是 Pupil 和 Graduate 的父类
public class Student {
    //共有属性
    public String name;
    public int age;
    private double score;//成绩
    //共有的方法
    public void setScore(double score) {
        this.score = score;
    }
    public void showInfo() {
        System.out.println("学生名 " + name + " 年龄 " + age + " 成绩 " + score);
    }
}

☕️Pupil类:(Pupil 继承 Student 类)

package com.hj.第八章面向对象编程.继承;
//让 Pupil 继承 Student 类
public class Pupil extends Student {
    public void testing() {
        System.out.println("小学生 " + name + " 正在考小学数学..");
    }
}

☕️Graduate类:(Graduate 继承 Student 类)

package com.hj.第八章面向对象编程.继承;

public class Graduate extends Student {
    public void testing() {//和 Pupil 不一样
        System.out.println("大学生 " + name + " 正在考大学数学..");
    }
}

输出结果:

小学生 银角大王~ 正在考小学数学..
学生名 银角大王~ 年龄 11 成绩 50.0
=======
大学生 金角大王~ 正在考大学数学..
学生名 金角大王~ 年龄 23 成绩 80.0

##继承给编程带来的便利

    代码的复用性提高了代码的扩展性和维护性提高了
继承的深入讨论/细节问题

 1.子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问, 但是私有属性和方法不能在子类直接访问,要通过父类提供公共的方法去访问。
本细节分析:
现在提供两个类:
☕️base类:父类

package com.hj.第八章面向对象编程.继承细节;

public class base { //父类
    //4 个属性
    public int n1 = 100;
    protected int n2 = 200;
    int n3 = 300;
    private int n4 = 400;

    public base() { //无参构造器
        System.out.println("父类 base()构造器被调用....");
    }

    public void test100() {
        System.out.println("test100");
    }

    protected void test200() {
        System.out.println("test200");
    }

    void test300() {
        System.out.println("test300");
    }

    private void test400() {
        System.out.println("test400");
    }
}

☕️Sub类:子类

package com.hj.第八章面向对象编程.继承细节;

public class Sub extends base { //子类
    public Sub() {//无参构造器
        System.out.println("子类 Sub()构造器被调用....");
    }
    public void sayOk(){
    }
}

1.1 非私有的属性和方法可以在子类直接访问, 但是私有属性和方法不能在子类直接访问

在Sub类sayOk方法中添加输出语句访问父类的属性n1、n2、n3、n4:

package com.hj.第八章面向对象编程.继承细节;

public class Sub extends base { //子类
    public Sub() {//无参构造器
        System.out.println("子类 Sub()构造器被调用....");
    }
    public void sayOk(){
        //我们可以发现父类的非private属性和方法都可以在子类访问
        System.out.println(n1+" "+n2+" "+" "+n3+" "+n4);
        test100();
        test200();
        test300();
        test400();
    }
}

这时会报错:

总结:说明了父类的非private属性和方法都可以在子类访问。
1.2私有属性和方法不能在子类直接访问,要通过父类提供公共的方法去访问
☕️在base类中增加公共方法getN4() :

    //父类提供一个public的方法,返回了n4
    public int getN4() {
        return n4;
    }
    //call:调用,调用test400方法。
    public void callTest400(){
        test400();
    }

☕️在子类中调用公共方法getN4()以输出私有属性n4:

package com.hj.第八章面向对象编程.继承细节;

public class Sub extends base { //子类
    public Sub() {//无参构造器
        System.out.println("子类 Sub()构造器被调用....");
    }
    public void sayOk(){
        //要通过父类提供公共的方法去访问
        System.out.println("n4=" + getN4());
        callTest400();
    }
}

增加一个类用于调用:
☕️ExtendsDetail类:

package com.hj.第八章面向对象编程.继承细节;

public class ExtendsDetail {
    public static void main(String[] args) {
        Sub sub=new Sub();
        sub.sayOk();
    }
}

☕️输出结果:

父类 base()构造器被调用....
子类 Sub()构造器被调用....
n4=400
test400

总结:
私有属性和方法不能在子类直接访问,要通过父类提供公共的方法去访问

2. 子类必须调用父类的构造器, 完成父类的初始化
本细节分析:
看上面的代码和分析的输出结果可以看到。

父类 base()构造器被调用....
子类 Sub()构造器被调用....

里面的机制其实是下面代码:

public Sub() {//无参构造器
     //super(); //默认调用父类的无参构造器
     System.out.println("子类 Sub()构造器被调用....");
}

子类无参构造器默认有一个语句:super();,不管写不写它都存在,它的作用是默认调用父类的无参构造器。

3. 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用 super 去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过。

本细节分析:

3.1: 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器
☕️在子类Sub下增加一个有参构造器:

package com.hj.第八章面向对象编程.继承细节;

public class Sub extends base { //子类
    public Sub() {//无参构造器
        //super(); //默认调用父类的无参构造器
        System.out.println("子类 Sub()构造器被调用....");
    }
    //当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器
    public Sub(String name){//有参构造器
        //什么都不写
        System.out.println("子类 Sub(String name)构造器被调用....");
    }
}

☕️创建子类对象sub2:

package com.hj.第八章面向对象编程.继承细节;

public class ExtendsDetail {
    public static void main(String[] args) {
        System.out.println("===创建第一个对象===");
        Sub sub=new Sub();//创建子类对象sub
        //sub.sayOk();
        System.out.println("===创建第二个对象===");
        Sub sub2=new Sub("jack");//创建子类对象sub2
    }
}

☕️运行结果:

父类 base()构造器被调用....
子类 Sub()构造器被调用....
===创建第二个对象===
父类 base()构造器被调用....
子类 Sub(String name)构造器被调用....

可以看到运行后两个对象都会首先调用父类的无参构造器。
3.2: 如果父类没有提供无参构造器,则必须在子类的构造器中用 super 去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过。(前提是父类有有参构造器)

☕️修改父类代码如下:

package com.hj.第八章面向对象编程.继承细节;

public class base { //父类
//    public base() { //无参构造器
//        System.out.println("父类 base()构造器被调用....");
//    }
    public base(String name,int age){
        System.out.println("父类 base(String name,int age)构造器被调用....");
    }
}

此时子类就会报错:


在‘com.hj.第八章面向对象编程.继承细节.base’中没有可用的默认构造函数

这里还要说明一点:
父类是必须要有有参构造器才会有以上错误的,如果没有有参构造器,子类的构造器就不会出现错误,因为创建一个类后,类是默认含有无参构造器的。这一点要非常的注意
解决方法:
☕️指定父类调用有参构造器:

public class Sub extends base { //子类
    public Sub() {//无参构造器
        //super(); //默认调用父类的无参构造器
        super("hj",20);
        System.out.println("子类 Sub()构造器被调用....");
    }
    public Sub(String name){//有参构造器
        super("CLN",20);
        System.out.println("子类 Sub(String name)构造器被调用....");
    }
}

☕️运行结果:

===创建第一个对象===
父类 base(String name,int age)构造器被调用....
子类 Sub()构造器被调用....
===创建第二个对象===
父类 base(String name,int age)构造器被调用....
子类 Sub(String name)构造器被调用....

4. 如果希望指定去调用父类的某个构造器,则显式的调用一下 : super(参数列表)
本细节分析
1.若想用子类的有参构造器Sub(String name,int age)调用父类无参构造可使用以下代码:

public class Sub extends base { //子类
    public Sub() {//无参构造器
        super("hj",20);
        System.out.println("子类 Sub()构造器被调用....");
    }
    //当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器
    public Sub(String name){//有参构造器
        super("CLN",20);
        System.out.println("子类 Sub(String name)构造器被调用....");
    }
    public Sub(String name,int age){
        //1. 要调用父类的无参构造器, 如下或者 什么都不写,默认就是调用 super()
        super();
    }
}
public class ExtendsDetail {
    public static void main(String[] args) {
          Sub sub3=new Sub("z",20);
    }
}

☕️运行结果为:

父类 base()构造器被调用....

2.若想用子类的有参构造器Sub(String name,int age)调用父类有参构造base(String name)可使用以下代码:

public Sub(String name,int age){
        //1. 要调用父类的无参构造器, 如下或者 什么都不写,默认就是调用 super()
        //super();
        //2. 要调用父类的 base(String name) 构造器
        super("hj");
    }

☕️运行结果为:

父类 base(String name)构造器被调用....

3.同理若想用子类的有参构造器Sub(String name,int age)调用父类有参构造base(String name,int age)可使用以下代码:

public Sub(String name,int age){
    //1. 要调用父类的无参构造器, 如下或者 什么都不写,默认就是调用 super()
    //super();
    //2. 要调用父类的 base(String name) 构造器
    //super("hj");
    //3. 要调用父类的 base(String name, int age) 构造器
    super("king", 20);
}

5. super 在使用时,必须放在构造器第一行(super 只能在构造器中使用)
若super不在构造器第一行则会报错:

大家想一想为什么必须放在构造器第一行?
因为子类构造器必须先要调用父类构造器,等父类构造器使用完以后子类构造器才能够继续执行,现有”父“才有”子“。

6. super() 和 this() 都只能放在构造器第一行,因此这两个方法不能共存在一个构造器(注意不是不能用this,是不能用this())

7. java 所有类都是 Object的子类,Object类是所有类的基类。

8. 父类构造器的调用不限于直接父类!将一直往上追溯直到 Object 类(顶级父类)

9. 子类最多只能继承一个父类(指直接继承),即 java 中是单继承机制。
思考:
如何让 A 类继承 B 类和 C 类?
答案:A 继承 B, B 继承 C
10. 不能滥用继承,子类和父类之间必须满足 is-a 的逻辑关系

继承的本质分析(重要)

分析下面案例:

public class ExtendsTheory {
    public static void main(String[] args) {
        Son son = new Son();//内存的布局
        //?-> 这时请大家注意,要按照查找关系来返回信息
        //(1) 首先看子类是否有该属性
        //(2) 如果子类有这个属性,并且可以访问,则返回信息
        //(3) 如果子类没有这个属性,就看父类有没有这个属性(如果父类有该属性,并且可以访问,就返回信息..)
        //(4) 如果父类没有就按照(3)的规则,继续找上级父类,直到 Object...        System.out.println(son.name);//返回就是大头儿子
        //System.out.println(son.age);//返回的就是 39
        //System.out.println(son.getAge());//返回的就是 39
        System.out.println(son.hobby);//返回的就是旅游
    }
}
class GrandPa { //爷爷类
    String name = "大头爷爷";
    String hobby = "旅游";
}
class Father extends GrandPa {//父类
    String name = "大头爸爸";
    private int age = 39;
    public int getAge() {
        return age;
    }
}
class Son extends Father { //子类
    String name = "大头儿子";
}

运行结果:

旅游

这里为什么是旅游呢?
因为:

要按照查找关系来返回信息
(1) 首先看子类是否有该属性
(2) 如果子类有这个属性,并且可以访问,则返回信息
(3) 如果子类没有这个属性,就看父类有没有这个属性(如果父类有该属性,并且可以访问,就返回信息…)
(4) 如果父类没有就按照(3)的规则,继续找上级父类,直到 Object…

内存机制如下图所示:

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/736594.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号