声明:本文为牛旦教育原创,所有权保留,转载请注明来源。

一个对象有两件事:状态和行为。 类中的实例变量表示其对象的状态。 实例方法表示其对象的行为。 类的每个对象都保持自己的状态。 创建类的对象时,将为在类中声明的所有实例变量以及在所有级别的祖先中声明所有实例变量分配内存。 比如Employee类声明了一个name实例变量。 创建Employee类的对象时,将为其name实例变量分配内存。Mananger为Employee子类, 创建Manager类的对象时,将为其超类Employee中存在的名称字段分配内存。 毕竟,manager的状态与employee的状态类似。 manager的行为类似于employee。 我们来看一个例子。 考虑两个类,U和V,如下所示:

public class U {private int id;protected String name;}public class V extends U {protected double salary;protected String address;}

图-1描述了创建U类和V类对象时的内存分配。 当创建类U的对象时,仅为在类U中声明的实例变量分配内存。当创建类V的对象时,为类U和类V中的所有实例变量分配内存。

图-1.对象的内存分配包括类的所有实例变量及其所有祖先

让我们来看看本文的主要讨论主题,即构造函数(构造器或构造方法)。

继承和初始化以及构造器调用

重点: 构造函数不是类的成员,它们不会被子类继承。 它们用于初始化实例变量。 创建类的对象时,该对象包含类及其所有祖先的实例变量。 要初始化祖先类的实例变量,必须调用祖先类的构造函数

考虑以下两个类,CSuper和CSub,如清单-1和2所示。 清单-3中的CTest类用于创建CSub类的对象。

清单-1.带有无参数构造函数的CSuper类

 // CSuper.javapackage com.newday.inheritance;public class CSuper {public CSuper() { System.out.println("Inside CSuper() constructor.");}}

清单-2. 一个CSub类,它继承自CSuper类并具有No-Args构造函数

 // CSub.javapackage com.newday.inheritance;public class CSub extends CSuper {public CSub() { System.out.println("Inside CSub() constructor.");}}

清单-3.一个测试类,它演示了当创建类对象时,从类层次结构顶部开始,其所有祖先的构造函数都会被调用

 // CTest.javapackage com.newday.inheritance;public class CTest {public static void main(String[] args) { CSub cs = new CSub();}}

输出结果如下:

Inside CSuper() constructor.

Inside CSub() constructor.

CTest类的输出显示首先调用CSuper类的构造函数,然后调用CSub类的构造函数。 实际上,在CSuper类的构造函数之前调用Object类的构造函数。 你无法打印调用Object类的构造函数的信息,因为Object类不是你的类,因此无法修改它。 问题是,"如何调用CSuper类的构造函数?"这个问题的答案基于这样的规则:当创建类的对象时,为所有实例变量分配内存,包括所有祖先类中的实例变量。 所有类的实例变量必须通过调用其构造函数来初始化。 编译器可以帮助你在很大程度上强制执行此规则。 编译器把对直接祖先类的无参(no-args)构造函数的调用,作为第一个语句添加到类的每个构造函数中。 关键字super用于许多上下文中。 它也指一个类的直接祖先。 如果后跟括号,则指引用超类的构造函数。 如果超类构造函数接受参数,则可以在括号内传递参数列表,类似于方法调用。 以下是调用超类构造函数的示例:

// 调用超类的无参构造器super();// 调用一个字符串参数的超类构造器super("Hello");// 调用带两个double型参数的超类构造器super(10.5, 89.2);

可以显式调用超类的构造函数,也可以让编译器为您调用no-args构造函数。 编译CSuper和CSub类时,编译器会修改其构造函数的代码,如清单4和5所示。

清单20-23. 编译器注入一个super()来调用直接祖先的无参构造函数

 // CSuper.javapackage com.newday.inheritance;public class CSuper {public CSuper() { super(); // 编译器注入 System.out.println("Inside CSuper() constructor.");}}

清单5. 编译器注入一个super()来调用直接祖先的无参构造函数

 // CSub.javapackage com.newday.inheritance;public class CSub extends CSuper {public CSub() { super(); // 编译器注入 System.out.println("Inside CSub() constructor.");}}

