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

  • 相关阅读:
    sed 删除最后几行 和删除指定行 awk使用
    nagios监控
    nginx服务启动脚本
    Linux修改环境变量的4种方法
    源码编译apache报错的解决方法
    apache源码安装及启动脚本添加
    haproxy配置
    awk命令2
    awk命令1
    通配符及变量
  • 原文地址:https://www.cnblogs.com/webor2006/p/11551189.html
Copyright © 2020-2023  润新知