Fork me on GitHub

类与实例的初始化顺序

在Java中,static修饰符一直都是很重要的知识点,特别是当它修饰为一个静态代码块的时候,还有它被用来修饰变量的时候,当发生继承关系或者是内部类关系的时候,我们这里分几种情况进行讨论

当发生继承关系的时候

如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public  class SuperClass {
private static int superInt = 12345;
static {
System.out.println("Super Class init! superInt = " + superInt);
}

{
System.out.println("Super Class non-static init!");
}

public SuperClass() {
System.out.println("SuperClass Build Method work!");
}

public static final void show() {
System.out.println("SuperClass show()");
}

public void doIt() {
System.out.println("SuperClass do it!");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public final class SonClass extends SuperClass {
private static int sonInt = 987654321;
static {
System.out.println("Son Class static init! sonInt = " + sonInt);
}

{
System.out.println("Son Class non-static init!");
}

public SonClass() {
System.out.println("SonClass Build Method work!");
}

public static final void showT() {
System.out.println("SonClass show()");
}

@Override
public void doIt() {
System.out.println("SonClass do it!");
}
}

在这里定义了一个静态函数,而静态函数是属于类的,而并不属于示例,我们可以通过一下两个方式验证

  1. 当使用静态函数的时候
1
2
3
public static void main(String[] args) {
SonClass.showT();
}

结果为

捕获0.PNG

  1. 当new对象的时候
1
2
3
4
public static void main(String[] args) {
SonClass sonClass = new SonClass();
sonClass.doIt();
}

结果为

捕获1.PNG

  1. 两种情况全部存在的时候
1
2
3
4
5
public static void main(String[] args) {
SonClass.showT();
SonClass sonClass = new SonClass();
sonClass.doIt();
}

结果为

捕获2.PNG

结论

当使用静态函数的时候,先初始化父类的静态变量,然后执行父类的静态代码块,然后再是子类的静态变量,静态代码块。当我们构造出一个对象的时候,也是先进行类的初始化,同上,在初始化完静态变量后,先是执行父类的非静态变量,然后是非静态代码块,最后才是父类的构造函数,然后是子类的这个过程,具体如下:

  1. 父类的静态变量及静态代码块
  2. 子类的静态变量及静态代码块
  3. 父类的非静态变量、非静态代码块、构造函数
  4. 子类的非静态变量、非静态代码块、构造函数

这里我们还可以得出的结论有

类的初始化只有一次,若实例化对象的时候,类已经进行了初始化,则不需要再重复进行

还有静态常量等在程序初始化的时候就已经初始化了,这是必须知道的。

静态内部类与非静态内部类

静态内部类如下:

1
2
3
4
5
6
7
8
9
10
public static final class SonInner {
private static int sonInnerInt = 5;
static {
System.out.println("SonInner Class init! sonInnerInt = " + sonInnerInt);
}

public static final void showI() {
System.out.println("SonInner Class show()");
}
}

这个类是在SonClass中的,我们可以实验一下

1
2
3
public static void main(String[] args) {
SonClass.SonInner.showI();
}

结果为:

捕获3.PNG

结论

静态内部类的初始化不依赖外围类的初始化。

若是非静态内部类,则会发现在内部类中不能使用静态代码块与静态函数,只能先创建外围类再创建内部类

这时的初始化过程如下:

  1. 先按照父类子类的顺序初始化外围类
  2. 初始化内部类的非静态变量、非静态代码块

最后还需要注意的是,我们在静态函数中只需要定义函数是static的,不需要再加final关键字修饰