▶ 提示:

关键字super指的是类的直接祖先。你可以使用super关键字作为构造函数中的第一个语句来调用超类构造函数。

你还可以将no-args构造函数或超类的任何其他构造函数显式调用为类构造函数中的第一个语句。 仅当你没有显式添加一个时,编译器才会注入无参(no-args)构造函数调用。让我们尝试改进Employee和Manager类。 让我们在Employee类中添加一个构造函数,该类接受雇员的名字作为参数。 然后调用新类Employee2,如清单6所示。

清单6. Employee2类,它是原始Employee类的修改版本并具有接受字符串参数的构造方法

 // Employee2.javapackage com.newday.inheritance;public class Employee2 { private String name = "Unknown"; public Employee2(String name) { this.name = name; } public void setName(String name) { this.name = name; } public String getName() { return name; }}

我们叫新的Manager类为Manager2,它继承自Employee2类。

// Manager2.javapackage com.newday.inheritance;// 不会编译public class Manager2 extends Employee2 { // No code for now}

前面的Manager2类代码无法编译。 它会生成类似以下编译时错误:

Error(4,23): constructor Employee2() not found in class com.newday.inheritance.Employee2

尚未为Manager2类添加任何构造函数。 因此,编译器将为它添加一个no-args构造函数。 它还将尝试注入一个super()调用作为no-args构造函数中的第一个语句,它将调用Employee2类的no-args构造函数。 但是,Employee2类没有no-args构造函数。 这就是你收到上一个错误的原因。 在被编译器修改后的Manager2类的代码看起来如下所示。你可能会注意到super()调用无效,因为Employee2类没有no-args构造函数。

// 编译器注入无参构造函数并调用super()后的Manager2类代码package com.newday.inheritance;// 不会编译public class Manager2 extends Employee2 {// 由编译器注入如下构造器public Manager2() { // 由编译器注入的调用Employee2类不存在的无参构造器 super();}}

那么,你如何修复Manager2类? 有很多方法可以解决它。 一些可以修复Manager2类的方法如下。

A)可以向Employee2类添加no-args构造函数,就像这样:

public class Employee2 {// A no-args constructorpublic Employee2() {}/* 所有其它代码保持不变 */}

在向Employee2类添加no-args构造函数之后,Manager2类的代码将编译正常。

B)可向Manager2类添加一个no-args构造函数,并显式调用带有一个String参数的Employee2类的构造函数,如下所示:

public class Manager2 extends Employee2 {public Manager2() { // 显示调用Employee2类的构造器 super("Unknown");}}

C)可向Manager2类添加构造函数,该类接受String参数并将参数值传递给Employee2类构造函数。 这样,您可以通过将Manager2的name作为参数传递给Employee2构造函数来创建Manager2。

public class Manager2 extends Employee2 {public Manager2(String name) { // 显示调用Employee2构造器 super(name);}}

通常,第三种实现用于使用了Manager的name来创建Manager2类对象方式。 请注意,Manager2类无权访问Employee2类的name实例变量。 但仍然可以使用super关键字从Manager2类初始化Employee2类中的name实例变量,并调用Employee2类的构造函数。 清单20-26包含将要编译的Manager2类的完整代码。 清单20-27包含测试Manager2类的代码,其输出结果它按预期工作。

▶ 提示:

每个类必须直接或间接地从其构造函数中调用其超类的构造函数。 如果超类没有no-args构造函数,则必须显式调用超类的任何其他构造函数,如清单-7所示。

清单-7。具有接受字符串参数的构造函数的Manager2类并显式调用Employee2类的构造函数

 // Manager2.javapackage com.newday.inheritance;public class Manager2 extends Employee2 {public Manager2(String name) { super(name);}}

清单-8.用于测试Manager2类的测试类

