写点什么

Java | this 和 super 关键字【深入理解子类和父类的继承关系】

作者:Fire_Shield
  • 2022 年 9 月 26 日
    浙江
  • 本文字数:4516 字

    阅读完需:约 15 分钟

Java | this和super关键字【深入理解子类和父类的继承关系】

在上一篇文章中,我们提到了 extends 关键字,讲了有关面向对象的第二大特征——类的继承,本文,我们来深入讲解一下通过 this 和 super 这两个关键字反映的子类和父类的继承关系


@TOC

一、关键字介绍及引入

1、this 关键字

this 关键字是什么?

之前我们在讲类的 static 关键字时也有提到过 this 关键字,它可以用来访问当前对象的成员变量和成员方法


this 是一个引用,是一个变量,this 变量中保存了内存地址指向了自身,它存储在 JVM 堆内存中 Java 对象的内部


==有关使用格式在下面 super 关键字中一起说明==

2、super 关键字

super 关键字是什么?

对于 super 关键字,这个是在 Java 中出现的,而 C++中没有,它表示子类继承父类之后可以通过这个关键字去访问父类的一些成员变量和成员方法,以及可以通过这个关键字去实现父类的构造器

this 和 super 关键字要怎么使用(附有表格插图)

  • 有关这个,我在上面的 this 关键字中并没有提到具体的使用格式,这里一并说明:book:

  • 从上述表格我们可以很清晰的看出这两个关键字的使用方法和格式,主要是 this.和 this()以及 super.和 super()这两种方法,this.和 super.主要是用于访问成员方法和成员变量,我们不做过多讲解,后面主要讲解的是 this()和 super()这两种如何去访问本类和父类的构造器

二、关键字的使用及分步讲解

1、this 关键字

this 关键字的基础应用

接下来分别来讲一下 this 关键字在构造方法和实例方法中的应用


public class People {    String name;    int leg,hand;    People(String name){        this.name = name;        this.init();  //可省略    }    void init(){        leg = 2;        hand = 3;        System.out.println(name + "有" + leg + "条腿" + hand + "双手");    }    public static void main(String[] args) {        People people = new People("奥巴马");    }}
复制代码


  • 首先是在构造方法中,从 People 类的有参构造中我们可以看出,不仅初始化了 name 成员变量,而且调用了成员方法**init()**,这里都是使用到了 this 关键字,但是这个 this 关键字对于成员变量和成员方法的调用时可以省略的,因为这个关键字即使你没写的话它系统中也是会默认有的


class A{    int x;    static int y;    void f(){        this.x = 100;        A.y;        System.out.println(this);    }}
复制代码


  • 这里可以看到,我直接将 this 关键字通过输出语句打印了出来,这里代表的其实就是当前对象的地址

this 关键字调用本类其他构造器

  • 有时候,我们在创建一个对象的时候,忘了去初始化其一些参数,但这些参数又是必须要有的,这个时候我们就可以去通过 this(...)去默认初始化化子类的一些数据

  • 看到下面代码,我们看到是==写了三个构造器==,一个是默认无参构造器,一个是两个参数的全参构造器,但是还有一个构造器之后一个 name 属性,这其实也是合法的,为的就是在传参的时候若是没有传入全部的参数也可以运行,也不会出错,所以甚至你可以也只有一个 schoolName 这个数据去初始化


public class People {    private String name;    private String schoolName;
public People(){} public People(String name){ //借用本类兄弟构造器 //super(); 此处不能调用父类构造器,否则会重复调用 this(name,"光明顶"); } public People(String name, String schoolName) { super(); this.name = name; this.schoolName = schoolName; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getSchoolName() { return schoolName; }
public void setSchoolName(String schoolName) { this.schoolName = schoolName; }}
复制代码


  • 接下去就是使用 test 测试类去测试一下,首先创建第一个对象 ,初始化了姓名和学校,但是在创建第二个对象的时候,却没有去初始化其学校,但是去没有报错,而且从运行结果来看我们默认设置的学校名称初始化也成功了,张无忌在【光明顶】学习,可见这种使用的容错率很低,也比较灵活:bell:


public class Test {    public static void main(String[] args) {        People p1 = new People("张三丰","凌霄宝殿");        System.out.print(p1.getName());        System.out.println("在" + p1.getSchoolName() + "学习");
People p2 = new People("张无忌"); System.out.print(p2.getName()); System.out.println("在" + p2.getSchoolName() + "学习"); }}
复制代码



  • 具体的调用流出如下


2、super 关键字

super 关键字操作被隐藏的成员变量和方法

首先我们来看这个案例,初步了解 super 关键字的调用


//求解总和class Sum{    int n;    float f(){        float sum = 0;        for(int i = 1;i <= n; ++i)            sum += i;        return sum;     //返回总和    }}
复制代码


//求解平均值class Average extends Sum{    int n;    float f(){        float c;        super.n = n;        //调用父类中的n,进行初始化操作        c = super.f();      //获取总和        return c/n;         //返回平均值    }    float g(){        float c;        c = super.f();        return c/2;     //返回总和的一半    }}
复制代码


public class test1 {    public static void main(String[] args) {        Average average = new Average();        average.n = 100;        //初始化数据个数        float resultOne = average.f();      // 5050/100 = 50.5        float resultTwo = average.g();      // 5050/2 = 2525.0
System.out.println("resultOne = " + resultOne); System.out.println("resultTwo = " + resultTwo); }}
复制代码


