Java 对象初始化过程

例子里总共测试了静态代码块,普通代码块,静态成员变量,普通成员 变量,构造器。分别在基类和派生类里各有一套。

public class ClassLog {
    private static final String TAG = "ClassLog";

    public static String baseFieldInit() {
        Loger.i(TAG, "baseFieldInit");
        return "";
    }

    public static String baseStaticFieldInit() {
        Loger.i(TAG, "baseStaticFieldInit");
        return "";
    }


    public static String childFieldInit() {
        Loger.i(TAG, "childFieldInit");
        return "";
    }

    public static String childStaticFieldInit() {
        Loger.i(TAG, "childStaticFieldInit");
        return "";
    }
}
public class ClassBase {
    private static final String TAG = "ClassBase";

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

    private static String BASE_STATIC_VALUE = ClassLog.baseStaticFieldInit();

    static {
        Loger.i(TAG, "Base Static Block 2");
    }

    {
        Loger.i(TAG, "Base Normal Block 1");
    }

    private String baseNormalValue = ClassLog.baseFieldInit();

    {
        Loger.i(TAG, "Base Normal Block 2");
    }

    public ClassBase() {
        Loger.i(TAG, "Base Class Constructor");
    }
}

public class ClassChild extends ClassBase {
    private static final String TAG = "ClassChild";

    static {
        Loger.i(TAG, "Child Static Block 1");
    }

    private static String CHILD_STATIC_VALUE = ClassLog.childStaticFieldInit();

    static {
        Loger.i(TAG, "Child Static Block 2");
    }

    {
        Loger.i(TAG, "Child Normal Block 1");
    }

    private String childNormalValue = ClassLog.childFieldInit();

    {
        Loger.i(TAG, "Child Normal Block 2");
    }

    public ClassChild() {
        Loger.i(TAG, "Child Class Constructor");
    }
}

创建一个ClassChild类的对象:

ClassChild tChild = new ClassChild();


Log 如下:


Java 对象初始化过程_第1张图片


结果证明:

对象在class文件加载完毕,以及为各成员在方法区开辟好内存空间后,就开始了所谓“初始化”的过程:

1.基类静态代码块、基类静态成员字段(并列优先级,按代码中出现的先后顺序)(只有第一次加载类时执行);

2.派生类静态代码块、基类静态成员字段(并列优先级,按代码中出现的先后顺序)(只有第一次加载类时执行);

3.基类普通代码块、基类普通成员字段(并列优先级,按代码中出现的先后顺序);

4.基类构造函数;

5.派生类普通代码块、基类普通成员字段(并列优先级,按代码中出现的先后顺序);

6.派生类构造函数;


注意: 第1、第2步骤的静态过程,只在这个类第一次被加载的时候才会运行。

再创建一个ClassChild类的对象:

ClassChild tChild = new ClassChild();


Java 对象初始化过程_第2张图片

第二次创建只执行3、4、5,6步。


From:https://www.zhihu.com/question/49196023



Java Object Initialization Order - Know your JLS!

Recently I came across an interesting problem whose solution eluded me at first glance. Consider these three classes:
package com.ds.test;

public class Upper {
 String upperString;

 public Upper() {
  Initializer.initialize(this);
 }
}
package com.ds.test;

public class Lower extends Upper {

 String lowerString = null;

 public Lower() {
  super();
  System.out.println("Upper:  " + upperString);
  System.out.println("Lower:  " + lowerString);
 }

 public static void main(final String[] args) {
  new Lower();
 }
}
package com.ds.test;
public class Initializer {
 static void initialize(final Upper anUpper) {
  if (anUpper instanceof Lower) {
   Lower lower = (Lower) anUpper;
   lower.lowerString = "lowerInited";
  }
  anUpper.upperString = "upperInited";
 }
}
What output is to be expected from running the  Lower class?

In this very reduced example it is much easier to get a view of the whole situation - in reality where this occurred there was a lot more code to distract one's attention...
Anyway, this is what the output looks like:
Upper:  upperInited
Lower:  null;
While the little example uses Strings, the real code of  Initializer had a delegate object registered with the equivalent of the  Lower class - at least that was the intention. For some reason however did this not work when running the application. Instead, the default path was taken - the one for the delegate object being not set ( null).
Now, change the code of  Lower slightly:
package com.ds.test;

public class Lower extends Upper {

 String lowerString;

 public Lower() {
  super();
  System.out.println("Upper:  " + upperString);
  System.out.println("Lower:  " + lowerString);
 }

 public static void main(final String[] args) {
  new Lower();
 }
}
The output is now:
Upper:  upperInited
Lower:  lowerInited
Notice the difference in the code?
Yes, the  lowerString field is no longer explicitly set to  null. Why would this make a difference? Isn't the default value for reference type fields (such as  String here)  null anyway? Of course, it is. However it turns out that this tiny little change - which apparently would not change the code's behavior in any way - makes this thing fly or not fly.
So what is going on? It becomes clear when looking at the initialization order:
  1. main() calls the Lower constructor.
  2. An instance of Lower is prepared. That means, all fields are created and populated with default values, i. e. null for reference types, false for booleans and so on. At this time, any inline assignments to the fields have not taken place!
  3. The super-constructor is called. This is mandated by the language spec. So, before anything else happens, Upper's constructor is called.
  4. The Upper constructor runs and hands a reference to the freshly created instance to the Initializer.initialize() method.
  5. The Initializer attaches new Strings to both fields. It does so by using a somewhat dirty instanceof check - not a particularly good design pattern, but possible, nevertheless. Once that has happened, both the upperString lowerString references are no longer null.
  6. The Initializer.initialize() call finishes, as does the Upper constructor.
  7. Now it becomes interesting: Construction of the Lower instance continues. Assuming there is no explicit =null assignment in the lowerString field declaration, the Lower constructor resumes execution and prints out the two Strings that are attached to the fields.
    However, if there is an explicit assignment to null, execution has a slightly different flow: Just after the super constructor is done, any variable initializers are executed (see section 12.5 of the Java Language Spec), before the rest of the constructor is run. In this case the String reference that was previously assigned to lowerString is now overwritten with null again! Only then does the rest of the constructor continue execution, now printing lowerString: null.
Apart from being a nice example for why it is handy to be aware of some of the minutiae of object creation (or knowing where to look in the JLS, printed or online) this shows why it is a bad idea to write the  Initializer like this. It should not be aware of  Upper's subclasses at all! Instead, if for some reason initialization of certain fields cannot be done in the  Lower class itself, it will just require its own variant of some sort of initialization helper. In that case, it would really make no difference if you used  String lowerString; or  String lowerString = null; - just as it should be.

From: http://www.danielschneller.com/2010/07/java-object-initialization-order-know.html

你可能感兴趣的:(JAVA)