首先抛出一段代码,来看看执行结果是什么吧:

 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出来的,是不需要加载这个类的,因为它在编译器就已经放入常量池了