[Kotlin] Entenda a ordem de execução de construtores kotlin, variáveis de membro e blocos de código init da perspectiva do bytecode

Escrevi um artigo antes, analisando a ordem de execução do construtor Kotlin, inicialização de variável de membro e bloco de código init da perspectiva da gramática Java:

Construtor Kotlin, variável de membro, ordem de execução do bloco de código init

Desta vez, analise sua ordem de execução sob a perspectiva do bytecode.

Ainda usando o exemplo anterior:

class InitOrderDemo(name: String) {
    
    
    val firstProperty = "First property: $name".also(::println)
    
    init {
    
    
        println("First initializer block that prints ${
      
      name}")
    }
    
    val secondProperty = "Second property: ${
      
      name.length}".also(::println)
    
    init {
    
    
        println("Second initializer block that prints ${
      
      name.length}")
    }
}

O resultado da chamada InitOrderDemo(“hello”)e impressão é o seguinte:

First property: hello
First initializer block that prints hello
Second property: 5
Second initializer block that prints 5

Você pode ver que a ordem de execução é executada na ordem em que são declaradas.

Depois de converter o código Koltin acima em bytecode, o conteúdo exibido é o seguinte:

// ================com/devnn/javalib/InitOrderDemo.class =================
// 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/InitOrderDemo.firstProperty : 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/InitOrderDemo.secondProperty : 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/Intrinsics.checkNotNullParameter (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/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    ALOAD 1
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    INVOKEVIRTUAL java/lang/StringBuilder.toString ()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/System.out : Ljava/io/PrintStream;
    ALOAD 3
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V
   L8
   L9
   L10
    GETSTATIC kotlin/Unit.INSTANCE : Lkotlin/Unit;
    ASTORE 6
    ALOAD 5
   L11
    LINENUMBER 4 L11
   L12
    ALOAD 2
   L13
    PUTFIELD com/devnn/javalib/InitOrderDemo.firstProperty : 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/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    ALOAD 1
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
    ASTORE 2
   L16
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    ALOAD 2
    INVOKEVIRTUAL java/io/PrintStream.println (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/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    ALOAD 1
    INVOKEVIRTUAL java/lang/String.length ()I
    INVOKEVIRTUAL java/lang/StringBuilder.append (I)Ljava/lang/StringBuilder;
    INVOKEVIRTUAL java/lang/StringBuilder.toString ()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/System.out : Ljava/io/PrintStream;
    ALOAD 3
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V
   L25
   L26
   L27
    GETSTATIC kotlin/Unit.INSTANCE : Lkotlin/Unit;
    ASTORE 6
    ALOAD 5
   L28
    LINENUMBER 10 L28
   L29
    ALOAD 2
   L30
    PUTFIELD com/devnn/javalib/InitOrderDemo.secondProperty : 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/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    ALOAD 1
    INVOKEVIRTUAL java/lang/String.length ()I
    INVOKEVIRTUAL java/lang/StringBuilder.append (I)Ljava/lang/StringBuilder;
    INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
    ASTORE 2
   L33
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    ALOAD 2
    INVOKEVIRTUAL java/io/PrintStream.println (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
}

Pode-se ver que o construtor acima, a inicialização da variável de membro e o bloco de código init são todos colocados no bloco de código init do bytecode de acordo com a declaração.

O inicializador init do bytecode é, na verdade, o construtor da classe. Há também um construtor init para converter código Java em bytecode.

Vejamos um exemplo de Java para aprofundar a compreensão do bloco de inicialização init do bytecode.

package com.devnn.javalib;

public class JavaInit {
    
    
    String firstName = "Steven";

    {
    
    
        System.out.println("This is init block");
    }

    JavaInit(String secondName) {
    
    
        System.out.println("firstName=" + firstName);
        System.out.println("secondName=" + secondName);
    }

    public static void main(String[] args) {
    
    
        new JavaInit("Jobs");
    }
}

A execução da função principal imprime os seguintes resultados:

This is init block
firstName=Steven
secondName=Jobs

Depois de converter a classe acima JavaInitem bytecode, o conteúdo é o seguinte:

// class version 51.0 (51)
// access flags 0x21
public class com/devnn/javalib/JavaInit {
    
    

  // compiled from: JavaInit.java

  // 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/JavaInit.firstName : Ljava/lang/String;
   L2
    LINENUMBER 7 L2
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    LDC "This is init block"
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
   L3
    LINENUMBER 11 L3
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    NEW java/lang/StringBuilder
    DUP
    INVOKESPECIAL java/lang/StringBuilder.<init> ()V
    LDC "firstName="
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    ALOAD 0
    GETFIELD com/devnn/javalib/JavaInit.firstName : Ljava/lang/String;
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
   L4
    LINENUMBER 12 L4
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    NEW java/lang/StringBuilder
    DUP
    INVOKESPECIAL java/lang/StringBuilder.<init> ()V
    LDC "secondName="
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    ALOAD 1
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
    INVOKEVIRTUAL java/io/PrintStream.println (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
}

Pode-se ver que a inicialização da variável de membro, o construtor e o bloco de construção da classe Java também são copiados no bloco de código init. Então, há um problema de ordem com eles?

Tente colocar a variável de membro firname da classe JavaInit acima no bloco de inicialização:

package com.devnn.javalib;

public class JavaInit {
    
    
    {
    
    
        System.out.println("This is init block");
    }

    JavaInit(String secondName) {
    
    
        System.out.println("firstName=" + firstName);
        System.out.println("secondName=" + secondName);
    }

    String firstName = "Steven";


    public static void main(String[] args) {
    
    
        new JavaInit("Jobs");
    }
}

Exibir código de bytes:

// class version 51.0 (51)
// access flags 0x21
public class com/devnn/javalib/JavaInit {
    
    

  // compiled from: JavaInit.java

  // 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/System.out : Ljava/io/PrintStream;
    LDC "This is init block"
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
   L2
    LINENUMBER 13 L2
    ALOAD 0
    LDC "Steven"
    PUTFIELD com/devnn/javalib/JavaInit.firstName : Ljava/lang/String;
   L3
    LINENUMBER 9 L3
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    NEW java/lang/StringBuilder
    DUP
    INVOKESPECIAL java/lang/StringBuilder.<init> ()V
    LDC "firstName="
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    ALOAD 0
    GETFIELD com/devnn/javalib/JavaInit.firstName : Ljava/lang/String;
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
   L4
    LINENUMBER 10 L4
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    NEW java/lang/StringBuilder
    DUP
    INVOKESPECIAL java/lang/StringBuilder.<init> ()V
    LDC "secondName="
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    ALOAD 1
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
    INVOKEVIRTUAL java/io/PrintStream.println (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
}

Pode-se ver que a inicialização da variável de membro, o construtor e o bloco de construção da classe Java também são copiados para o bloco de código init bytecode. A inicialização da variável membro de Java e os blocos de construção também são executados na ordem de declaração. A diferença é que o código do construtor Java é sempre colocado atrás do bloco de código init bytecode.

O bloco de inicialização init do bytecode é, na verdade, o verdadeiro construtor da classe. Os vários blocos de código init do Kotlin são copiados no bloco de inicialização init do bytecode em ordem, o que pode ser entendido como parte do construtor.
A inicialização das variáveis ​​de membro Java e kotlin é colocada no bloco de código init do bytecode, ou seja, executada no construtor.

Supongo que te gusta

Origin blog.csdn.net/devnn/article/details/127620245
Recomendado
Clasificación