经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 移动开发 » Kotlin » 查看文章
Kotlin字节码层探究构造函数与成员变量和init代码块执行顺序
来源:jb51  时间:2022/11/17 8:48:32  对本文有异议

之前写了一篇文章,从Java语法的角度分析了Kotlin构造函数、成员变量初始化、init代码块三者的执行顺序:

Kotlin构造函数与成员变量和init代码块执行顺序详细讲解

这次再从字节码的角度分析它们的执行顺序。

还是用之前那个例子:

  1. class InitOrderDemo(name: String) {
  2. val firstProperty = "First property: $name".also(::println)
  3. init {
  4. println("First initializer block that prints ${name}")
  5. }
  6. val secondProperty = "Second property: ${name.length}".also(::println)
  7. init {
  8. println("Second initializer block that prints ${name.length}")
  9. }
  10. }

调用InitOrderDemo(“hello”)打印的结果如下:

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

可以看到执行顺序,是按照它们声明的顺序执行。

将上面Koltin代码转成字节码之后,显示内容如下:

  1. // ================com/devnn/javalib/InitOrderDemo.class =================
  2. // class version 52.0 (52)
  3. // access flags 0x31
  4. public final class com/devnn/javalib/InitOrderDemo {
  5. // access flags 0x12
  6. private final Ljava/lang/String; firstProperty
  7. @Lorg/jetbrains/annotations/NotNull;() // invisible
  8. // access flags 0x11
  9. public final getFirstProperty()Ljava/lang/String;
  10. @Lorg/jetbrains/annotations/NotNull;() // invisible
  11. L0
  12. LINENUMBER 4 L0
  13. ALOAD 0
  14. GETFIELD com/devnn/javalib/InitOrderDemo.firstProperty : Ljava/lang/String;
  15. ARETURN
  16. L1
  17. LOCALVARIABLE this Lcom/devnn/javalib/InitOrderDemo; L0 L1 0
  18. MAXSTACK = 1
  19. MAXLOCALS = 1
  20. // access flags 0x12
  21. private final Ljava/lang/String; secondProperty
  22. @Lorg/jetbrains/annotations/NotNull;() // invisible
  23. // access flags 0x11
  24. public final getSecondProperty()Ljava/lang/String;
  25. @Lorg/jetbrains/annotations/NotNull;() // invisible
  26. L0
  27. LINENUMBER 10 L0
  28. ALOAD 0
  29. GETFIELD com/devnn/javalib/InitOrderDemo.secondProperty : Ljava/lang/String;
  30. ARETURN
  31. L1
  32. LOCALVARIABLE this Lcom/devnn/javalib/InitOrderDemo; L0 L1 0
  33. MAXSTACK = 1
  34. MAXLOCALS = 1
  35. // access flags 0x1
  36. public <init>(Ljava/lang/String;)V
  37. // annotable parameter count: 1 (visible)
  38. // annotable parameter count: 1 (invisible)
  39. @Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 0
  40. L0
  41. ALOAD 1
  42. LDC "name"
  43. INVOKESTATIC kotlin/jvm/internal/Intrinsics.checkNotNullParameter (Ljava/lang/Object;Ljava/lang/String;)V
  44. L1
  45. LINENUMBER 3 L1
  46. ALOAD 0
  47. INVOKESPECIAL java/lang/Object.<init> ()V
  48. L2
  49. LINENUMBER 4 L2
  50. ALOAD 0
  51. NEW java/lang/StringBuilder
  52. DUP
  53. INVOKESPECIAL java/lang/StringBuilder.<init> ()V
  54. LDC "First property: "
  55. INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
  56. ALOAD 1
  57. INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
  58. INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
  59. ASTORE 2
  60. L3
  61. ALOAD 2
  62. ASTORE 3
  63. L4
  64. LINENUMBER 17 L4
  65. ASTORE 5
  66. L5
  67. ICONST_0
  68. ISTORE 4
  69. L6
  70. LINENUMBER 4 L6
  71. L7
  72. GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
  73. ALOAD 3
  74. INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V
  75. L8
  76. L9
  77. L10
  78. GETSTATIC kotlin/Unit.INSTANCE : Lkotlin/Unit;
  79. ASTORE 6
  80. ALOAD 5
  81. L11
  82. LINENUMBER 4 L11
  83. L12
  84. ALOAD 2
  85. L13
  86. PUTFIELD com/devnn/javalib/InitOrderDemo.firstProperty : Ljava/lang/String;
  87. L14
  88. LINENUMBER 6 L14
  89. NOP
  90. L15
  91. LINENUMBER 7 L15
  92. NEW java/lang/StringBuilder
  93. DUP
  94. INVOKESPECIAL java/lang/StringBuilder.<init> ()V
  95. LDC "First initializer block that prints "
  96. INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
  97. ALOAD 1
  98. INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
  99. INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
  100. ASTORE 2
  101. L16
  102. GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
  103. ALOAD 2
  104. INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V
  105. L17
  106. L18
  107. LINENUMBER 8 L18
  108. NOP
  109. L19
  110. LINENUMBER 10 L19
  111. ALOAD 0
  112. NEW java/lang/StringBuilder
  113. DUP
  114. INVOKESPECIAL java/lang/StringBuilder.<init> ()V
  115. LDC "Second property: "
  116. INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
  117. ALOAD 1
  118. INVOKEVIRTUAL java/lang/String.length ()I
  119. INVOKEVIRTUAL java/lang/StringBuilder.append (I)Ljava/lang/StringBuilder;
  120. INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
  121. ASTORE 2
  122. L20
  123. ALOAD 2
  124. ASTORE 3
  125. L21
  126. LINENUMBER 17 L21
  127. ASTORE 5
  128. L22
  129. ICONST_0
  130. ISTORE 4
  131. L23
  132. LINENUMBER 10 L23
  133. L24
  134. GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
  135. ALOAD 3
  136. INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V
  137. L25
  138. L26
  139. L27
  140. GETSTATIC kotlin/Unit.INSTANCE : Lkotlin/Unit;
  141. ASTORE 6
  142. ALOAD 5
  143. L28
  144. LINENUMBER 10 L28
  145. L29
  146. ALOAD 2
  147. L30
  148. PUTFIELD com/devnn/javalib/InitOrderDemo.secondProperty : Ljava/lang/String;
  149. L31
  150. LINENUMBER 12 L31
  151. NOP
  152. L32
  153. LINENUMBER 13 L32
  154. NEW java/lang/StringBuilder
  155. DUP
  156. INVOKESPECIAL java/lang/StringBuilder.<init> ()V
  157. LDC "Second initializer block that prints "
  158. INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
  159. ALOAD 1
  160. INVOKEVIRTUAL java/lang/String.length ()I
  161. INVOKEVIRTUAL java/lang/StringBuilder.append (I)Ljava/lang/StringBuilder;
  162. INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
  163. ASTORE 2
  164. L33
  165. GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
  166. ALOAD 2
  167. INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V
  168. L34
  169. L35
  170. LINENUMBER 14 L35
  171. RETURN
  172. L36
  173. LOCALVARIABLE p1 Ljava/lang/Object; L5 L10 3
  174. LOCALVARIABLE $i$a$-unknown-InitOrderDemo$firstProperty$1 I L6 L10 4
  175. LOCALVARIABLE p1 Ljava/lang/Object; L22 L27 3
  176. LOCALVARIABLE $i$a$-unknown-InitOrderDemo$secondProperty$1 I L23 L27 4
  177. LOCALVARIABLE this Lcom/devnn/javalib/InitOrderDemo; L0 L36 0
  178. LOCALVARIABLE name Ljava/lang/String; L0 L36 1
  179. MAXSTACK = 3
  180. MAXLOCALS = 7
  181. }

