java类的初始化顺序
首先抛出一段代码,来看看执行结果是什么吧:
1 public class SuperClass {
2
3 static {
4 System.out.println("super static area");
5 cal();
6 }
7
8 public static int k = 5;
9
10
11 public int kk = call();
12
13 {
14 System.out.println("super area");
15 }
16
17 public SuperClass() {
18 System.out.println("super construct ");
19 }
20
21 public static int cal() {
22 System.out.println("super static member + k = "+k);
23 return 2;
24 }
25
26 public int call() {
27 System.out.println("super member + kk = "+kk);
28 return 3;
29 }
30
31 public static void main (String[] args) {
32 SuperClass sc = new SuperClass();
33 }
34 }
一开始看到这个代码我是崩溃的,到底应该是啥呢!
这里给出结果:
super static area
super static member + k = 0
super member + kk = 0
super area
super construct
- 那么显然,作为一个类的static成员,无论是成员变量还是static代码块,其初始化都是先于类的实例的(有一种情况除外,等下说)
- 并且static成员都是按照我们代码的顺序进行初始化的,这就是为什么第二行的输出结果k = 0, 因为在执行static代码块时, k还没有被赋值为5, 只有一个默认值0
- 在初始化一个实例的过程中, 首先会初始化类的static成员,然后再初始化这个实例的成员和代码块,也是按照顺序来的
- 构造函数才是最后调用的
上面的理解了,我们再来看看这个玩意儿:
1 public class SubClass extends SuperClass {
2
3 static {
4 System.out.println("sub static area");
5 cal();
6 }
7
8 public static int k = 5;
9
10 public int kk = call();
11
12 {
13 System.out.println("sub area");
14 }
15
16 public SubClass() {
17 System.out.println("sub construct ");
18 }
19
20 public static int cal() {
21 System.out.println("sub static member + k = "+k);
22 return 3;
23 }
24
25 public int call() {
26 System.out.println("sub member + kk = "+kk);
27 return 4;
28 }
29
30 public static void main(String[] args) {
31 SubClass sc = new SubClass();
32 }
33 }
这下蒙逼了没?来看结果:
super static area
super static member + k = 0
sub static area
sub static member + k = 0
sub member + kk = 0
super area
super construct
sub member + kk = 0
sub area
sub construct
乍一看上去很清楚:
* 初始化父类static成员
* 初始化子类static成员
* 初始化父类实例(成员变量及代码块依次初始化,最后是构造函数)
* 初始化子类实例(同上)
但是发现没有,父类的成员变量调用call()的时候,竟然去调用了子类的call()方法,这是因为子类对这个方法进行了重写
那么问题又来了,为什么cal()没有被重写呢?stackoverflow帮我解答了:
the point of polymorphism is that you can subclass a class and the objects implementing those subclasses will have different behaviors for the same methods defined in the superclass (and overridden in the subclasses). A static method is not associated with any instance of a class so the concept is not applicable.
再回到SuperClass中:
1 public class SuperClass {
2
3 static {
4 System.out.println("super static area");
5 }
6
7 public static SuperClass ss = new SuperClass();
8
9
10 public static int k = cal();
11
12 public int kk = call();
13
14 {
15 System.out.println("super area");
16 }
17
18 public SuperClass() {
19 System.out.println("super construct ");
20 }
21
22 public static int cal() {
23 System.out.println("super static member + k = "+k);
24 return 2;
25 }
26
27 public int call() {
28 System.out.println("super member + kk = "+kk);
29 return 3;
30 }
31 public static void main(String[] args) {
32 }
33
34 }
注意代码第7行, 这里创建了一个静态实例,那又会怎么样呢?
super static area
super member + kk = 0
super area
super construct
super static member + k = 0
我们看到, 在static成员初始化的中间插入了实例的初始化过程,这就说明,实例的初始化并不一定需要类初始化完毕才能进行,也就是类的初始化和实例的初始化没有必然的联系,只不过是jvm在创建实例的时候,会先去帮我们把类初始化仅此而已
不过有两点是可以确定的:
1. 子类初始化前要进行父类的初始化
2. 子类实例初始化前要进行父类实例的初始化
关于类的初始化,有一个比较好的用法是单例:
1 class Singleton{
2 private static class Holder{
3 private final static Singleton INSTANCE=new Singleton();
4 }
5 private Singleton(){}
6 public static Singleton getInstance(){
7 return Holder.INSTANCE;
8 }
9 }
这样既能保证唯一的初始化,又可以不使用synchronized以提升效率
第一篇文章就先到这里,平时不擅长码字,可能有一些东西说不清楚,我相信越写越多以后肯定会有改善的
## One more thing what about final?
1 static {
2 System.out.println("super static area");
3 cal();
4 }
5
6 public final static String k = new String("hehe");
7 public final static String kk = "hehe";
8
9 public static void cal() {
10 System.out.println("k = "+k);
11 System.out.println("kk = "+kk);
12
13 }
14
15 public static void main(String[] args) {
16 }
super static area
k = null
kk = hehe
可以看到,当我们使用字面量的时候, 在编译器就会放入常量池中, 而new操作永远是在运行时才会做的, 如果想要把新new出来的String放入常量池, 可以考虑使用intern()方法
最后当调用某个类用final static修饰的字面量时,注意是字面量,不是new出来的,是不需要加载这个类的,因为它在编译器就已经放入常量池了