Java初始化顺序

成员变量的初始化顺序

  • 实例变量在声明处初始化
  • 实例变量在实例初始化块中初始化
  • 实例变量在构造器中初始化
  • 静态变量在声明处初始化
  • 静态变量在静态初始块中初始化

将5种初始化代码无规则的分布在程序中,观察执行的顺序

 1 public class InitOrderTest {
 2 
 3     public InitOrderTest(int x, int y) {
 4         System.out.println("执行构造器···");
 5         this.x = x;
 6         this.y = y;
 7     }
 8 
 9     {
10         System.out.println("执行实例初始化块1···");
11     }
12     private int x;
13     private int y = initY();
14 
15     {
16         System.out.println("执行实例初始化块2···");
17         x = initX();
18     }
19 
20     static {
21         System.out.println("执行静态初始化块···");
22     }
23 
24     private static int staticX;
25     private static int staticY = initStaticY();
26 
27     static {
28         System.out.println("执行静态初始化块2···");
29         staticX = initStaticX();
30     }
31 
32     public int initX() {
33         System.out.println("执行实例变量x初始化···");
34         return 1;
35     }
36 
37     public int initY() {
38         System.out.println("执行实例变量y初始化···");
39         return 1;
40     }
41 
42     public static int initStaticX() {
43         System.out.println("执行静态变量staticX初始化···");
44         return 1;
45     }
46 
47     public static int initStaticY() {
48         System.out.println("执行静态变量staticY初始化···");
49         return 1;
50     }
51 
52     public void show() {
53         System.out.printf("x=%d,y=%d,staticX=%d,staticY=%d
", x, y, staticX,
54                 staticY);
55     }
56 
57     public static void main(String[] args) {
58 
59         System.out.println("执行main方法···");
60         InitOrderTest iot1 = new InitOrderTest(10, 15);
61         InitOrderTest iot2 = new InitOrderTest(20, 25);
62         iot1.show();
63         iot2.show();
64 
65     }
66 
67 }

运行结果:

分析结果:

1. 当运行InitOrderTest类时,Java虚拟机试图调用该类的main方法时,JVM发现该类尚未加载,因此会首先加载该类,并会执行链接,在链接阶段会为该类的静态方法分配空间,并置为默认值,然后按照初始化在类中的顺序执行静态初始化(只执行一次)。

2. 静态成员的初始化执行完成后,就会执行main方法,创建InitOrderTest类的对象,使用new在堆中为对象分配空间,这时所有的实例变量都设置为默认值。然后执行实例初始化。3种实例初始化方式,首先会按照实例变量在类中声明的初始化顺序执行(这里的顺序指的是初始化在类中声明的顺序,而不是变量在类中声明的顺序),然后执行构造器。

3. 再次创建InitOrderTest类的对象时,不再执行静态初始化。但是每次创建一个对象都要执行一次实例变量初始化。

继承下的初始化

 1 public class SubInitOrderTest extends InitOrderTest {
 2 
 3     private int z = initZ();
 4     private static int staticZ = initStaticZ();
 5 
 6     public SubInitOrderTest(int x, int y, int z) {
 7         super(x, y);
 8         System.out.println("执行子类构造器···");
 9         this.z = z;
10     }
11 
12     public int initZ() {
13         System.out.println("执行子类实例变量z初始化···");
14         return 1;
15     }
16 
17     public static int initStaticZ() {
18         System.out.println("执行子类静态变量staticZ初始化···");
19         return 1;
20     }
21 
22     public static void main(String[] args) {
23 
24         new SubInitOrderTest(1, 2, 3);
25     }
26 
27 }

运行结果:

分析执行过程:

1. JVM调用子类的main首先加载、链接并尝试初始化子类,而子类继承了父类,所以在初始化子类之前,需要加载、链接并初始化父类,这是一个递归的过程,直到Object类。所以会首先执行父类中静态变量staticX与staticY和静态初始化块。

2. 当父类与子类静态初始化完成后,执行子类的main方法,创建子类的对象。当子类对象创建时,就会执行实例初始化,而子类继承了父类,因此,首先会执行父类的实例初始化,这也是一个递归的过程,直到object类为止。因而父类的实例初始化会在子类的实例初始化之前完成。即首先执行父类中的实例变量x与y,还有两个实例初始化块与构造器,然后执行子类中实例变量z、实例初始化块与构造器。

总结:

  • 初始化的顺序可以简单的总结为先静态,后实例,先父类,后子类。对于静态初始化,按照静态变量声明处初始化与静态初始化块在类中出现的顺序执行。对于实例初始化,按照实例变量声明处初始化与实例初始化块在类中出现的顺序执行,然后执行构造器。
  • 当心潜在的向前引用,如果使用一个尚未初始化值的变量值,就可能得到错误的结果
  • 在构造器中不要调用可由子类重写的方法,调用private与final的方法才是安全的。
  • 对于值为编译时常量的final变量,可以认为这样的变量会最先得到初始化。在程序中无法观察到其默认值,即使向前引用这种类型的变量也是如此。
原文地址:https://www.cnblogs.com/lahblogs/p/4357612.html