Kotlin字节码生成机制详尽分析
通过注解修改Kotlin的class文件名:
对于Kotlin文件在编译之后生成的class文件名默认是有一定规则的,比如:


而其实这个生成字节码的文件名称是可以被改的,之前https://www.cnblogs.com/webor2006/p/11530600.html也提及到,也就是可以用JvmName注解,再来试一下:

然后重新的build一次,再来看一下生成的class文件:

既然可以手动的将编译的字符码文件名给改了,那。。如果两个Kotlin文件都指定同一个名称,会有啥情况发生呢?试试,再来建一个Kotlin文件:


那。。有没有一种机制能将这两个类的内容生成到一个HelloWorld.class,当然有,也就是Kotlin中可以将多个Kotlin文件合并成一个字节码文件,下面来看一下:


接下来再次编译:

居然此时就没报错了,那这个字节码文件中的内容是?跟进去瞅一下:
bogon:kotlin_lecture xiongwei$ javap -c -v com/kotlin/test11/HelloWorld.class
Classfile /Users/xiongwei/Documents/workspace/IntelliJSpace/kotlin_lecture/out/production/kotlin_lecture/com/kotlin/test11/HelloWorld.class
Last modified 2019-9-20; size 860 bytes
MD5 checksum 47cd63b80d95d489afa25bedd346da23
public final class com.kotlin.test11.HelloWorld
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_FINAL, ACC_SUPER
Constant pool:
#1 = Utf8 com/kotlin/test11/HelloWorld
#2 = Class #1 // com/kotlin/test11/HelloWorld
#3 = Utf8 java/lang/Object
#4 = Class #3 // java/lang/Object
#5 = Utf8 getMystr
#6 = Utf8 ()Ljava/lang/String;
#7 = Utf8 Lorg/jetbrains/annotations/NotNull;
#8 = Utf8 com/kotlin/test11/HelloWorld__HelloKotlin3Kt
#9 = Class #8 // com/kotlin/test11/HelloWorld__HelloKotlin3Kt
#10 = NameAndType #5:#6 // getMystr:()Ljava/lang/String;
#11 = Methodref #9.#10 // com/kotlin/test11/HelloWorld__HelloKotlin3Kt.getMystr:()Ljava/lang/String;
#12 = Utf8 setMystr
#13 = Utf8 (Ljava/lang/String;)V
#14 = NameAndType #12:#13 // setMystr:(Ljava/lang/String;)V
#15 = Methodref #9.#14 // com/kotlin/test11/HelloWorld__HelloKotlin3Kt.setMystr:(Ljava/lang/String;)V
#16 = Utf8 <set-?>
#17 = Utf8 Ljava/lang/String;
#18 = Utf8 myPrint
#19 = Utf8 ()V
#20 = NameAndType #18:#19 // myPrint:()V
#21 = Methodref #9.#20 // com/kotlin/test11/HelloWorld__HelloKotlin3Kt.myPrint:()V
#22 = Utf8 myPrint2
#23 = Utf8 com/kotlin/test11/HelloWorld__HelloKotlin4Kt
#24 = Class #23 // com/kotlin/test11/HelloWorld__HelloKotlin4Kt
#25 = NameAndType #22:#19 // myPrint2:()V
#26 = Methodref #24.#25 // com/kotlin/test11/HelloWorld__HelloKotlin4Kt.myPrint2:()V
#27 = Utf8 Lkotlin/Metadata;
#28 = Utf8 mv
#29 = Integer 1
#30 = Integer 15
#31 = Utf8 bv
#32 = Integer 0
#33 = Integer 3
#34 = Utf8 k
#35 = Integer 4
#36 = Utf8 d1
#37 = Utf8 Code
#38 = Utf8 LineNumberTable
#39 = Utf8 RuntimeInvisibleAnnotations
#40 = Utf8 LocalVariableTable
#41 = Utf8 RuntimeInvisibleParameterAnnotations
#42 = Utf8 RuntimeVisibleAnnotations
{
public static final java.lang.String getMystr();
descriptor: ()Ljava/lang/String;
flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL
Code:
stack=1, locals=0, args_size=0
0: invokestatic #11 // Method com/kotlin/test11/HelloWorld__HelloKotlin3Kt.getMystr:()Ljava/lang/String;
3: areturn
LineNumberTable:
line 1: 0
RuntimeInvisibleAnnotations:
0: #7() public static final void setMystr(java.lang.String);
descriptor: (Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokestatic #15 // Method com/kotlin/test11/HelloWorld__HelloKotlin3Kt.setMystr:(Ljava/lang/String;)V
4: return
LineNumberTable:
line 1: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 <set-?> Ljava/lang/String;
RuntimeInvisibleParameterAnnotations:
0:
0: #7() public static final void myPrint();
descriptor: ()V
flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL
Code:
stack=0, locals=0, args_size=0
0: invokestatic #21 // Method com/kotlin/test11/HelloWorld__HelloKotlin3Kt.myPrint:()V
3: return
LineNumberTable:
line 1: 0 public static final void myPrint2();
descriptor: ()V
flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL
Code:
stack=0, locals=0, args_size=0
0: invokestatic #26 // Method com/kotlin/test11/HelloWorld__HelloKotlin4Kt.myPrint2:()V
3: return
LineNumberTable:
line 1: 0
}
RuntimeVisibleAnnotations:
0: #27(#28=[I#29,I#29,I#30],#31=[I#29,I#32,I#33],#34=I#35,#36=[s#8,s#23])
很明显字节码中确实是已经包含HelloKotlin3.kt和HelloKotlin4.kt的内容进行了合并了,既然这俩文件最终会编译成一个字节码,那如果在HelloKotlin4.kt中也定义一个跟HelloKotlin3.kt一样的方法名会有啥反应呢,如下:

比较容易理解,还是将其方法名进行还原,接下来新建一个Java文件来调用一下:

@JvmField:
使用@JvmFiled注解对Kotlin中的属性进行标注时,表示它是一个实例字段(instance filed),Kotlin编译器在处理的时候,将不会给这个字段生成getter/setter。
下面先来新建一个类:

接下来咱们可以使用@JvmField注解,如下:

注上它之后,其实该字段就变为了一个实例字段,Kotlin编译器就不会给该字段生成getter和setter了,下面反编译一下:


那。。没法验证所说的理论了呀,这里换一种思路,从Java调用上面来验证:

当然,实际过程中貌似木有必要使用该注解,一般用getter和setter的较大,做个了解。
伴生对像:
关于它其实在之前https://www.cnblogs.com/webor2006/p/11210181.html已经学习过了,这里从Java调用Kotlin的角度再来看一它:

然后再新建一个Java:

完整调用如下:

好,接下来咱们再用一个@JvmField注解:


此时程序调用就得修改为:

接下来再来看一下在伴生对象中定义方法又是如何调用的,如下:


那在字节码中MyClass2是如何定义的呢?反编译一下,之前反编译乱码了,下面用javap -v来看一下:
bogon:kotlin_lecture xiongwei$ javap -verbose com/kotlin/test11/MyClass2.class
Classfile /Users/xiongwei/Documents/workspace/IntelliJSpace/kotlin_lecture/out/production/kotlin_lecture/com/kotlin/test11/MyClass2.class
Last modified 2019-9-21; size 770 bytes
MD5 checksum b7bf58035d08f2c1f25542b389451e7e
Compiled from "HelloKotlin7.kt"
public final class com.kotlin.test11.MyClass2
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_FINAL, ACC_SUPER
Constant pool:
#1 = Utf8 com/kotlin/test11/MyClass2
#2 = Class #1 // com/kotlin/test11/MyClass2
#3 = Utf8 java/lang/Object
#4 = Class #3 // java/lang/Object
#5 = Utf8 <init>
#6 = Utf8 ()V
#7 = NameAndType #5:#6 // "<init>":()V
#8 = Methodref #4.#7 // java/lang/Object."<init>":()V
#9 = Utf8 this
#10 = Utf8 Lcom/kotlin/test11/MyClass2;
#11 = Utf8 <clinit>
#12 = Utf8 Companion
#13 = Utf8 Lcom/kotlin/test11/MyClass2$Companion;
#14 = Utf8 Lkotlin/Metadata;
#15 = Utf8 mv
#16 = Integer 1
#17 = Integer 15
#18 = Utf8 bv
#19 = Integer 0
#20 = Integer 3
#21 = Utf8 k
#22 = Utf8 d1
#23 = Utf8
\n\n\ 20:B¢¨
#24 = Utf8 d2
#25 = Utf8
#26 = Utf8 kotlin_lecture
#27 = Utf8 com/kotlin/test11/MyClass2$Companion
#28 = Class #27 // com/kotlin/test11/MyClass2$Companion
#29 = Utf8 (Lkotlin/jvm/internal/DefaultConstructorMarker;)V
#30 = NameAndType #5:#29 // "<init>":(Lkotlin/jvm/internal/DefaultConstructorMarker;)V
#31 = Methodref #28.#30 // com/kotlin/test11/MyClass2$Companion."<init>":(Lkotlin/jvm/internal/DefaultConstructorMarker;)V
#32 = NameAndType #12:#13 // Companion:Lcom/kotlin/test11/MyClass2$Companion;
#33 = Fieldref #2.#32 // com/kotlin/test11/MyClass2.Companion:Lcom/kotlin/test11/MyClass2$Companion;
#34 = Utf8 HelloKotlin7.kt
#35 = Utf8 Code
#36 = Utf8 LineNumberTable
#37 = Utf8 LocalVariableTable
#38 = Utf8 InnerClasses
#39 = Utf8 SourceFile
#40 = Utf8 RuntimeVisibleAnnotations
{
public static final com.kotlin.test11.MyClass2$Companion Companion;
descriptor: Lcom/kotlin/test11/MyClass2$Companion;
flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL public com.kotlin.test11.MyClass2();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 3: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/kotlin/test11/MyClass2; static {};
descriptor: ()V
flags: ACC_STATIC
Code:
stack=3, locals=0, args_size=0
0: new #28 // class com/kotlin/test11/MyClass2$Companion
3: dup
4: aconst_null
5: invokespecial #31 // Method com/kotlin/test11/MyClass2$Companion."<init>":(Lkotlin/jvm/internal/DefaultConstructorMarker;)V
8: putstatic #33 // Field Companion:Lcom/kotlin/test11/MyClass2$Companion;
11: return
}
InnerClasses:
public static final #12= #28 of #2; //Companion=class com/kotlin/test11/MyClass2$Companion of class com/kotlin/test11/MyClass2
SourceFile: "HelloKotlin7.kt"
RuntimeVisibleAnnotations:
0: #14(#15=[I#16,I#16,I#17],#18=[I#16,I#19,I#20],#21=I#16,#22=[s#23],#24=[s#10,s#25,s#6,s#12,s#26])
木有乱码了,好,面目就可以揭开了,首先:

所以我们就可以这样来调用:

其实我们可以改变其伴身对象方法的默认规则,就得用下面这个注解了。
@JvmStatic:
在Kotlin中,我们可以将具名对象或是伴生对象中定义的函数注解为@JvmStatic,这样编译器既会在相应对象的类中生成静态方法,也会在对象自身中生成实例方法。下面来尝试一下:

此时咱们的调用方式就可以这样写了:

很明显,这个test2肯定是一个静态方法了,再来反编译看一下:
bogon:kotlin_lecture xiongwei$ javap -verbose com/kotlin/test11/MyClass2.class
Classfile /Users/xiongwei/Documents/workspace/IntelliJSpace/kotlin_lecture/out/production/kotlin_lecture/com/kotlin/test11/MyClass2.class
Last modified 2019-9-21; size 858 bytes
MD5 checksum 7b8819a8b4e7417db633e619b794273f
Compiled from "HelloKotlin7.kt"
public final class com.kotlin.test11.MyClass2
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_FINAL, ACC_SUPER
Constant pool:
#1 = Utf8 com/kotlin/test11/MyClass2
#2 = Class #1 // com/kotlin/test11/MyClass2
#3 = Utf8 java/lang/Object
#4 = Class #3 // java/lang/Object
#5 = Utf8 <init>
#6 = Utf8 ()V
#7 = NameAndType #5:#6 // "<init>":()V
#8 = Methodref #4.#7 // java/lang/Object."<init>":()V
#9 = Utf8 this
#10 = Utf8 Lcom/kotlin/test11/MyClass2;
#11 = Utf8 <clinit>
#12 = Utf8 Companion
#13 = Utf8 Lcom/kotlin/test11/MyClass2$Companion;
#14 = Utf8 Lkotlin/Metadata;
#15 = Utf8 mv
#16 = Integer 1
#17 = Integer 15
#18 = Utf8 bv
#19 = Integer 0
#20 = Integer 3
#21 = Utf8 k
#22 = Utf8 d1
#23 = Utf8
\n\n\ 20:B¢¨
#24 = Utf8 d2
#25 = Utf8
#26 = Utf8 kotlin_lecture
#27 = Utf8 test2
#28 = Utf8 Lkotlin/jvm/JvmStatic;
#29 = NameAndType #12:#13 // Companion:Lcom/kotlin/test11/MyClass2$Companion;
#30 = Fieldref #2.#29 // com/kotlin/test11/MyClass2.Companion:Lcom/kotlin/test11/MyClass2$Companion;
#31 = Utf8 com/kotlin/test11/MyClass2$Companion
#32 = Class #31 // com/kotlin/test11/MyClass2$Companion
#33 = NameAndType #27:#6 // test2:()V
#34 = Methodref #32.#33 // com/kotlin/test11/MyClass2$Companion.test2:()V
#35 = Utf8 (Lkotlin/jvm/internal/DefaultConstructorMarker;)V
#36 = NameAndType #5:#35 // "<init>":(Lkotlin/jvm/internal/DefaultConstructorMarker;)V
#37 = Methodref #32.#36 // com/kotlin/test11/MyClass2$Companion."<init>":(Lkotlin/jvm/internal/DefaultConstructorMarker;)V
#38 = Utf8 HelloKotlin7.kt
#39 = Utf8 Code
#40 = Utf8 LineNumberTable
#41 = Utf8 LocalVariableTable
#42 = Utf8 RuntimeVisibleAnnotations
#43 = Utf8 InnerClasses
#44 = Utf8 SourceFile
{
public static final com.kotlin.test11.MyClass2$Companion Companion;
descriptor: Lcom/kotlin/test11/MyClass2$Companion;
flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL public com.kotlin.test11.MyClass2();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 3: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/kotlin/test11/MyClass2; static {};
descriptor: ()V
flags: ACC_STATIC
Code:
stack=3, locals=0, args_size=0
0: new #32 // class com/kotlin/test11/MyClass2$Companion
3: dup
4: aconst_null
5: invokespecial #37 // Method com/kotlin/test11/MyClass2$Companion."<init>":(Lkotlin/jvm/internal/DefaultConstructorMarker;)V
8: putstatic #30 // Field Companion:Lcom/kotlin/test11/MyClass2$Companion;
11: return public static final void test2();
descriptor: ()V
flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL
Code:
stack=1, locals=0, args_size=0
0: getstatic #30 // Field Companion:Lcom/kotlin/test11/MyClass2$Companion;
3: invokevirtual #34 // Method com/kotlin/test11/MyClass2$Companion.test2:()V
6: return
RuntimeVisibleAnnotations:
0: #28()
}
InnerClasses:
public static final #12= #32 of #2; //Companion=class com/kotlin/test11/MyClass2$Companion of class com/kotlin/test11/MyClass2
SourceFile: "HelloKotlin7.kt"
RuntimeVisibleAnnotations:
0: #14(#15=[I#16,I#16,I#17],#18=[I#16,I#19,I#20],#21=I#16,#22=[s#23],#24=[s#10,s#25,s#6,s#12,s#26])
此时确实在MyClass2字节码中生成了一个静态的test2()方法了,而它的实现很明显可以看到,它最终是调用了Companion.test2(),其根本就是由该@JvmStatic注解发生了作用。
Kotlin字节码生成机制详尽分析的更多相关文章
- [WebKit内核] JavaScript引擎深度解析--基础篇(一)字节码生成及语法树的构建详情分析
[WebKit内核] JavaScript引擎深度解析--基础篇(一)字节码生成及语法树的构建详情分析 标签: webkit内核JavaScriptCore 2015-03-26 23:26 2285 ...
- Java代理全攻略【有瑕疵:字节码生成部分没看到,最后两节没仔细看,累了】
Java代理 1.代理模式 定义:给某个对象提供一个代理对象,并由代理对象控制对于原对象的访问,即客户不直接操控原对象,而是通过代理对象间接地操控原对象. 其实就是委托.聚合.中间人. 为了保持行为的 ...
- 谁还没遇上过NoClassDefFoundError咋地——浅谈字节码生成与热部署
谁还没遇上过NoClassDefFoundError咋地--浅谈字节码生成与热部署 前言 在Java程序员的世界里,NoClassDefFoundError是一类相当令人厌恶的错误,因为这类错误通常非 ...
- 深入浅出Java探针技术2---java字节码生成框架ASM、Javassist和byte buddy的使用
目前Java字节码生成框架大致有ASM.Javassist和byte buddy三种 ASM框架介绍及使用 1.ASM介绍 ASM是一种Java字节码操控框架,能够以二进制形式修改已有的类或是生成类, ...
- [WebKit内核] JavaScriptCore深度解析--基础篇(一)字节码生成及语法树的构建
看到HorkeyChen写的文章<[WebKit] JavaScriptCore解析--基础篇(三)从脚本代码到JIT编译的代码实现>,写的很好,深受启发.想补充一些Horkey没有写到的 ...
- PHP-7.1 源代码学习:字节码生成 之 "$a = 1"
前言 本文通过分析 "$a=1" 这个 PHP 语句的编译和执行来窥探 php-cli 解释执行逻辑 准备 参考之前的系列文章,在 ubuntu 环境下下载,编译 PHP 源代码 ...
- Flask debug 模式 PIN 码生成机制安全性研究笔记
Flask debug 模式 PIN 码生成机制安全性研究笔记 0x00 前言 前几天我整理了一个笔记:Flask开启debug模式等于给黑客留了后门,就Flask在生产网络中开启debug模式可能产 ...
- 透过字节码生成审视Java动态代理运作机制
对于动态代理我想应该大家都不陌生,就是可以动态去代理实现某个接口的类来干一些我们自己想要的功能,但是在字节码层面它的表现是如何的呢?既然目前刚好在研究字节码相关的东东,有必要对其从字节码角度来审视一下 ...
- 分析Java的类加载器与ClassLoader(二):classpath与查找类字节码的顺序,分析ExtClassLoader与AppClassLoader的源码
先回顾一下classpath classpath的作用: classpath的作用是指定查找类的路径:当使用java命令执行一个类(类中的main方法)时,会从classpath中进行查找这个类. 指 ...
随机推荐
- Python的网页解析库-PyQuery
PyQuery库也是一个非常强大又灵活的网页解析库,如果你有前端开发经验的,都应该接触过jQuery,那么PyQuery就是你非常绝佳的选择,PyQuery 是 Python 仿照 jQuery 的严 ...
- Centos7安装文件传输软件rz sz
一直使用Xshell的xftp传输文件,谁知道忽然无法正常使用. 于是,决定用户rz进行传输 安装步骤也比较简单 1.首先安装第三方源(以下源比默认源包含更多安装包,建议添加该源使用) yum ins ...
- 怎么去掉织梦(dedecms)网站首页默认携带的index.html尾巴
如果你的网站带小尾巴,比如这样的 那你就要注意了,因为这会导致搜索引擎会认为和不带尾巴的页面是两个页面,都会参与排名,会分散首页的权重. 那我们怎么去除呢? 方法1: 1)在空间面板里面找到默认首页设 ...
- CentOS7开机进入紧急模式EmergencyMode的解决办法
这个情况主要是 修改了 /etc/fstab 文件 vi /etc/fstab 文件 如果之前添加过一行 把添加的一行注释掉 如果之前没有添加过,把挂载到 /home 这一行取消注释 操作之后 记得 ...
- python代码git上传
python代码git上传 1.每次上传代码之前需要先拉取线上的代码 操作如下:
- jquery对div元素进行鼠标移动(稍稍修改下可以实现div跟随鼠标)
/* 网上找了资料都是对于event.clientX和offset().left进行了计算,但是去掉了这个计算方式,直接使用当前坐标也一样,效果都一样不太好 strHeader:标题 jquery定位 ...
- 使用 Redis 的 sorted set 实现用户排行榜
要求:实现一个用户排行榜,用户数量有很多,排行榜存储的是用户玩游戏的分数,对排行榜的读取压力比较大,如何实现? 思路分析: 实现排行榜,可以考虑使用 Redis 的 zset 结构: 用户数量很多的话 ...
- 【C语言】崩溃的双重指针
指针的指针? 前言: 指针的初识点击移步 双重指针: 指向指针的指针是一种多级间接寻址的形式,或者说是一个指针链.通常,一个指针包含一个变量的地址.当我们定义一个指向指针的指针时,第一个指针包含了第二 ...
- EasyExcel读取文件-同步处理数据
读取代码 // 前端传过来的文件 MultipartFile file; InputStream inputStream = file.getInputStream(); // 读取excel数据,边 ...
- 20、Outer Apply 和 Cross Apply
1.場合 select...caseが複雑の時 2.運用方法 SELECT * FROM stu CROSS APPLY ( --like inner join * FROM score WHERE ...