preface

In juejin. Cn/post / 694834… At the end of this article, we talked about new DUP Invokespecial to form a conditional reflection is the initialization of a class. In that article, we looked at the initialization of a class from bytecode instructions.


A simple example is class initialization

First of all, let’s clarify the meaning of the following instructions:

New: Creates an instance of the class, allocates memory, but the init constructor has not been called

Dup: Copies an instance to the top of the operand stack

Invokespecial: Call the constructor function, cinIT is called (initialization of static code blocks) before the constructor is called

//java
public class ClassInit {
        static int a;
        static int b;
        static {
            a = 1;
            b = 2; }}// byte code:
public class javaplan.ClassInit {
    static int a;
    static int b;

    // Default constructor
    public javaplan.ClassInit();
    Code:
    0: aload_0   // Load the local variable table first to the top of the operand stack
    1: invokespecial #1 // Method Java /lang/Object."
      
       ":()V // execute the constructor
      
    4: return

    // Static code blocks correspond to cInit, i.e. class initialization

    static {};
    Code:
    0: iconst_1   // load int 1 at the top of the operand stack
    1: putstatic #2 // Field a:I // assign a value to variable A
    4: iconst_2   // load int 2 at the top of the operand stack
    5: putstatic #3 // Field b:I assigns a value to the variable b
    8: return
}
Copy the code

We see that the constructor of the class corresponds to init and the static code block corresponds to CINit


Two, look at an old problem

public class ClassA {
    static {
        System.out.println("A init");
    }
    public ClassA(a) {
        System.out.println("A Instance"); }}public class ClassB extends ClassA{
    static {
        System.out.println("B init");
    }
    public ClassB (a) {
        System.out.println("B Instance");
    }

    public static void main(String[] args) {
        ClassA a = new ClassB();
        //ClassB [] arr = new ClassB[10]; }}//C 
public class ClassInit {
    public static void main(String[] args) {
        ClassB[] array = new ClassB[10]; }}Copy the code
(1).classb calls new ClassB(), what output?
ClassB[] arr = new ClassB[10]
(3). What is output from a call to ClassB[] array = new ClassB[10] in ClassInit?
A. Let’s look at the bytecode in the first case:
public class javaplan.ClassA {
// the constructor of A is A init
public javaplan.ClassA();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
7: ldc #3 // String A Instance
9: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;) V
12: return

//A's cinit is A class initialization, static code block
static {};
Code:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #5 // String A init
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;) V
8: return
}

public class javaplan.ClassB extends javaplan.ClassA {

The constructor for B is B init
public javaplan.ClassB();
Code:
0: aload_0
1: invokespecial #1 // Method javaplan/ClassA."<init>":()V
4: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
7: ldc #3 // String B Instance
9: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;) V
12: return
//B's static main method
public static void main(java.lang.String[]);
Code:

// Execute new ClassB
0: new #5 // class javaplan/ClassB
3: dup
4: invokespecial #6 // Method "<init>":()V
7: astore_1
8: return
  
//B's class initializes cinit, which is B's static code block
static {};
Code:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #7 // String B init
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;) V
8: return
}
Copy the code

The constructor of class A is called cinit(static code block), new ClassB(), It triggers Binit, and Binit will call B cinit first, Binit will call A init first, and A’s init will call A cinit first, all in the order B cinit, A cinit, A init, Binit

B. Let’s look at the second case:
  public static void main(java.lang.String[]);
    Code:
       0: bipush        10
       2: anewarray     #5                  // class javaplan/ClassB
       5: astore_1
       6: return
Copy the code

The second case is just A new array of B, because the main method is A static method of ClassB, only involves the loading of ClassB, does not involve the call of ClassB init method, so it does not call the cinit and init of class A, all the output is only B cinit

C. In the third case, let’s look at the bytecode of classinit:
 public static void main(java.lang.String[]);
    Code:
       0: bipush        10
       2: anewarray     #2                  // class javaplan/ClassB
       5: astore_1
       6: return
Copy the code

Since the main method is ClassInit, new an array just allocates memory and does not trigger initialization of B, so nothing is printed.


Three, look at a problem

public class ClassInit {
    int a;
    public  void main(String[] args) {
        intb; System.out.println(a); System.out.println(b); }}Copy the code

As in the above code, the compiler tells us that B is not initialized and will report an error, so why not a?

This requires an understanding of the class loading process

  • Class load check
  • Execute the static code block, cinit
  • Allocate heap memory for objects
  • Initialize a member variable (instance field of an object can be used without assigning an initial value, and local variables can be used without assigning a value, because without this step, the state is undefined and the compiler will directly report an error)
  • Call the code inside the initialization block {}
  • Call the constructor function init

With the above flow, member variables are initialized before constructors, but local variables that are not assigned are undefined and not available.


Four,

New Order of execution of a class: static code block (cinit), initialization code block (code inside {}), constructor (init)