Java学习-类和对象的初始化顺序

目标

  • 从阿里巴巴笔试题看Java加载顺序

static

  • 公有属性,可以由类名称直接访问
  • 静态方法,可以由类名称直接调用方法
  • 静态方法或属性,可以在对象实例化之前就调用

阿里巴巴笔试题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class Test0 {
public static int k = 0;
public static Test0 t1 = new Test0("t1");
public static Test0 t2 = new Test0("t2");
public static int i = print("i");
public static int n = 99;
public int j = print("j");
{
print("构造块");
}
static {
print("静态块");
}
public Test0(String str) {
System.out.println((++k) + ":" + str + " i=" + i + " n=" + n);
++i;
++n;
}
private static int print(String str) {
System.out.println((++k) + ":" + str + " i=" + i + " n=" + n);
++n;
return ++i;
}
public static void main(String[] args) {
Test0 t = new Test0("init");
}
}

运行结果如下:

1
2
3
4
5
6
7
8
9
10
11
1:j i=0 n=0
2:构造快 i=1 n=1
3:t1 i=2 n=2
4:j i=3 n=3
5:构造快 i=4 n=4
6:t2 i=5 n=5
7:i i=6 n=6
8:静态块 i=7 n=99
9:j i=8 n=100
10:构造快 i=9 n=101
11:init i=10 n=102

关键点

关键点:(代码注释上已给出)

  • 先初始化静态成员k=0,k = 0;t1 = new Test1(“t1”); t2 = new Test1(“t2”);i = print(“i”); int n = 99;,所以应该是先k=0,
  • 接着初始化t1,t1的初始化是调用构造方法,所以先初始化对象,而初始化对象就像上面的说明先初始化实例成员j和代码块print(“构造块”);,此时k=0;++k后k=1,i和n的初始值都为0,因此初始化j的时候打印”1:j i=0 n=0”,接着++i;++n;后i和n的值都为1,初始化代码块print(“构造块”)的时候打印”2:构造块 i=1 n=1”;
  • 后面的静态成员和实例成员的初始化跟上面步骤1,2类似,自己推导一下就能得到结果;

总结

几大原则

一、静态成员变量(static)

  • 静态成员变量为类变量,所有对象共享同一内存空间
  • 静态成员变量的声明和定义仅在首次加载时执行一次
  • 手机加载类时首先对所有静态成员变量根据类型默认赋初值,然后再对有右值的赋右值

二、静态初始块

  • 多个静态成员变量与静态初始化块参照出现顺序先后执行

三、动态成员变量

  • 动态成员变量定义,在每次实例化对象时,在构造函数之前执行

四、动态初始化块

  • 动态初始化块的执行是在每次实例化对象时,在构造函数之前执行
  • 多个动态成员变量与动态初始化块参照出现顺序先后执行

总结:总的来说,在不涉及继承的前提下,当首次加载类时,按照如下顺序执行

  • 按照出现顺序先后执行静态成员变量定义与静态初始化块
  • 按照出现顺序先后执行动态成员变量定义与动态初始化块
  • 执行构造函数
  • 再次实例化对象时只执行第2、3步即可

五、当涉及到继承时,按如下顺序执行

  • 执行父类的静态成员变量定义与静态初始化块,执行子类的静态成员变量定义与静态初始化块
  • 执行父类的非静态成员变量定义与动态初始化块,执行父类构造方法
  • 执行子类的非静态成员变量定义与动态初始化块,执行子类构造方法

另:父类构造方法中用到的方法如果已被子类重写,那么在构造子类对象时在调用父类构造函数中使用子类重写的方法。

More info: 更多