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类的构造器。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • java为何不能多继承的原因详解

    多继承指一个子类能同时继承于多个父类,从而同时拥有多个父类的特征,但缺点是显著的. 1.若子类继承的父类中拥有相同的成员变量,子类在引用该变量时将无法判别使用哪个父类的成员变量. 如: public class classA // 父类 classA { private int num = 0; } public class classB // 父类 classB { private int num = 1; } public class classC extends classA,classB

  • 详解java为什么不允许类多重继承却允许接口多重继承

    首先看下面这一段代码: interface a{ void b(); } interface a1 extends a{ void b(); } interface a2 extends a{ void b(); } interface a12 extends a1,a2{ void b(); } public class Main { public static void main(String args[]){ } } 上面a1和a2都继承了接口a,都获得了b方法的定义.然后a12又多重继承

  • Java私有构造器使用方法示例

    构造器作为Java类的一个特殊的成员同样可以设置关键字来控制其访问权限.在大多数情况下,我们一般把构造器设置为公有成员,即public的,在默认情况下,如果不写任何关键字,其访问权限也是public.这样,在我们新建一个类的对象的时候,构造器会创建对象后被自动调用,发生在其他类成员被设置为默认初始值之后,当然如果有字段初始器和初始化块的话,构造器的调用会发生在此之后.构造器的主要作用是在new将对象的引用返回之前初始化对象. 言归正传,什么叫私有构造器.所谓私有构造器,就是用private关键字

  • java的继承原理与实现方法详解

    本文实例讲述了java的继承原理与实现方法.分享给大家供大家参考,具体如下: 继承 1.java中是单继承的.每个子类只有一个父类. 语法:子类 extends 父类 2.在java中,即使没有声明父类,也有一个隐含的父类,就是Object类 3.在子类中可以使用super来调用父类的方法 4.继承中的构造方法问题 在new一个子类实例的过程中,会优先自动调用父类默认的无参数构造方法,然后再调用子类的构造方法.如果父类没有默认的构造方法,只有带参数的构造方法,此时就会出错. 除了由jvm自动调用

  • 实例解析Java中的构造器初始化

    1.初始化顺序 当Java创建一个对象时,系统先为该对象的所有实例属性分配内存(前提是该类已经被加载过了),接着程序开始对这些实例属性执行初始化,其初始化顺序是:先执行初始化块或声明属性时制定的初始值,再执行构造器里制定的初始值. 在类的内部,变量定义的先后顺序决定了初始化的顺序,即时变量散布于方法定义之间,它们仍就会在任何方法(包括构造器)被调用之前得到初始化. class Window { Window(int maker) { System.out.println("Window(&quo

  • Java接口继承和使用接口操作示例

    本文实例讲述了Java接口继承和使用接口操作.分享给大家供大家参考,具体如下: 一 接口的继承 1 点睛 接口支持多继承,一个接口可以有多个父接口,子接口扩展某个父接口,将会获得父接口里定义的所有抽象方法.常量. 一个接口继承多个父接口时,多个父接口排在extends关键字之后. 2 代码 interface interfaceA { int PROP_A = 5; void testA(); } interface interfaceB { int PROP_B = 6; void testB

  • Java基础教程之构造器与方法重载

    在方法与数据成员中,我们提到,Java中的对象在创建的时候会初始化(initialization).初始化时,对象的数据成员被赋予初始值.我们可以显式初始化.如果我们没有给数据成员赋予初始值,数据成员会根据其类型采用默认初始值. 显式初始化要求我们在写程序时就确定初始值,这有时很不方便.我们可以使用构造器(constructor)来初始化对象.构造器可以初始化数据成员,还可以规定特定的操作.这些操作会在创建对象时自动执行. 定义构造器 构造器是一个方法.像普通方法一样,我们在类中定义构造器.构造

  • Java构造器使用方法及注意事项

    Java构造器使用方法及注意事项 超类的构造器在子类的构造器运行之前运行,也就是说,在创建对象时,先运行超类中的构造器,然后再运行子类中的构造器,此时,如果在执行超类构造器的时候,构造器执行了可覆盖的方法,那么就会去调用子类中的该方法,而此时子类还未被实例化,此时就有可能出问题. 以一个例子说明: public class Super { int age = 10; protected void say() { System.out.println("super"); } public

  • Java继承构造器使用过程解析

    这篇文章主要介绍了Java继承构造器使用过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 初始化基类 前面提到,继承是子类对父类的拓展.<Thinking in Java>中提到下面一段话: 当创建一个导出类的对象时,该对象包含了一个基类的子对象.这个子对象与你用基类直接创建的对象是一样的.二者区别在于,后者来自于外部,而基类的子对象被包装在导出类的对象内部. 我们在创建子类对象时,调用了父类的构造器,甚至父类的父类构造器.我们知道,构

  • 原生Java操作mysql数据库过程解析

    这篇文章主要介绍了原生Java操作mysql数据库过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 1.引入数据库驱动的jar包 以通过maven引入mysql driver为例 1.1 到http://mvnrepository.com 搜索 mysql 1.2 复制所需maven配置文件到工程的 pom.xml <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-

  • 通过Java实现bash命令过程解析

    这篇文章主要介绍了通过Java实现bash命令过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 1.BASH 命令简介 Bash,Unix shell的一种,在1987年由布莱恩·福克斯为了GNU计划而编写.1989年发布第一个正式版本,原先是计划用在GNU操作系统上,但能运行于大多数类Unix系统的操作系统之上,包括Linux与Mac OS X v10.4都将它作为默认shell. Bash是Bourne shell的后继兼容版本与开放

  • JAVA如何定义构造函数过程解析

    这篇文章主要介绍了JAVA如何定义构造函数过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 构造函数在类里面定义,构造函数名一定要跟类名相同,实例化一个对象的时候,如果没有初始化成员变量,可以不用定义构造函数,系统会自己定义好,定义了也不会影响.如果实例化对象需要初始化成员变量就一定自定义构造函数. 定义构造函数 class Cat { String name; int age; //可定义可不定义,不定义时实例化一个对象会自动定义这个构

  • 如何通过Java实现时间轴过程解析

    这篇文章主要介绍了如何通过Java实现时间轴过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 1.需要添加FastJson的依赖处理数据. <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.47</version> </depen

  • 如何基于java实现Gauss消元法过程解析

    补充知识: 正定矩阵 奇异矩阵 严格对角占优 要理解Gauss消去法,首先来看一个例子: 从上例子可以看出,高斯消去法实际上就是我们初中学的阶二元一次方程组,只不过那里的未知数个数$n=2$ $n>2$时,Gauss消去法的思路实际上和解二元一次方程组是一样的,方法如下: 将n方程组中的n−1个方程通过消元,形成一个与原方程组等价的一个新方程组,新方程组中的n−1个方程仅包含n−1个未知数. 故问题就转化为了求解n−1元的方程组,这样我们可以继续消元,以次类推,直到最后一个方程组为一元一次方程组

  • 使用maven插件对java工程进行打包过程解析

    现在基本上都是采用maven来进行开发管理,我有一个需求是需要把通过maven管理的java工程打成可执行的jar包,这样也就是说必需把工程依赖的jar包也一起打包.而使用maven默认的package命令构建的jar包中只包括了工程自身的class文件,并没有包括依赖的jar包.我们可以通过配置插件来对工程进行打包,pom具体配置如下: maven-assembly-plugin <plugin> <artifactId>maven-assembly-plugin</art

  • Java 图片复制功能实现过程解析

    需求说明 实现思路 见代码注释 代码内容 使用IO复制图片 import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; /** * @auther::9527 * @Description: 第十题 * @program: 多线程 * @create: 2019-08-10 00:26 */ pu

  • MAC配置java+jmeter环境变量过程解析

    打开ITerm终端 进入命令输入,sudo su,输入密码 创建.bash_profile文件touch .bash_profile 打开.bash_profile文件open .bash_profile 弹出文本编辑 export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk-14.0.1.jdk/Contents/Home export PATH=$JAVA_HOME/bin:$PATH export CLASSPATH=.:$JAVA_HO

  • 基于Java信号量解决死锁过程解析

    死锁在多线程的情况下,会出现数据不同步情况, 而为了避免这种情况,之前也说了:界区实现方法有两种,一种是用synchronized,一种是用Lock显式锁实现. 而如果不恰当的使用了锁,且出现同时要锁多个对象时,会出现死锁情况,如下: package lockTest; import java.util.Date; /** * 崔素强 * @author cuisuqiang@163.com */ public class LockTest { public static String obj1

随机推荐