通过注解修改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字节码生成机制详尽分析的更多相关文章

  1. [WebKit内核] JavaScript引擎深度解析--基础篇(一)字节码生成及语法树的构建详情分析

    [WebKit内核] JavaScript引擎深度解析--基础篇(一)字节码生成及语法树的构建详情分析 标签: webkit内核JavaScriptCore 2015-03-26 23:26 2285 ...

  2. Java代理全攻略【有瑕疵:字节码生成部分没看到,最后两节没仔细看,累了】

    Java代理 1.代理模式 定义:给某个对象提供一个代理对象,并由代理对象控制对于原对象的访问,即客户不直接操控原对象,而是通过代理对象间接地操控原对象. 其实就是委托.聚合.中间人. 为了保持行为的 ...

  3. 谁还没遇上过NoClassDefFoundError咋地——浅谈字节码生成与热部署

    谁还没遇上过NoClassDefFoundError咋地--浅谈字节码生成与热部署 前言 在Java程序员的世界里,NoClassDefFoundError是一类相当令人厌恶的错误,因为这类错误通常非 ...

  4. 深入浅出Java探针技术2---java字节码生成框架ASM、Javassist和byte buddy的使用

    目前Java字节码生成框架大致有ASM.Javassist和byte buddy三种 ASM框架介绍及使用 1.ASM介绍 ASM是一种Java字节码操控框架,能够以二进制形式修改已有的类或是生成类, ...

  5. [WebKit内核] JavaScriptCore深度解析--基础篇(一)字节码生成及语法树的构建

    看到HorkeyChen写的文章<[WebKit] JavaScriptCore解析--基础篇(三)从脚本代码到JIT编译的代码实现>,写的很好,深受启发.想补充一些Horkey没有写到的 ...

  6. PHP-7.1 源代码学习:字节码生成 之 "$a = 1"

    前言 本文通过分析 "$a=1" 这个 PHP 语句的编译和执行来窥探 php-cli 解释执行逻辑 准备 参考之前的系列文章,在 ubuntu 环境下下载,编译 PHP 源代码 ...

  7. Flask debug 模式 PIN 码生成机制安全性研究笔记

    Flask debug 模式 PIN 码生成机制安全性研究笔记 0x00 前言 前几天我整理了一个笔记:Flask开启debug模式等于给黑客留了后门,就Flask在生产网络中开启debug模式可能产 ...

  8. 透过字节码生成审视Java动态代理运作机制

    对于动态代理我想应该大家都不陌生,就是可以动态去代理实现某个接口的类来干一些我们自己想要的功能,但是在字节码层面它的表现是如何的呢?既然目前刚好在研究字节码相关的东东,有必要对其从字节码角度来审视一下 ...

  9. 分析Java的类加载器与ClassLoader(二):classpath与查找类字节码的顺序,分析ExtClassLoader与AppClassLoader的源码

    先回顾一下classpath classpath的作用: classpath的作用是指定查找类的路径:当使用java命令执行一个类(类中的main方法)时,会从classpath中进行查找这个类. 指 ...

随机推荐

  1. Entity Framework Core for Console

    包 Microsoft.EntityFrameworkCore Microsoft.EntityFrameworkCore.SqlServer Microsoft.EntityFrameworkCor ...

  2. JobStorage.Current property value has not been initialized. You must set it before using Hangfire Client or Server API.

    JobStorage.Current property value has not been initialized. You must set it before using Hangfire Cl ...

  3. Properties的有序读写

    使用java.util.Properties提供的类,读取properties文件的时候,读出来的是乱序的 如下边的情况 import java.io.*; import java.util.Arra ...

  4. distinct 数组去重,对象去重

    distinct 操作符可以用来去重,将上游重复的数据过滤掉. import { of } from 'rxjs'; import { distinct} from 'rxjs/operators'; ...

  5. jquery下拉单选框可左右移动数据

    <!doctype html> <html> <head> <meta charset="utf-8"> <title> ...

  6. day01——python初始、变量、常量、注释、基础数据类型、输入、if

    python的历史: 04年Django框架诞生了 内存回收机制是什么(面试题) python2:源码不统一,有重复的功能代码 python3:没有重复的功能代码 python是一个什么的编程语言 编 ...

  7. datanode启动异常(Incompatible clusterIDs)

    问题: 正常start-all.sh无法启动datanode进程,但是./hadoop-daemon.sh start datanode又可以启动.过一会后datanode进程又莫名消失. 原理: 多 ...

  8. 第3课,python使用for循环

    前言: 学习了python的while循环后感觉循环是挺强大的.下面学习一个更智能,更强大的循环-- for循环. 课程内容: 1.由while循环,到for循环,格式和注意项 2.for循环来报数 ...

  9. libevent源码分析三--signal事件响应

    libevent支持io事件,timeout事件,signal事件,这篇文件将分析libevent是如何组织signal事件,以及如何实现signal事件响应的. 1.  sigmap 类似于io事件 ...

  10. 介绍ArcGIS中各种数据的打开方法——mdb(个人数据库)

    3.打开存储在Access GeoDatabase的要素类 使用工作空间打开一个Access库中的一个要素类. private void OpenWorkspaceFromFileAccess(str ...