 // Manager2Test.javapackage com.newday.inheritance;public class Manager2Test {public static void main(String[] args) { Manager2 mgr = new Manager2("John T"); String name = mgr.getName(); System.out.println("Manager name: " + name);}}

输出结果:

Manager name: John T

我需要讨论一些关于从子类中使用超类的构造函数的规则。考虑以下类X和Y的定义,它们位于两个不同的包中:

// X.javapackage com.newday.inheritance.pkg1;public class X { // X() has package-level access X() { }}// Y.javapackage com.newday.inheritance.pkg2;import com.newday.inheritance.pkg1.X;public class Y extends X { public Y() { }}

Y类的代码无法编译。 它生成编译时错误,如下所示:

Error(7): X() is not public in com.newday.inheritance.pkg1.X; cannot be accessed from outside package

该错误表明类X中的no-args构造函数具有包级访问权限。 因此,无法从类Y中访问它,Y位于不同的包中。 之所以收到此错误,因为编译器将修改类Y的定义,如下所示:

// 编译器修改了类Y的版本// Y.javapackage com.newday.inheritance.pkg2;import com.newday.inheritance.pkg1.X;public class Y extends X {public Y() { //编译器注入了调用X()构造器 super();}}

类X的no-args构造函数具有包级访问权限。 因此,只能从com.newday.inheritance.pkg1包访问它。 你如何修正Y类? 在这种情况下建议解决方案很棘手。 解决方案取决于创建类X和类Y后面使用的设计。但是,要编译类Y,必须为类X创建一个构造函数,它具有public访问权限或受保护访问权限,以便它可以被从Y类访问。

这是伴随继承使用构造函数的另一个规则。 必须使用super关键字从类的构造函数内部显式或隐式调用超类构造函数。 但是,从类中访问超类构造函数是由超类构造函数的访问级别控制的。 有时,类的构造函数的访问级别的结果可能是根本无法访问它。 考虑以下名为NoSubclassingAllowed的类的定义:

public class NoSubclassingAllowed {private NoSubclassingAllowed() {}// 其它相关你代码}

NoSubclassingAllowed类已显式声明了私有构造函数。 无法从包括子类在内的任何地方访问私有构造函数。 对于存在的子类,子类必须能够至少调用其超类的一个构造函数。 这得出结论,NoSubclassingAllowed类不能被任何其他类继承。 这是禁用类继承的方法之一。 以下代码将无法编译,它会尝试子类化NoSubclassingAllowed类,该类没有可访问的构造函数:

// 不会编译,没有可访问的构造函数public class LetUsTryInVain extends NoSubclassingAllowed {}

你可能注意到的一件事是没有人可以创建NoSubclassingAllowed类的对象,因为它的构造函数不能从外部访问。 像这样的类提供了创建其对象并将其返回给调用者的方法。 这也是一种控制和封装类的对象创建的方法。最常见的就是单例模式。

可以查看(掌握好Java中this关键字和方法重载的精要),你可以使用this关键字从同一个类的另一个构造函数中调用类的构造函数,并且调用必须是构造函数体中的第一个语句。 当你查看规则以调用同一个类的另一个构造函数和超类的构造函数时,您会发现两者都声明该调用必须是构造函数体内的第一个语句。 这两个规则的结果是,从一个构造函数中,你可以使用this()来调用同一个类的另一个构造函数,或使用super()来调用超类的构造函数,但不能同时调用它们。 此规则还确保始终只调用一次超类的构造函数

相信你已能完全掌握由于继承而引出的实例变量的初始化和构造函数的调用问题了。

就写到这里了,相信一定对有帮助。分享出去吧。

java的子类调用构造器的顺序_深入剖析Java继承中的初始化与构造器调用关系相关推荐

  1. React中MUI初始化和方法调用

    React中MUI初始化和方法调用 step1:引用 npm install @mui/material @emotion/react @emotion/styled step2:D:\vue\unt ...

  2. fegin调用为什么要序列化_全方位解析Java的序列化

    前言 相信大家日常开发中,经常看到Java对象"implements Serializable".那么,它到底有什么用呢?本文从以下几个角度来解析序列这一块知识点~ 什么是Java ...

  3. 必考题:子类继承父类,初始化以及方法调用顺序

