Kotlin字节码层探究构造函数与成员变量和init代码块执行顺序
之前写了一篇文章,从Java语法的角度分析了Kotlin构造函数、成员变量初始化、init代码块三者的执行顺序:
Kotlin构造函数与成员变量和init代码块执行顺序详细讲解
这次再从字节码的角度分析它们的执行顺序。
还是用之前那个例子:
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}") } }
调用InitOrderDemo(“hello”)
打印的结果如下:
First property: hello
First initializer block that prints hello
Second property: 5
Second initializer block that prints 5
可以看到执行顺序,是按照它们声明的顺序执行。
将上面Koltin代码转成字节码之后,显示内容如下:
// ================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 }
可以看到上面的构造函数、成员变量初始化和init代码块,按照声明都被放到了字节码的init代码块中了。
字节码的init初始化器其实就是类的构造函数。将Java代码转成字节码也是存在init构造函数。
下面看一个Java示例,加深对字节码的init初始化块的认识。
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"); } }
运行main函数打印结果如下:
This is init block
firstName=Steven
secondName=Jobs
将上面的JavaInit
类转成字节码之后的内容如下:
// 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 }
可见,Java类的成员变量初始化、构造函数、构造块同样都被拷贝进了init代码块中。那么它们是否存在顺序问题呢?
将上面JavaInit类的firname成员变量放到初始化块下面试试:
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"); } }
查看字节码:
// 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 }
可见,Java类的成员变量初始化、构造函数、构造块同样都被拷贝进了字节码init代码块中。Java的成员变量初始化和构造块也是按声明顺序执行。不同的是,Java的构造函数代码始终放在了字节码init代码块的后面。
字节码的init初始化块,其实就是类的真正的构造函数。Kotlin多个init代码块都是按照顺序拷贝进了字节码的init初始化块中,可以理解为它们是构造函数的组成部分。
Java和kotlin成员变量的初始化都是放到字节码的init代码块中,也就是在构造函数中执行的。
到此这篇关于Kotlin字节码层探究构造函数与成员变量和init代码块执行顺序的文章就介绍到这了,更多相关Kotlin构造函数内容请搜索猪先飞以前的文章或继续浏览下面的相关文章希望大家以后多多支持猪先飞!
原文出处:https://blog.csdn.net/devnn/article/details/127620245
相关文章
- 虽然C#编译器为每个类型都设置了默认类型,但作为面向对象的设计原则,我们还是需要对变量进行正确的初始化。实际上这也是C#推荐的做法...2020-06-25
- 这篇文章主要介绍了JavaScript 中调用 Kotlin 方法实例详解的相关资料,需要的朋友可以参考下...2017-06-15
- 本篇文章主要是对C#类中属性与成员变量的使用进行了总结介绍,需要的朋友可以过来参考下,希望对大家有所帮助...2020-06-25
- 本篇文章主要是对C#中属性和成员变量的区别进行了介绍说明。需要的朋友可以过来参考下,希望对大家有所帮助...2020-06-25
- 这篇文章主要介绍了C#中构造函数和析构函数用法,结合实例形式详细分析了C#中构造函数与析构函数的原理、定义、使用方法与相关注意事项,需要的朋友可以参考下...2020-06-25
Android kotlin+协程+Room数据库的简单使用
这篇文章主要介绍了Android kotlin+协程+Room数据库的简单使用,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-01-04JavaScript精炼之构造函数 Constructor及Constructor属性详解
除了创建对象,构造函数(constructor) 还做了另一件有用的事情―自动为创建的新对象设置了原型对象(prototype object) 。原型对象存放于 ConstructorFunction.prototype 属性中。例如,我们重写之前例子,使用构造函数创建...2015-11-08- 这篇文章主要介绍了教你一文搞懂Kotlin中的Jvm注解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-10-11
- 在Kotlin中,不管是类、接口、构造函数、函数、属性及其设置器都具有可见性修饰符,下面这篇文章主要给大家介绍了关于Kotlin入门学习教程之可见性修饰符的相关资料,需要的朋友可以参考下...2021-11-11
- 下面小编就为大家带来一篇浅谈js构造函数的方法与原型prototype。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2016-07-06
- 这篇文章主要介绍了ASP.NET私有构造函数用法,较为详细的分析了ASP.NET中私有构造函数的特性及具体用法,需要的朋友可以参考下...2021-09-22
- 这篇文章主要介绍了C#中私有构造函数的特点和用途,需要的朋友可以参考下...2020-06-25
- 下面小编就为大家带来一篇C++静态成员函数不能调用非静态成员变量(详解)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2020-04-25
- 这篇文章主要介绍了kotlin java 混合代码 maven 打包实现,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-03-03
- 这篇文章主要介绍了C++空类及没有成员变量的类的大小,对于初学者更好的了解C++的指针及类的存储结构很有帮助,需要的朋友可以参考下...2020-04-25
- 这篇文章主要介绍了使用kotlin编写spring cloud微服务的相关知识,本文给大家提到配置文件的操作代码,给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-09-03
- 这篇文章主要介绍了C++编程中类的成员变量和成员函数的相关知识,是C++入门学习中的基础知识,需要的朋友可以参考下...2020-04-25
- 今天小编就为大家分享一篇关于C++构造函数和析构函数的使用与讲解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧...2020-04-25
- 下面小编就为大家带来一篇C++聚合关系类的构造函数的调用顺序详解。小编觉得挺不错的,现在就分享给大家,也给大家做个参考,一起跟随小编过来看看吧...2020-04-25
- 构造函数意思就是在类执行时通过构造函数作为入口进行操作了,我们下面来看一篇关于php构造函数用法吧。 构造函数 和 析构函数 构造函数 void __construct ([ mi...2016-11-25