I wrote an article before, analyzing the execution order of Kotlin constructor, member variable initialization, and init code block from the perspective of Java syntax:
Detailed explanation of the execution order of Kotlin constructor, member variables and init code blocks
This time, they will analyze their execution order from the perspective of bytecode.
Let's use the previous example:
class InitOrderDemo(name: String) { val firstProperty = "First property: $name".also(::println) init { println("First initializer block that prints ${name}") } val secondProperty = "Second property: ${}".also(::println) init { println("Second initializer block that prints ${}") } }
CallInitOrderDemo(“hello”)
The printing results are as follows:
First property: hello
First initializer block that prints hello
Second property: 5
Second initializer block that prints 5
You can see that the execution order is executed in the order they are declared.
After converting the above Koltin code into bytecode, the content is displayed as follows:
// ================com/devnn/javalib/ ================= // class version 52.0 (52) // access flags 0x31 public final class com/devnn/javalib/InitOrderDemo { // access flags 0x12 private final Ljava/lang/String; firstProperty @Lorg/jetbrains/annotations/NotNull;() // invisible // access flags 0x11 public final getFirstProperty()Ljava/lang/String; @Lorg/jetbrains/annotations/NotNull;() // invisible L0 LINENUMBER 4 L0 ALOAD 0 GETFIELD com/devnn/javalib/ : Ljava/lang/String; ARETURN L1 LOCALVARIABLE this Lcom/devnn/javalib/InitOrderDemo; L0 L1 0 MAXSTACK = 1 MAXLOCALS = 1 // access flags 0x12 private final Ljava/lang/String; secondProperty @Lorg/jetbrains/annotations/NotNull;() // invisible // access flags 0x11 public final getSecondProperty()Ljava/lang/String; @Lorg/jetbrains/annotations/NotNull;() // invisible L0 LINENUMBER 10 L0 ALOAD 0 GETFIELD com/devnn/javalib/ : Ljava/lang/String; ARETURN L1 LOCALVARIABLE this Lcom/devnn/javalib/InitOrderDemo; L0 L1 0 MAXSTACK = 1 MAXLOCALS = 1 // access flags 0x1 public <init>(Ljava/lang/String;)V // annotable parameter count: 1 (visible) // annotable parameter count: 1 (invisible) @Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 0 L0 ALOAD 1 LDC "name" INVOKESTATIC kotlin/jvm/internal/ (Ljava/lang/Object;Ljava/lang/String;)V L1 LINENUMBER 3 L1 ALOAD 0 INVOKESPECIAL java/lang/Object.<init> ()V L2 LINENUMBER 4 L2 ALOAD 0 NEW java/lang/StringBuilder DUP INVOKESPECIAL java/lang/StringBuilder.<init> ()V LDC "First property: " INVOKEVIRTUAL java/lang/ (Ljava/lang/String;)Ljava/lang/StringBuilder; ALOAD 1 INVOKEVIRTUAL java/lang/ (Ljava/lang/String;)Ljava/lang/StringBuilder; INVOKEVIRTUAL java/lang/ ()Ljava/lang/String; ASTORE 2 L3 ALOAD 2 ASTORE 3 L4 LINENUMBER 17 L4 ASTORE 5 L5 ICONST_0 ISTORE 4 L6 LINENUMBER 4 L6 L7 GETSTATIC java/lang/ : Ljava/io/PrintStream; ALOAD 3 INVOKEVIRTUAL java/io/ (Ljava/lang/Object;)V L8 L9 L10 GETSTATIC kotlin/ : Lkotlin/Unit; ASTORE 6 ALOAD 5 L11 LINENUMBER 4 L11 L12 ALOAD 2 L13 PUTFIELD com/devnn/javalib/ : Ljava/lang/String; L14 LINENUMBER 6 L14 NOP L15 LINENUMBER 7 L15 NEW java/lang/StringBuilder DUP INVOKESPECIAL java/lang/StringBuilder.<init> ()V LDC "First initializer block that prints " INVOKEVIRTUAL java/lang/ (Ljava/lang/String;)Ljava/lang/StringBuilder; ALOAD 1 INVOKEVIRTUAL java/lang/ (Ljava/lang/String;)Ljava/lang/StringBuilder; INVOKEVIRTUAL java/lang/ ()Ljava/lang/String; ASTORE 2 L16 GETSTATIC java/lang/ : Ljava/io/PrintStream; ALOAD 2 INVOKEVIRTUAL java/io/ (Ljava/lang/Object;)V L17 L18 LINENUMBER 8 L18 NOP L19 LINENUMBER 10 L19 ALOAD 0 NEW java/lang/StringBuilder DUP INVOKESPECIAL java/lang/StringBuilder.<init> ()V LDC "Second property: " INVOKEVIRTUAL java/lang/ (Ljava/lang/String;)Ljava/lang/StringBuilder; ALOAD 1 INVOKEVIRTUAL java/lang/ ()I INVOKEVIRTUAL java/lang/ (I)Ljava/lang/StringBuilder; INVOKEVIRTUAL java/lang/ ()Ljava/lang/String; ASTORE 2 L20 ALOAD 2 ASTORE 3 L21 LINENUMBER 17 L21 ASTORE 5 L22 ICONST_0 ISTORE 4 L23 LINENUMBER 10 L23 L24 GETSTATIC java/lang/ : Ljava/io/PrintStream; ALOAD 3 INVOKEVIRTUAL java/io/ (Ljava/lang/Object;)V L25 L26 L27 GETSTATIC kotlin/ : Lkotlin/Unit; ASTORE 6 ALOAD 5 L28 LINENUMBER 10 L28 L29 ALOAD 2 L30 PUTFIELD com/devnn/javalib/ : Ljava/lang/String; L31 LINENUMBER 12 L31 NOP L32 LINENUMBER 13 L32 NEW java/lang/StringBuilder DUP INVOKESPECIAL java/lang/StringBuilder.<init> ()V LDC "Second initializer block that prints " INVOKEVIRTUAL java/lang/ (Ljava/lang/String;)Ljava/lang/StringBuilder; ALOAD 1 INVOKEVIRTUAL java/lang/ ()I INVOKEVIRTUAL java/lang/ (I)Ljava/lang/StringBuilder; INVOKEVIRTUAL java/lang/ ()Ljava/lang/String; ASTORE 2 L33 GETSTATIC java/lang/ : Ljava/io/PrintStream; ALOAD 2 INVOKEVIRTUAL java/io/ (Ljava/lang/Object;)V L34 L35 LINENUMBER 14 L35 RETURN L36 LOCALVARIABLE p1 Ljava/lang/Object; L5 L10 3 LOCALVARIABLE $i$a$-unknown-InitOrderDemo$firstProperty$1 I L6 L10 4 LOCALVARIABLE p1 Ljava/lang/Object; L22 L27 3 LOCALVARIABLE $i$a$-unknown-InitOrderDemo$secondProperty$1 I L23 L27 4 LOCALVARIABLE this Lcom/devnn/javalib/InitOrderDemo; L0 L36 0 LOCALVARIABLE name Ljava/lang/String; L0 L36 1 MAXSTACK = 3 MAXLOCALS = 7 }
You can see that the constructor, member variable initialization and init code block above are all placed in the bytecode init code block according to the declaration.
The init initializer of bytecode is actually the class constructor. Converting Java code into bytecode also has an init constructor.
Let’s see a Java example below to deepen your understanding of the init initialization block of bytecode.
package ; public class JavaInit { String firstName = "Steven"; { ("This is init block"); } JavaInit(String secondName) { ("firstName=" + firstName); ("secondName=" + secondName); } public static void main(String[] args) { new JavaInit("Jobs"); } }
Run the main function and print the result as follows:
This is init block
firstName=Steven
secondName=Jobs
Put the aboveJavaInit
The content after the class is converted to bytecode is as follows:
// class version 51.0 (51) // access flags 0x21 public class com/devnn/javalib/JavaInit { // compiled from: // access flags 0x0 Ljava/lang/String; firstName // access flags 0x0 <init>(Ljava/lang/String;)V L0 LINENUMBER 10 L0 ALOAD 0 INVOKESPECIAL java/lang/Object.<init> ()V L1 LINENUMBER 4 L1 ALOAD 0 LDC "Steven" PUTFIELD com/devnn/javalib/ : Ljava/lang/String; L2 LINENUMBER 7 L2 GETSTATIC java/lang/ : Ljava/io/PrintStream; LDC "This is init block" INVOKEVIRTUAL java/io/ (Ljava/lang/String;)V L3 LINENUMBER 11 L3 GETSTATIC java/lang/ : Ljava/io/PrintStream; NEW java/lang/StringBuilder DUP INVOKESPECIAL java/lang/StringBuilder.<init> ()V LDC "firstName=" INVOKEVIRTUAL java/lang/ (Ljava/lang/String;)Ljava/lang/StringBuilder; ALOAD 0 GETFIELD com/devnn/javalib/ : Ljava/lang/String; INVOKEVIRTUAL java/lang/ (Ljava/lang/String;)Ljava/lang/StringBuilder; INVOKEVIRTUAL java/lang/ ()Ljava/lang/String; INVOKEVIRTUAL java/io/ (Ljava/lang/String;)V L4 LINENUMBER 12 L4 GETSTATIC java/lang/ : Ljava/io/PrintStream; NEW java/lang/StringBuilder DUP INVOKESPECIAL java/lang/StringBuilder.<init> ()V LDC "secondName=" INVOKEVIRTUAL java/lang/ (Ljava/lang/String;)Ljava/lang/StringBuilder; ALOAD 1 INVOKEVIRTUAL java/lang/ (Ljava/lang/String;)Ljava/lang/StringBuilder; INVOKEVIRTUAL java/lang/ ()Ljava/lang/String; INVOKEVIRTUAL java/io/ (Ljava/lang/String;)V L5 LINENUMBER 13 L5 RETURN L6 LOCALVARIABLE this Lcom/devnn/javalib/JavaInit; L0 L6 0 LOCALVARIABLE secondName Ljava/lang/String; L0 L6 1 MAXSTACK = 3 MAXLOCALS = 2 // access flags 0x9 public static main([Ljava/lang/String;)V L0 LINENUMBER 16 L0 NEW com/devnn/javalib/JavaInit DUP LDC "Jobs" INVOKESPECIAL com/devnn/javalib/JavaInit.<init> (Ljava/lang/String;)V POP L1 LINENUMBER 17 L1 RETURN L2 LOCALVARIABLE args [Ljava/lang/String; L0 L2 0 MAXSTACK = 3 MAXLOCALS = 1 }
It can be seen that the initialization of member variables, constructors, and constructors of Java class are also copied into the init code block. So do they have order issues?
Put the above JavaInit class frname member variable into the initialization block to try:
package ; public class JavaInit { { ("This is init block"); } JavaInit(String secondName) { ("firstName=" + firstName); ("secondName=" + secondName); } String firstName = "Steven"; public static void main(String[] args) { new JavaInit("Jobs"); } }
View bytecode:
// class version 51.0 (51) // access flags 0x21 public class com/devnn/javalib/JavaInit { // compiled from: // access flags 0x0 Ljava/lang/String; firstName // access flags 0x0 <init>(Ljava/lang/String;)V L0 LINENUMBER 8 L0 ALOAD 0 INVOKESPECIAL java/lang/Object.<init> ()V L1 LINENUMBER 5 L1 GETSTATIC java/lang/ : Ljava/io/PrintStream; LDC "This is init block" INVOKEVIRTUAL java/io/ (Ljava/lang/String;)V L2 LINENUMBER 13 L2 ALOAD 0 LDC "Steven" PUTFIELD com/devnn/javalib/ : Ljava/lang/String; L3 LINENUMBER 9 L3 GETSTATIC java/lang/ : Ljava/io/PrintStream; NEW java/lang/StringBuilder DUP INVOKESPECIAL java/lang/StringBuilder.<init> ()V LDC "firstName=" INVOKEVIRTUAL java/lang/ (Ljava/lang/String;)Ljava/lang/StringBuilder; ALOAD 0 GETFIELD com/devnn/javalib/ : Ljava/lang/String; INVOKEVIRTUAL java/lang/ (Ljava/lang/String;)Ljava/lang/StringBuilder; INVOKEVIRTUAL java/lang/ ()Ljava/lang/String; INVOKEVIRTUAL java/io/ (Ljava/lang/String;)V L4 LINENUMBER 10 L4 GETSTATIC java/lang/ : Ljava/io/PrintStream; NEW java/lang/StringBuilder DUP INVOKESPECIAL java/lang/StringBuilder.<init> ()V LDC "secondName=" INVOKEVIRTUAL java/lang/ (Ljava/lang/String;)Ljava/lang/StringBuilder; ALOAD 1 INVOKEVIRTUAL java/lang/ (Ljava/lang/String;)Ljava/lang/StringBuilder; INVOKEVIRTUAL java/lang/ ()Ljava/lang/String; INVOKEVIRTUAL java/io/ (Ljava/lang/String;)V L5 LINENUMBER 11 L5 RETURN L6 LOCALVARIABLE this Lcom/devnn/javalib/JavaInit; L0 L6 0 LOCALVARIABLE secondName Ljava/lang/String; L0 L6 1 MAXSTACK = 3 MAXLOCALS = 2 // access flags 0x9 public static main([Ljava/lang/String;)V L0 LINENUMBER 17 L0 NEW com/devnn/javalib/JavaInit DUP LDC "Jobs" INVOKESPECIAL com/devnn/javalib/JavaInit.<init> (Ljava/lang/String;)V POP L1 LINENUMBER 18 L1 RETURN L2 LOCALVARIABLE args [Ljava/lang/String; L0 L2 0 MAXSTACK = 3 MAXLOCALS = 1 }
It can be seen that the initialization of member variables, constructors, and constructors of Java class are also copied into the bytecode init code block. Java member variable initialization and construction blocks are also executed in the declared order. The difference is that Java constructor code is always placed behind the bytecode init code block.
The init initialization block of bytecode is actually the real constructor of the class. Kotlin multiple init code blocks are copied into the init initialization block of bytecode in order, which can be understood as being part of the constructor.
The initialization of Java and kotlin member variables are placed in the bytecode init code block, that is, executed in the constructor.
This is the article about Kotlin bytecode layer exploring the execution order of constructors, member variables and init code blocks. For more related content of Kotlin constructor, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!