类的初始化和对象的实例化
类的初始化是指类和接口加载到方法区后,因某些原因的触发,执行静态变量初始化语句和静态初始化块中语句的过程,初始化语句的执行顺序是由上往下的。
类的初始化语句由编译器 收集并放到一个特殊的方法中,称为类初始化方法,在Class文件中对应的是<clinit>,这种只方法只能由JVM调用。
触发类初始化方法的条件可以包括:
1.创建本类对象或子类对象。包括使用new操作符创建对象、通过反射(Class类的newInstance方法、Constructor类的newInstance方法)。
2.访问静态成员。包括对静态变量的赋值和读取,但不包括对常量的访问,调用静态方法。
3.Class.forName方法。
4.子类初始化。
5.Java应用程序的入口,即应用程序启动时执行的主类。
特殊情况:
通过子类调用父类的静态变量只会触发父类的初始化。
直接调用class对象而不用于访问类的结构是不会触发类的初始化,因为该对象是类在装载时在堆内存创建的。
接口不能定义static块,同时子接口的初始化不会触发父接口的初始化。
实现类在不调用接口成员的情况下不会初始化接口。
初始化类的过程必须保持同步,如果有多个线程初始化一个类,仅仅允许一个线程执行初始化,其他的线程都需要等待。
对象的实例化是指使用new创建对象和调用构造方法初始化对象的过程,发生在类初始化之后。构造方法在Class文件中被编译成<init>,每个构造器都会先执行实例变量的初始化语句,意思是实例变量声明的语句中的赋值。
public class C {
String name = "default";
public C(String name) {
this.name = name;
}
}
构造方法的字节码将如下:
public jptest.C(java.lang.String);
descriptor: (Ljava/lang/String;)V
flags: ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: invokespecial #3 // Method java/lang/Object."<init>":()V
4: aload_0
5: ldc #4 // String default
7: putfield #5 // Field name:Ljava/lang/String;
10: aload_0
11: ldc #6 // String init
13: putfield #5 // Field name:Ljava/lang/String;
16: aload_0
17: aload_1
18: putfield #5 // Field name:Ljava/lang/String;
21: return
descriptor: (Ljava/lang/String;)V
flags: ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: invokespecial #3 // Method java/lang/Object."<init>":()V
4: aload_0
5: ldc #4 // String default
7: putfield #5 // Field name:Ljava/lang/String;
10: aload_0
11: ldc #6 // String init
13: putfield #5 // Field name:Ljava/lang/String;
16: aload_0
17: aload_1
18: putfield #5 // Field name:Ljava/lang/String;
21: return
由此可以得知实例化一个对象,代码执行的顺序是:静态初始化块->父类构造方法->实例初始化块->实例变量初始化语句->构造方法的代码。