  • 从上述案例,我们可以看到,有一个 Sum 求和类,然后有一个 Average 求平均值类,这个类使用 extends 关键字继承了 Sum 求和类,在这个类中是,因为需要求解平均值,所以需要使用总和,因此可以直接通过 super 关键字去获取父类中已封装好的方法,然后在主方法中只需要传入对应的数据个数即可;以及返回总和的一半也是一样,需要调用父类的求和方法


==注:当用 super 调用被隐藏的方法时,该方法中出现的成员变量和被子类隐藏的成员变量或继承的成员变量==

使用 super 调用父类的构造方法(重点!!!)

1、子类的全部构造器默认会调用父类的默认构造器


public class Animal {    Animal() {        System.out.println("父类无参构造");    }}
class Dog extends Animal{ Dog(){ super(); //写不写都有,默认找父类的无参构造器 System.out.println("子类无参构造"); } Dog(String name){ super(); //写不写都有,默认找父类的无参构造器 System.out.println("子类有参构造"); }}
复制代码


public class Test {    public static void main(String[] args) {        Dog d = new Dog();        System.out.println(d);
Dog d2 = new Dog("得到"); System.out.println(d2); }}
复制代码



  • 从上面的例子我们可以看出,当使用子类去声明一个对象时,会首先调用父类的默认构造器,这个在 C++中其实也是这样,无论你子类使用的是无参还是有参构造器,因为你继承了一个父类,所以会默认去调用父类的无参构造器

  • 在子类构造其中的第一行,默认会有 super()这个东西,这个写不写其实都是有的,意思就是去调用父类的无参构造器,从运行结果我们也可以看出==父类的无参构造器都是执行在子类构造器之前==

  • 那有小伙伴就会问为什么,其实就是因为你子类在初始化的时候,可能会用到父类的数据,所以一定要先调用父类构造器,完成父类空间初始化,这样子类才能使用父类中的成员变量和成员方法


2、在子类中可以使用 super 去调用父类的有参构造器


  • 比如这里有一个 People 类,里面有它的两个成员变量 name 和 age,以及它们的一些 get、set 方法和构造方法


public class People {    private String name;    private int age;
public People(){}
public People(String name, int age) { this.name = name; this.age = age; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }}
复制代码


public class Test2 {    public static void main(String[] args) {        Teacher t = new Teacher("刘德华",50);        System.out.println(t.getName());        System.out.println(t.getAge());    }}
复制代码


public class Teacher extends People{    public Teacher(String name,int age){        super(name,age);    }}
复制代码


  • 接着我这里有一个测试类需要创建一个老师对象,传入其姓名和年龄,但是通过 Teacher 老师类来看,其并没有自己的独属的成员变量,只有继承自父类的一些功能,那要怎么去初始化其姓名和年龄呢?

  • 当我们不知道 super 关键字的时候,我们可能回去这么做,就如下面代码所示,但是这样写是无法初始化子类数据的,因为子类中根本就没有 name 和 age 属性,要初始化子类的这个数据,必须将其传入到父类中,==通过调用父类的有参构造去初始化子类的数据==,具体的初始化数据如下图所示:point_down:



public class Teacher extends People{    public Teacher(String name,int age){        this.name = name;        this.age = age;    }}
复制代码


  • 但是有一点要注意的是,如果你只是使用 super()去调用父类中的构造器,那么我们在上面讲到过,会默认调用父类的无参构造器,但如果这时候父类中没有给出无参构造器的话,就会报错,这时候程序就会出问题,那如何解决呢?:mag:

  • 这个很简单,我们只要通过手写 super(...),去调用父类的有参构造,就没事了,或者更加安全一点就是在父类中给出无参构造器,这样的话无论别人是是用的 super()或是 super(...),都不会出问题

三、在使用 this(...)和 super(...)时的注意事项

  • 子类通过 this(...)去调用本类的其他都早起,本类的其他构造器会通过 super 去手动调用父类构造器,最终还是会调用父类构造器的

  • 注意:this(...)和 super(...)都只能放在构造器的第一行,所以二者不能共存在同一个构造器中


我们到通过具体代码的分析来看一下:mag:



四、总结和回顾

在本文中,我们讲解了 Java 中的 this 和 super 两个关键字,知晓了其基本概念以及如果通过两种方式 this.和 this(...)去调用子类中的变量和方法以及本类兄弟构造器,使用 super.和 super(...)通过子类去访问父类中的成员变量和成员方法,以及父类的构造器,较 extends 关键字更加深入地理解了子类和父类的继承关系,也对类和对象的第二大特性——继承有了一个完整的知识体系


最后感谢您对本文的观看,如有问题请于评论区留言或私信我:four_leaf_clover:


以下是我开创的社区,感兴趣的小伙伴们可以加入进来一起交流学习,解决编程的难题


我的社区::fire:烈火神盾:fire:


发布于: 刚刚阅读数: 6
用户头像

Fire_Shield

关注

语言观决定世界观 2022.09.02 加入

高校学生,热爱编程,喜欢写作

评论

发布
暂无评论
Java | this和super关键字【深入理解子类和父类的继承关系】_super_Fire_Shield_InfoQ写作社区