可以看到上面的构造函数、成员变量初始化和init代码块,按照声明都被放到了字节码的init代码块中了。

字节码的init初始化器其实就是类的构造函数。将Java代码转成字节码也是存在init构造函数。

下面看一个Java示例,加深对字节码的init初始化块的认识。

  1. package com.devnn.javalib;
  2. public class JavaInit {
  3. String firstName = "Steven";
  4. {
  5. System.out.println("This is init block");
  6. }
  7. JavaInit(String secondName) {
  8. System.out.println("firstName=" + firstName);
  9. System.out.println("secondName=" + secondName);
  10. }
  11. public static void main(String[] args) {
  12. new JavaInit("Jobs");
  13. }
  14. }

运行main函数打印结果如下:

This is init block
firstName=Steven
secondName=Jobs

将上面的JavaInit类转成字节码之后的内容如下:

  1. // class version 51.0 (51)
  2. // access flags 0x21
  3. public class com/devnn/javalib/JavaInit {
  4. // compiled from: JavaInit.java
  5. // access flags 0x0
  6. Ljava/lang/String; firstName
  7. // access flags 0x0
  8. <init>(Ljava/lang/String;)V
  9. L0
  10. LINENUMBER 10 L0
  11. ALOAD 0
  12. INVOKESPECIAL java/lang/Object.<init> ()V
  13. L1
  14. LINENUMBER 4 L1
  15. ALOAD 0
  16. LDC "Steven"
  17. PUTFIELD com/devnn/javalib/JavaInit.firstName : Ljava/lang/String;
  18. L2
  19. LINENUMBER 7 L2
  20. GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
  21. LDC "This is init block"
  22. INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
  23. L3
  24. LINENUMBER 11 L3
  25. GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
  26. NEW java/lang/StringBuilder
  27. DUP
  28. INVOKESPECIAL java/lang/StringBuilder.<init> ()V
  29. LDC "firstName="
  30. INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
  31. ALOAD 0
  32. GETFIELD com/devnn/javalib/JavaInit.firstName : Ljava/lang/String;
  33. INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
  34. INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
  35. INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
  36. L4
  37. LINENUMBER 12 L4
  38. GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
  39. NEW java/lang/StringBuilder
  40. DUP
  41. INVOKESPECIAL java/lang/StringBuilder.<init> ()V
  42. LDC "secondName="
  43. INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
  44. ALOAD 1
  45. INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
  46. INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
  47. INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
  48. L5
  49. LINENUMBER 13 L5
  50. RETURN
  51. L6
  52. LOCALVARIABLE this Lcom/devnn/javalib/JavaInit; L0 L6 0
  53. LOCALVARIABLE secondName Ljava/lang/String; L0 L6 1
  54. MAXSTACK = 3
  55. MAXLOCALS = 2
  56. // access flags 0x9
  57. public static main([Ljava/lang/String;)V
  58. L0
  59. LINENUMBER 16 L0
  60. NEW com/devnn/javalib/JavaInit
  61. DUP
  62. LDC "Jobs"
  63. INVOKESPECIAL com/devnn/javalib/JavaInit.<init> (Ljava/lang/String;)V
  64. POP
  65. L1
  66. LINENUMBER 17 L1
  67. RETURN
  68. L2
  69. LOCALVARIABLE args [Ljava/lang/String; L0 L2 0
  70. MAXSTACK = 3
  71. MAXLOCALS = 1
  72. }

可见,Java类的成员变量初始化、构造函数、构造块同样都被拷贝进了init代码块中。那么它们是否存在顺序问题呢?

将上面JavaInit类的firname成员变量放到初始化块下面试试:

  1. package com.devnn.javalib;
  2. public class JavaInit {
  3. {
  4. System.out.println("This is init block");
  5. }
  6. JavaInit(String secondName) {
  7. System.out.println("firstName=" + firstName);
  8. System.out.println("secondName=" + secondName);
  9. }
  10. String firstName = "Steven";
  11. public static void main(String[] args) {
  12. new JavaInit("Jobs");
  13. }
  14. }

查看字节码:

  1. // class version 51.0 (51)
  2. // access flags 0x21
  3. public class com/devnn/javalib/JavaInit {
  4. // compiled from: JavaInit.java
  5. // access flags 0x0
  6. Ljava/lang/String; firstName
  7. // access flags 0x0
  8. <init>(Ljava/lang/String;)V
  9. L0
  10. LINENUMBER 8 L0
  11. ALOAD 0
  12. INVOKESPECIAL java/lang/Object.<init> ()V
  13. L1
  14. LINENUMBER 5 L1
  15. GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
  16. LDC "This is init block"
  17. INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
  18. L2
  19. LINENUMBER 13 L2
  20. ALOAD 0
  21. LDC "Steven"
  22. PUTFIELD com/devnn/javalib/JavaInit.firstName : Ljava/lang/String;
  23. L3
  24. LINENUMBER 9 L3
  25. GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
  26. NEW java/lang/StringBuilder
  27. DUP
  28. INVOKESPECIAL java/lang/StringBuilder.<init> ()V
  29. LDC "firstName="
  30. INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
  31. ALOAD 0
  32. GETFIELD com/devnn/javalib/JavaInit.firstName : Ljava/lang/String;
  33. INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
  34. INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
  35. INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
  36. L4
  37. LINENUMBER 10 L4
  38. GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
  39. NEW java/lang/StringBuilder
  40. DUP
  41. INVOKESPECIAL java/lang/StringBuilder.<init> ()V
  42. LDC "secondName="
  43. INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
  44. ALOAD 1
  45. INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
  46. INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
  47. INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
  48. L5
  49. LINENUMBER 11 L5
  50. RETURN
  51. L6
  52. LOCALVARIABLE this Lcom/devnn/javalib/JavaInit; L0 L6 0
  53. LOCALVARIABLE secondName Ljava/lang/String; L0 L6 1
  54. MAXSTACK = 3
  55. MAXLOCALS = 2
  56. // access flags 0x9
  57. public static main([Ljava/lang/String;)V
  58. L0
  59. LINENUMBER 17 L0
  60. NEW com/devnn/javalib/JavaInit
  61. DUP
  62. LDC "Jobs"
  63. INVOKESPECIAL com/devnn/javalib/JavaInit.<init> (Ljava/lang/String;)V
  64. POP
  65. L1
  66. LINENUMBER 18 L1
  67. RETURN
  68. L2
  69. LOCALVARIABLE args [Ljava/lang/String; L0 L2 0
  70. MAXSTACK = 3
  71. MAXLOCALS = 1
  72. }

可见,Java类的成员变量初始化、构造函数、构造块同样都被拷贝进了字节码init代码块中。Java的成员变量初始化和构造块也是按声明顺序执行。不同的是,Java的构造函数代码始终放在了字节码init代码块的后面。

字节码的init初始化块,其实就是类的真正的构造函数。Kotlin多个init代码块都是按照顺序拷贝进了字节码的init初始化块中,可以理解为它们是构造函数的组成部分。

Java和kotlin成员变量的初始化都是放到字节码的init代码块中,也就是在构造函数中执行的。

到此这篇关于Kotlin字节码层探究构造函数与成员变量和init代码块执行顺序的文章就介绍到这了,更多相关Kotlin构造函数内容请搜索w3xue以前的文章或继续浏览下面的相关文章希望大家以后多多支持w3xue!

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号