Java继承之再谈构造器

目录

  • Java继承之再谈构造器

    • 初始化基类
    • 默认构造器
    • 带参数的构造器
    • 子类调用父类构造器

Java继承之再谈构造器

初始化基类

前面提到,继承是子类对父类的拓展。《Thinking in Java》中提到下面一段话:

当创建一个导出类的对象时,该对象包含了一个基类的子对象。这个子对象与你用基类直接创建的对象是一样的。二者区别在于,后者来自于外部,而基类的子对象被包装在导出类的对象内部。

我们在创建子类对象时,调用了父类的构造器,甚至父类的父类构造器。我们知道,构造器用于创建对象,那么突然产生疑惑:关于创建一个子类对象时,是否会先创建父类对象?
经过查找资料,得出结论
并没有。在创建子类对象时,会把父类的成员变量和方法加载进内存,既然要加载,便调用父类构造器看看这些数据是如何进行初始化的,仅此而已,并不是创建了父类的对象。
所以,可以看作,子类对象中包含着父类的子对象。我们知道,对象的初始化是至关重要的。那么,这个父类的子对象如何正确初始化呢?对了,就是接下来要说的:在构造器中调用基类构造器来执行初始化
注意:子类并不能继承父类的构造器,只是单纯调用了基类构造器中的初始化代码。

默认构造器

先看一段简单的测试代码:

package com.my.pac13;
/*继承中的构造*/
public class Person {
    Person(){
        System.out.println("Person()");
    }
}
class Student extends Person{
    Student(){
        System.out.println("Student()");
    }
}
class PrimaryStudent extends Student{
    PrimaryStudent(){
        //super();
        System.out.println("PrimaryStudent()");
    }
    public static void main(String[] args) {
        //创建了PrimaryStudent对象
        new PrimaryStudent();
    }
}
/*
 Person()
 Student()
 PrimaryStudent()
*/

关于构造器,我们前面提到,任何没有显式构造器的类都存在着一个无参数的默认构造器。我们上面的例子在默认构造器中加入了打印输出,以便理解。
可以看到的是:

  • 在创建PrimaryStudent时,他的直接父类Student和间接父类Person中的构造器都被调用了,而且可以看到,是"自上而下"的。
  • 父类在子类构造器可以访问它之前,就已经完成了初始化的操作。
  • 若子类没有显式调用父类的构造器,则自动调用父类的默认(无参)构造器。

带参数的构造器

前面的代码中,每个类都含有默认的构造器,创建子类对象时,是自上而下,且子类会默认调用父类的无参构造器。那么,假设父类正好没有无参构造器或者你正想调用父类的带参构造器,这时就需要我们的super关键字。(super关键字之后还会进行总结)
我们直接在原来的基础上稍作修改,并进行测试。

package com.my.pac13;
/*调用基类构造器是子类构造器中要做的第一件事*/
public class Person {
    //没有默认构造器
    Person(String name){
        System.out.println("Person()\t"+name);
    }
}
class Student extends Person{
    //也没有默认构造器,且用super显式调用
    Student(String n){
    //super关键字调用父类的构造器
        super(n);
        System.out.println("一参数Student\t"+n);
    }
    Student(String n,String m){
    //this关键字调用同一类中重载的构造器
        this(n);
        System.out.println("二参数student()\t"+m);
    }
}
class PrimaryStudent extends Student{
    //隐式调用父类构无参数构造器,但是父类没有,所以要用super显式调用
    PrimaryStudent(){
    //没有下面的语句会报错
        super("hello");
        System.out.println("PrimaryStudent()");
    }

}
class ExtendsTest{
    public static void main(String[] args) {
        new Person("the shy");
        System.out.println("***********");
        new Student("rookie");
        System.out.println("***********");
        new Student("the shy","rookie");
        System.out.println("***********");
        new PrimaryStudent();
        System.out.println("***********");
    }

}
/*
Person()    the shy
***********
Person()    rookie
一参数Student  rookie
***********
Person()    the shy
一参数Student  the shy
二参数student()    rookie
***********
Person()    hello
一参数Student  hello
PrimaryStudent()
***********
 */
  • this是正在创建的对象,用于调用同一类中重载的构造器,可以参看我之前的文章:Java关键字之this
  • super在调用构造器时,使用方法和this相似。(但super和this本身有本质的不同,super并不是一个对象的引用!!!)
  • super和this语句都必须出现在第一行,也就是说一个构造器中只能有其中之一

子类调用父类构造器

无论是否使用super语句来调用父类构造器的初始化代码,子类构造器总是会事先调用父类构造器!这是一定要记住的!

  • 子类构造器A在第一行显式使用super调用父类构造器B,格式super(参数列表),根据参数列表选择对应的父类构造器。
//父类
 Person(String name){
        System.out.println("Person()\t"+name);
    }
//子类
 Student(String n){
    //super关键字调用父类的构造器
        super(n);
        System.out.println("一参数Student\t"+n);
    }
  • 子类构造器A先用this调用本类重载的构造器B,然后B调用父类构造器。
//父类
 Person(String name){
        System.out.println("Person()\t"+name);
    }
//子类
Student(String n){
    //super关键字调用父类的构造器
        super(n);
        System.out.println("一参数Student\t"+n);
    }
Student(String n,String m){
//this关键字调用同一类中重载的构造器
    this(n);
    System.out.println("二参数student()\t"+m);
}
  • 子类构造器中没有super和this时,系统会隐式调用父类的无参构造器,要是没有无参的,那就报错。
