java类的初始化顺序

类的生命周期是:

加载-->验证-->准备-->解析-->初始化-->使用-->销毁。只有在准备阶段和初始化阶段才会涉及类变量的初始化和赋值。

类的准备阶段需要做为类变量分配内存并设置默认值,因此类变量为null或者0。如果类变量是final,编译时javac会为value生成ConstantValue属性,在准备阶段虚拟机就会根据ConstantValue的设置将被变量设置为指定的值,不再是0或者null。

一般情况顺序如下:

1.父类的静态变量赋值

2.子类的静态变量赋值

3.父类成员变量赋值和父类块赋值

4.父类构造函数赋值

5.子类成员变量赋值和子类块赋值

6.子类构造函数赋值

注:如果父类中调用的方法子类重写了,那么调用子类的方法。

 

编译器会自动收集类中的所有静态变量(类变量)和静态语句块(static{}块)中的语句合并产生的,编译器收集的顺序是根据语句在java代码中的顺序决定的。收集完成之后,会编译成java类的 static{} 方法,值得说明的是,如果我们的java类中,没有显式声明static{}块,如果类中有静态变量,编译器会默认给我们生成一个static{}方法。

Java收集我们的实例变量赋值语句,合并后在构造函数中执行赋值语句。没有构造函数的,系统会默认给我们生成构造函数。

例子1:

class Father{
   static{
          System.out.println("父类静态代码块初始化" );
   }

   {
          System.out.println("父类代码块初始化" );
   }

   private static String s=print();

   public static String print(){
          System.out.println("父类静态方法" );
          return "父类静态成员变量的初始化" ;
   }

   public Father(){
          System.out.println("父类无参构造函数初始化完成" );
          show();
   }

   public void show(){
          System.out.println("父类show()方法" );
   }
}

class Son extends Father{
   static{
          System.out.println("子类静态代码块初始化" );
   }

   {
          System.out.println("子类代码块初始化" );
   }

   private static  int i=1;

   private String s="子类私有成员变量" ;

   public void show(){
          System.out.println("子类show()方法:i=" +i);
   }

   public Son(){
          System.out.println("子类构造函数初始化完成" );
          show();
   }
}

public class TestClassLoadSeq {
        public static void main(String[] args){
               new Son();
       }
  
}

输出顺序:

父类静态代码块初始化
父类静态方法
子类静态代码块初始化
父类代码块初始化
父类无参构造函数初始化完成
子类show()方法:i=1
子类代码块初始化
子类构造函数初始化完成
子类成员变量初始化完成:s=子类私有成员变量
子类show()方法:i=1

例子2:

class Father{
   private static String s = print();

   static{
          System.out.println("父类静态代码块初始化" );
   }

   {
          System.out.println("父类代码块初始化" );
   }

   public static String print(){
          System.out.println("父类静态方法" );
          return "父类静态成员变量的初始化" ;
   }

   public Father(){
          System.out.println("父类无参构造函数初始化完成" );
          show();
   }

   public void show(){
          System.out.println("父类show()方法" );
   }
}

class Son extends Father{
   static{
          System.out.println("子类静态代码块初始化" );
   }

   {
          System.out.println("子类代码块初始化" );
   }

   private int i = 1;

   private String s="子类私有成员变量" ;

   public void show(){
          System.out.println("子类show()方法:i=" +i);
   }

   public Son(){
          System.out.println("子类构造函数初始化完成" );
          System.out.println("子类成员变量初始化完成:s=" +s);
          show();
   }
}

public class TestClassLoadSeq {
       public static void main(String[] args){
               new Son();
       }
}

输出顺序:

父类静态方法
父类静态代码块初始化
子类静态代码块初始化
父类代码块初始化
父类无参构造函数初始化完成
子类show()方法:i=0
子类代码块初始化
子类构造函数初始化完成
子类成员变量初始化完成:s=子类私有成员变量
子类show()方法:i=1

还有一种特殊情况如下:

例子3:

public class StaticTest{
   public static void main(String[] args){
       staticFunction();
   }

   static StaticTest st = new StaticTest();

   static{
       System.out.println("1");
   }

   {
       System.out.println("2");
   }

   StaticTest(){
       System.out.println("3");
       System.out.println("a="+a+",b="+b);
   }

   public static void staticFunction(){
       System.out.println("4");
   }

   int a = 110;

   static int b = 112;
}

输出顺序:

2
3
a=110,b=0
1
4

解析:

实例初始化不一定要在类初始化结束以后才开始初始化。

1.首先由main方法的调用触发了静态初始化。

2.在初始化StaticTest这个类时,遇到了st这个成员变量。

3.st引用的是本类的实例变量。此时静态初始化过程还没完成就要初始化实例部分了。

4.从java的角度,一旦开始初始化静态部分,无论是否完成,后续都不会再重新触发静态初始化流程了。

5.即把实例初始化嵌入到静态初始化流程中,导致了实例初始化完全在静态初始化之前。所以b没有值,为0。

6.其它流程和之前的类似,不再赘述。

你可能感兴趣的:(java)