一、构造方法与代码块的先后顺序

为了验证Java 中类的实例化顺序,我们需要有三个类:父类、子类、测试类。三者的定义具体请看下文。

1.1 构造方法

我们先来测试子类初始化时子类和父类的实例化顺序,具体代码如下:

class A {
A(){
 System.out.println("构造方法【A】");
}
}
class B extends A {
B(){
 super();
 System.out.println("构造方法【B】");
}
}
public class Test {
public static void main(String[] args) {
 System.out.println("=======================第一次实例化=======================");
 A a = new B();
 System.out.println("=======================第二次实例化=======================");
 a = new B();
 System.out.println("=======================方法执行结束=======================");
}
}

运行结果如下:
由浅入深详解Java 类的实例化顺序_Java
由上述代码的运行结果可以得知:【父类的构造方法先于子类的构造方法执行】。

1.2 构造方法与普通代码块

接下来,我们进一步测试构造方法与普通代码块的先后顺序,具体代码如下:

class A {
A(){
 System.out.println("构造方法【A】");
}
{
 System.out.println("普通代码块【A】");
}
}

class B extends A {
B(){
 super();
 System.out.println("构造方法【B】");
}
{
 System.out.println("普通代码块【B】");
}
}

运行结果如下:
由浅入深详解Java 类的实例化顺序_Java_02
由上述代码的运行结果可以得知:【普通代码块先于构造方法执行】。

1.3 构造方法与代码块

代码块分为两种:一为普通代码块,一为静态代码块。
由上一步我们已经知道了普通代码块是先于构造方法执行的,那么静态代码块呢?

class A {
A(){
 System.out.println("构造方法【A】");
}
{
 System.out.println("普通代码块【A】");
}
static {
 System.out.println("static代码块【A】");
}
}
class B extends A {
B(){
 super();
 System.out.println("构造方法【B】");
}
{
 System.out.println("普通代码块【B】");
}
static {
 System.out.println("static代码块【B】");
}
}

运行结果如下:
由浅入深详解Java 类的实例化顺序_Java_03
由上图可以得知:【静态代码块先于普通代码块执行,静态代码块只会被执行一次】。

1.4 总结

总结:在子类初始化过程中,

  1. 依次执行父类到子类的静态代码块

  2. 执行父类的普通代码块

  3. 执行父类的构造方法

  4. 执行子类的普通代码块

  5. 执行子类的构造方法

二、变量的先后顺序

在类的内部,变量分为两种:成员变量以及静态变量。这两者在类对象初始化的过程中是按照什么样的顺序被进行赋值的呢?

class C {
public static String str00=printString("static变量str00【C】");
public String str0=printString("成员变量str0【C】");  
public static String str10=printString("static变量str10【C】");

public static String printString(String str) {
       System.out.println(str);
       return null;
   }
}
class D extends C {
public static String str00=printString("static变量str00【D】");
public String str0=printString("成员变量str0【D】");
public static String str10=printString("static变量str10【D】");
}
public class Test2 {
public static void main(String[] Crgs) {
 System.out.println("=======================第一次实例化=======================");
 C C = new D();
 System.out.println("=======================方法执行结束=======================");
}
}

运行结果如下:
由浅入深详解Java 类的实例化顺序_Java_04
由上图结果可以得知:【类内静态变量先于成员变量被赋值,静态变量按代码的先后顺序被初始化】。

三、变量、构造方法与代码块

经由前面的两部分,我们已经分别清楚了构造方法与代码块的先后执行顺序和变量间的赋值顺序。
但是,当这两部分在同一个类内部时,它们的先后顺序又是如何的呢?

3.1 变量与构造方法

由简单到复杂,我们先来看看变量与构造方法之间的先后顺序。具体代码如下:

class C {
public static String str00=printString("static变量str00【C】");
public String str0=printString("成员变量str0【C】");  
C(){
 System.out.println("构造方法【C】");
}
public String str1=printString("成员变量str1【C】");
public static String str10=printString("static变量str10【C】");

public static String printString(String str) {
       System.out.println(str);
       return null;
   }
}
class D extends C {
public static String str00=printString("static变量str00【D】");
public String str0=printString("成员变量str0【D】");
D(){
 System.out.println("构造方法【D】");
}
public String str1=printString("成员变量str1【D】");
public static String str10=printString("static变量str10【D】");
}

运行结果如下:
由浅入深详解Java 类的实例化顺序_Java_05
由上述代码的运行结果可以得知:【父类和子类的静态变量首先被赋值,其后是父类的成员变量和构造方法,最后才是子类的成员变量和构造方法】。

3.2 普通代码块和变量

由【1.2 构造方法与普通代码块】已知普通代码块先于构造方法被执行,
再由上一步得知变量先于构造方法被执行,
那么普通代码块和变量之间的先后顺序又是如何的呢?

class C {
public static String str00=printString("static变量str00【C】");
public String str0=printString("成员变量str0【C】");  
C(){
 System.out.println("构造方法【C】");
}
{
 System.out.println("普通代码块【C】");
}
public String str1=printString("成员变量str1【C】");
public static String str10=printString("static变量str10【C】");

public static String printString(String str) {
       System.out.println(str);
       return null;
   }
}
class D extends C {
public static String str00=printString("static变量str00【D】");
public String str0=printString("成员变量str0【D】");
D(){
 System.out.println("构造方法【D】");
}
{
 System.out.println("普通代码块【D】");
}
public String str1=printString("成员变量str1【D】");
public static String str10=printString("static变量str10【D】");
}

运行结果如下:
由浅入深详解Java 类的实例化顺序_Java_06
由上图结果可以得知:【父类和子类的静态变量先后被初始化,其后顺序赋值成员变量或执行普通代码块,最后才执行构造方法】。

3.3 静态代码块和静态变量

已知静态代码块先于普通代码块和构造方法执行,
又由上一步知静态变量先于成员变量或普通代码块被赋值,
那么静态代码块和静态变量的先后顺序又是如何的呢?具体代码如下:

class C {
public static String str00=printString("static变量str00【C】");
public String str0=printString("成员变量str0【C】");  
C(){
 System.out.println("构造方法【C】");
}
{
 System.out.println("普通代码块【D】");
}
static {
 System.out.println("static代码块【C】");
}
public String str1=printString("成员变量str1【C】");
public static String str10=printString("static变量str10【C】");

public static String printString(String str) {
       System.out.println(str);
       return null;
   }
}
class D extends C {
public static String str00=printString("static变量str00【D】");
public String str0=printString("成员变量str0【D】");
D(){
 System.out.println("构造方法【D】");
}
{
 System.out.println("普通代码块【D】");
}
static {
 System.out.println("static代码块【C】");
}
public String str1=printString("成员变量str1【D】");
public static String str10=printString("static变量str10【D】");
}

运行结果如下:
由浅入深详解Java 类的实例化顺序_Java_07
由上述代码的执行结果可以得知:【父类和子类的静态变量和静态代码块首先被执行,其后才是成员变量/普通代码块,最后执行构造方法;静态变量和静态代码块按照代码的先后顺序被依次执行,并无明确的先后顺序】。

四、总结

在子类对象被实例化的过程中,变量、构造方法以及代码块三者的先后顺序为:

  1. 父类的静态变量和静态代码块,按代码先后顺序执行

  2. 子类的静态变量和静态代码块,按代码先后顺序执行

  3. 父类的成员变量和普通代码块,按代码先后顺序执行

  4. 父类的构造方法

  5. 子类的成员变量和普通代码块,按代码先后顺序执行

  6. 子类的构造方法
    其中,静态代码块只会被执行一次,不会因为多次new 而再次执行。

至此,本文结束。我是陈冰安,一个Java学习者。欢迎关注我的公众号【暗星涌动】,愿与你一同进步。