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注解发生了作用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

webor2006

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值