    前言: 最近重投简历,一家公司的笔试题,就是子类继承父类,初始化顺序以及方法的调用顺序,很遗憾,我做错了.所以,记录在这里,希望以后不要再错. 程序: // 父类 public class Paren ...

  4. java虚拟机编译顺序_深入理解Java虚拟机(程序编译与代码优化)

    文章首发于微信公众号:BaronTalk,欢迎关注! 对于性能和效率的追求一直是程序开发中永恒不变的宗旨,除了我们自己在编码过程中要充分考虑代码的性能和效率,虚拟机在编译阶段也会对代码进行优化.本文就 ...

  5. java调用c语言编辑器_如何用java调用c语言编译器实现在线编译c语言?

    要在java中调用c语言的库,需要使用Java提供了JNI. 举例说明 在c语言中定义一个 void sayHello()函数(打印Hello World);然后在Java中调用这个函数显示Hello ...

  6. java初级程序员考试真题_一份Java基础知识笔试题附答案

    如果运行JavaDemo,什么样的信息会打印出来? 1/** 2如果运行JavaDemo,什么样的信息会打印出来? 3*/ 4public class Demo { 5  public static ...

  7. JAVA类加载对字节码的处理_深入理解Java虚拟机(类文件结构+类加载机制+字节码执行引擎)...

    [本文版权归微信公众号"代码艺术"(ID:onblog)所有,若是转载请务必保留本段原创声明,违者必究.若是文章有不足之处,欢迎关注微信公众号私信与我进行交流!] 周志明的< ...

  8. java捕获定时器抛出的异常_详细了解Java中定时器Timer的使用及缺陷分析

    在需要定时并且周期执行任务时,在最初的JAVA工具类库中,Timer可以实现任务的定时周期执行的需求,不过有一定的缺陷,比如,Timer是基于绝对时间而非相对时间,因此Timer对系统时钟比较敏感,本 ...

  9. java 关注公众号没有调接口_深入理解Java继承、封装、多态的实现原理

    点击关注上方"Java技术江湖",设为"置顶或星标",第一时间送达技术干货. 作者:黄小斜 文章来源:微信公众号[Java技术江湖] 目录 从JVM结构开始谈多 ...

最新文章

  1. Python3 异步编程之进程与线程-1
  2. 如何检查字符串是否包含特定单词?
  3. Service Work
  4. 三值的排序 Sorting a Three-Valued Sequence
  5. 暴笑小笑话集----转自通信公社
  6. 20145226夏艺华 JAVA预备作业1
  7. 【组播技术入门 01】IP组播概述
  8. android的绝对布局,Android布局之绝对布局AbsoluteLayout详解
  9. Nik Collection 4
  10. 从嗤之以鼻到“奇迹” 前淘宝工程师详解12306技术
  11. 苹果4.3该如何避免?机审人审怎么过?
  12. 高仿知乎Android客户端欢迎引导动画
  13. 对数计算的实现方式(在windows计算器、OneNote、Python和Java中)
  14. ONE~~~~~~~~~
  15. ps更换证件照的背景色
  16. BIM正向设计与传统设计的区别有哪些
  17. 2008年专转本计算机试卷解,2005-2008年专转本计算机真题及答案
  18. 智能家居主要实现了什么功能,提供了哪些基本服务?
  19. 微商城分销系统:微商运营管理系统搭建介绍
  20. 实战|用Python制作邮箱自动回复机器人

热门文章

  1. 渲染完毕再渲染数据_三星Galaxy S21渲染图再曝:多种配色、新设计
  2. Android系统开发智能机器人,Android智能机器人详解
  3. python编写函数showmsg(n、name)_Python语言答案
  4. 无法解析 uafxcw.lib_吉利DMS系统一周热点问题解析
  5. ios项目中使用gcd的场景_Redis在PHP项目中的实际应用场景
  6. 使用Spring Boot Actuator 监控程序运行状态
  7. python使用布隆过滤器筛选数据
  8. selenium控制浏览器切换页面
  9. joblib多线程、多进程学习案例(一)——一步步写多进程任务
  10. android获得textview数值,android-如何获取textview中的文本语言?