//隐式调用父类构无参数构造器,但是父类没有,所以要用super显式调用
PrimaryStudent(){
//没有下面的语句会报错
    super("hello");
    System.out.println("PrimaryStudent()");
}

综上所述

当调用子类构造器对子类对象进行初始化时,父类构造器总会在子类构造器之前执行。甚至,父类的父类会在父类之前执行……一直追溯到所有类的超类Object类的构造器。

参考书籍:《Thinking in Java》、《疯狂Java讲义》、《Java核心技术卷I》

原文地址:https://www.cnblogs.com/summerday152/p/12041632.html

时间: 2024-08-11 11:59:53

Java继承之再谈构造器的相关文章

Javascript继承,再谈

说到Javascript的继承,相信只要是前端开发者都有所了解或应用,因为这是太基础的知识了.但不知各位有没有深入去理解其中的玄机与奥秘.今本人不才,但也想用自己的理解来说一说这其中的玄机和奥秘. 一.类继承的发展吏 function实现的继承 function的继承是完全模仿了OOP的编程思想.实现的是类的继承 object.create实现的继承 用object.create来修改其原型 es6的继承 增加了class来模拟OOP的继承实现.上述两种继承实现,他都还是支持的. 二.各时期类继

java解惑之再谈String.split()

<span style="font-size:18px;">import java.util.*; public class Sixty{ public static void main(String[] args){ String s = "sur,hs dg,fdg, d ,fd, d,d,dasg,ds"; String[] t = s.split(",\\s*"); System.out.println(s); for(int

【Java基础】--再谈面向对象

面向对象总结 V1.0     2014.9.14 面向对象总结V2.0   2015.8.14 对照之前的J2SE总结,发现现在的似乎更加的注重联系,開始能把细节的东西都编制到知识网络里面,导图的图片真的非常赏心悦目.

再谈 Java 的继承和超类 Object

再来聊聊继承,以及超类 Object. 01.先有继承,后有多态 利用继承,我们可以基于已存在的类构造一个新类.继承的好处在于,子类可以复用父类的非 private 的方法和非 private 成员变量. is-a 是继承的一个明显特征,就是说子类的对象引用类型可以是一个父类.我们可以将通用的方法和成员变量放在父类中,达到代码复用的目的:然后将特殊的方法和成员变量放在子类中,除此之外,子类还可以覆盖父类的方法.这样,子类也就焕发出了新的生命力. 一个对象变量可以引用多种类型的现象被称为多态.多态

Java编程思想(十八) —— 再谈反射

在Java编程思想(十五) -- 类型信息之反射和Java编程思想(十六) -- 联系JVM再谈Class,书上只用了3页就讲完了,还有讲了那么多Class的东西,接下来要从反射中怎么用,自己结合API和其他资料再写多一些. 示例:Test.java public class Test { public Test() {     }      public Test(int i) {         System.out.println(i);     } private void pri()

Java基础——再谈面向对象

去年的这个时候,心血来潮写了篇<简述面向对象技术>,先在看来不由的会想:这都是写的什么跟什么啊?(ps:虽然现在写的博客依然不咋地)但是,Java的学习中又一次不得不再一次面向对象,所以,奉上一篇<再谈面向对象>,做为新年的一盘开胃菜. 面向对象是相对于面向过程而言,是一种思想. 区别于面向过程: 面向过程是以函数为基础,完成各种操作,强调的是过程,而面向对象是以对象为基础,强调的是对象. 比如说把大象装进冰箱分为几步,宋丹丹是这样说的:三步呗, 第一步:打开冰箱门, 第二步:把大

沉淀再出发:再谈java的多线程机制

沉淀再出发:再谈java的多线程机制 一.前言 自从我们学习了操作系统之后,对于其中的线程和进程就有了非常深刻的理解,但是,我们可能在C,C++语言之中尝试过这些机制,并且做过相应的实验,但是对于java的多线程机制以及其中延伸出来的很多概念和相应的实现方式一直都是模棱两可的,虽然后来在面试的时候可能恶补了一些这方面的知识,但是也只是当时记住了,或者了解了一些,等到以后就会变得越来越淡忘了,比如线程的实现方式有两三种,线程池的概念,线程的基本生命周期等等,以及关于线程之间的多并发引起的资源的抢占

再谈angularJS数据绑定机制及背后原理—angularJS常见问题总结

Angular 的数据绑定采用什么机制,详述原理? 脏检查机制.阐释脏检查机制,必须先了解如下问题. 单向绑定(ng-bind) 和 双向绑定(ng-model) 的区别? ng-bind 单向数据绑定($scope -> view),用于数据显示,简写形式是 {{}}. 两者的区别在于页面没有加载完毕 {{val}} 会直接显示到页面,直到 Angular 渲染该绑定数据(这种行为有可能将 {{val}} 让用户看到):而 ng-bind 则是在 Angular 渲染完毕后将数据显示. ng-

java继承2——类与继承(转)

一.你了解类吗? 在Java中,类文件是以.java为后缀的代码文件,在每个类文件中最多只允许出现一个public类,当有public类的时候,类文件的名称必须和public类的名称相同,若不存在public,则类文件的名称可以为任意的名称(当然以数字开头的名称是不允许的). 在类内部,对于成员变量,如果在定义的时候没有进行显示的赋值初始化,则Java会保证类的每个成员变量都得到恰当的初始化: 1)对于  char.short.byte.int.long.float.double等基本数据类型的