In-depth initialization such as java virtual machine

    Class initialization is the last stage of the class loading process. In the initialization stage, the Java program code in the class is actually executed. The virtual machine specification strictly stipulates that there are only four cases in which the class must be initialized immediately:

 

  • When encountering the four bytecode instructions of new, getstatic, putstatic, and invokestatic, if the class has not been initialized, you need to trigger its initialization first. The most common Java code scenarios for generating these four instructions are: when instantiating an object using the new keyword, when reading or setting a static field (static) of a class (modified by static and then final, and the result has been compiled at compile time. except static fields placed in the constant pool ), and when calling a static method of a class.
  • When using the method of the Java.lang.refect package to make a reflective call to a class, if the class has not been initialized, you need to trigger its initialization first.
  • When initializing a class, if it is found that its parent class has not been initialized, it needs to trigger the initialization of its parent class first.
  • When the virtual machine starts, the user needs to specify a main class to be executed, and the virtual machine executes the main class first.

    The virtual machine stipulates that only these four situations will trigger the initialization of a class, which is called active reference to a class, and all other ways of referencing a class will not trigger its initialization, which is called passive reference. Here are some examples to illustrate passive references.

 

    1. The static field in the parent class is referenced through the subclass. At this time, the reference to the subclass is a passive reference, so the subclass will not be initialized, only the parent class will be initialized

 

[java]  view plain copy  
 
  1. class Father{  
  2.     public static int  m =  33 ;    
  3.     static{  
  4.         System.out.println( "The parent class is initialized" );  
  5.     }  
  6. }  
  7.   
  8. class Child extends Father{  
  9.     static{  
  10.         System.out.println( "Subclass is initialized" );  
  11.     }  
  12. }  
  13.   
  14. publicclass StaticTest{   
  15.     publicstaticvoid main(String[] args){    
  16.         System.out.println(Child.m);  
  17.     }  
  18. }  

    The output after execution is as follows:

 

    The parent class is initialized
    33

    For static fields, only the class that directly defines the field will be initialized. Therefore, referring to the static field defined in the parent class through its subclass will only trigger the initialization of the parent class but not the subclass.

 

    2. The constant will be stored in the constant pool of the class that calls it during the compilation phase. Essentially, there is no direct reference to the class that defines the constant, so it will not trigger the initialization of the class that defines the constant.

 

[java]  view plain copy  
 
  1. class Const{  
  2.     public static final  String NAME =  "I am a constant" ;    
  3.     static{  
  4.         System.out.println("初始化Const类");  
  5.     }  
  6. }  
  7.   
  8. public class FinalTest{  
  9.     public static void main(String[] args){  
  10.         System.out.println(Const.NAME);  
  11.     }  
  12. }  

    执行后输出的结果如下:

 

    我是常量

    虽然程序中引用了const类的常量NAME,但是在编译阶段将此常量的值“我是常量”存储到了调用它的类FinalTest的常量池中,对常量Const.NAME的引用实际上转化为了FinalTest类对自身常量池的引用。也就是说,实际上FinalTest的Class文件之中并没有Const类的符号引用入口,这两个类在编译成Class文件后就不存在任何联系了。

 

    3、通过数组定义来引用类,不会触发类的初始化

 

[java]  view plain  copy
 
  1. class Const{  
  2.     static{  
  3.         System.out.println("初始化Const类");  
  4.     }  
  5. }  
  6.   
  7. public class ArrayTest{  
  8.     public static void main(String[] args){  
  9.         Const[] con = new Const[5];  
  10.     }  
  11. }  

    执行后不输出任何信息,说明Const类并没有被初始化。

 

    但这段代码里触发了另一个名为“LLConst”的类的初始化,它是一个由虚拟机自动生成的、直接继承于java.lang.Object的子类,创建动作由字节码指令newarray触发,很明显,这是一个对数组引用类型的初初始化,而该数组中的元素仅仅包含一个对Const类的引用,并没有对其进行初始化。如果我们加入对con数组中各个Const类元素的实例化代码,便会触发Const类的初始化,如下:

 

[java]  view plain  copy
 
  1. class Const{  
  2.     static{  
  3.         System.out.println("初始化Const类");  
  4.     }  
  5. }  
  6.   
  7. public class ArrayTest{  
  8.     public static void main(String[] args){  
  9.         Const[] con = new Const[5];  
  10.         for(Const a:con)  
  11.             a = new Const();  
  12.     }  
  13. }  

    这样便会得到如下输出结果:

 

    初始化Const类
    根据四条规则的第一条,这里的new触发了Const类。

 

    Finally, look at the difference between the initialization process of an interface and the initialization process of a class.

    The interface also has an initialization process. In the above code, we use the static statement block to output initialization information, and the "static{}" statement block cannot be used in the interface, but the compiler will still generate a <clinit> class constructor for the interface. , used to initialize member variables defined in the interface (actually global constants modified by static final).

   The main difference between the two when initializing is: when a class is initialized, all its parent classes are required to have been initialized, but when an interface is initialized, it does not require all its parent interfaces to be initialized, only when the real When the parent interface is used (such as the constant defined in the reference interface), the parent interface will be initialized. This is also very different from the case of class initialization. Looking back at the second example, we can see that when the static final constant in the class is called, the initialization of the class will not be triggered, but when the static final constant in the interface is called. Triggers the initialization of this interface.

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326676767&siteId=291194637