对比一下 java 和 kotlin 编译源码文件(source.java 或 source.kt) 到 class 的区别。这种区别产生的结果是什么,看看其中的优缺点。
public class Hello { public static void main(String[] args) { System.out.println("hello world"); } }
以上代码执行 javac Hello.java 将生成 Hello.class 文件,用 javap 命令对生成的 Hello.class 文件做一下解析,得到结果
Classfile /Users/mac/Desktop/javadir/Hello.class Last modified 2019-8-24; size 415 bytes MD5 checksum cb7981a0b53212d3f3005746f14b245a Compiled from "Hello.java" public class Hello minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref #6.#15 // java/lang/Object."<init>":()V #2 = Fieldref #16.#17 // java/lang/System.out:Ljava/io/PrintStream; #3 = String #18 // hello world #4 = Methodref #19.#20 // java/io/PrintStream.println:(Ljava/lang/String;)V #5 = Class #21 // Hello #6 = Class #22 // java/lang/Object #7 = Utf8 <init> #8 = Utf8 ()V #9 = Utf8 Code #10 = Utf8 LineNumberTable #11 = Utf8 main #12 = Utf8 ([Ljava/lang/String;)V #13 = Utf8 SourceFile #14 = Utf8 Hello.java #15 = NameAndType #7:#8 // "<init>":()V #16 = Class #23 // java/lang/System #17 = NameAndType #24:#25 // out:Ljava/io/PrintStream; #18 = Utf8 hello world #19 = Class #26 // java/io/PrintStream #20 = NameAndType #27:#28 // println:(Ljava/lang/String;)V #21 = Utf8 Hello #22 = Utf8 java/lang/Object #23 = Utf8 java/lang/System #24 = Utf8 out #25 = Utf8 Ljava/io/PrintStream; #26 = Utf8 java/io/PrintStream #27 = Utf8 println #28 = Utf8 (Ljava/lang/String;)V { public Hello(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 1: 0 public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=1, args_size=1 0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #3 // String hello world 5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return LineNumberTable: line 3: 0 line 4: 8 } SourceFile: "Hello.java"
可以得到一些关键信息:
下面看看,用 Kotlin 语言实现同样的功能,然后编译之后的.class 文件又是怎样的。
命令行输入
brew install kotlin
object Hello1 { @JvmStatic fun main(args: Array<String>) { println("hello world") } }
这段代码和上述 Hello.java 是实现了同样的功能,方法参数也是一样。
以上代码执行 kotlinc Hello1.kt 后将生成 Hello1.class 文件,用 javap 命令对生成的 Hello1.class 文件做一下解析,得到结果
Classfile /Users/mac/Desktop/javadir/Hello1.class Last modified 2019-8-24; size 1236 bytes MD5 checksum 884060fdf34740300f1c8d187afb4c6a Compiled from "Hello1.kt" public final class Hello1 minor version: 0 major version: 50 flags: ACC_PUBLIC, ACC_FINAL, ACC_SUPER Constant pool: #1 = Utf8 Hello1 #2 = Class #1 // Hello1 #3 = Utf8 java/lang/Object #4 = Class #3 // java/lang/Object #5 = Utf8 main #6 = Utf8 ([Ljava/lang/String;)V #7 = Utf8 Lkotlin/jvm/JvmStatic; #8 = Utf8 Lorg/jetbrains/annotations/NotNull; #9 = Utf8 args #10 = String #9 // args #11 = Utf8 kotlin/jvm/internal/Intrinsics #12 = Class #11 // kotlin/jvm/internal/Intrinsics #13 = Utf8 checkParameterIsNotNull #14 = Utf8 (Ljava/lang/Object;Ljava/lang/String;)V #15 = NameAndType #13:#14 // checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V #16 = Methodref #12.#15 // kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V #17 = Utf8 hello world #18 = String #17 // hello world #19 = Utf8 java/lang/System #20 = Class #19 // java/lang/System #21 = Utf8 out #22 = Utf8 Ljava/io/PrintStream; #23 = NameAndType #21:#22 // out:Ljava/io/PrintStream; #24 = Fieldref #20.#23 // java/lang/System.out:Ljava/io/PrintStream; #25 = Utf8 java/io/PrintStream #26 = Class #25 // java/io/PrintStream #27 = Utf8 println #28 = Utf8 (Ljava/lang/Object;)V #29 = NameAndType #27:#28 // println:(Ljava/lang/Object;)V #30 = Methodref #26.#29 // java/io/PrintStream.println:(Ljava/lang/Object;)V #31 = Utf8 [Ljava/lang/String; #32 = Utf8 <init> #33 = Utf8 ()V #34 = NameAndType #32:#33 // "<init>":()V #35 = Methodref #4.#34 // java/lang/Object."<init>":()V #36 = Utf8 this #37 = Utf8 LHello1; #38 = Utf8 INSTANCE #39 = Utf8 <clinit> #40 = Utf8 Lkotlin/Metadata; #41 = Utf8 mv #42 = Integer 1 #43 = Integer 15 #44 = Utf8 bv #45 = Integer 0 #46 = Integer 3 #47 = Utf8 k #48 = Utf8 d1 #49 = Utf8 /n/n /n/n/n /n/n/n Æ 20B¢J0200H¢ #50 = Utf8 d2 #51 = Utf8 #52 = Methodref #2.#34 // Hello1."<init>":()V #53 = NameAndType #38:#37 // INSTANCE:LHello1; #54 = Fieldref #2.#53 // Hello1.INSTANCE:LHello1; #55 = Utf8 Hello1.kt #56 = Utf8 Code #57 = Utf8 LineNumberTable #58 = Utf8 LocalVariableTable #59 = Utf8 RuntimeVisibleAnnotations #60 = Utf8 RuntimeInvisibleParameterAnnotations #61 = Utf8 SourceFile #62 = Utf8 SourceDebugExtension { public static final Hello1 INSTANCE; descriptor: LHello1; flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL public static final void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL Code: stack=2, locals=3, args_size=1 0: aload_0 1: ldc #10 // String args 3: invokestatic #16 // Method kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V 6: ldc #18 // String hello world 8: astore_1 9: iconst_0 10: istore_2 11: getstatic #24 // Field java/lang/System.out:Ljava/io/PrintStream; 14: aload_1 15: invokevirtual #30 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V 18: return LineNumberTable: line 4: 6 line 5: 18 LocalVariableTable: Start Length Slot Name Signature 0 19 0 args [Ljava/lang/String; RuntimeVisibleAnnotations: 0: #7() RuntimeInvisibleParameterAnnotations: 0: 0: #8() static {}; descriptor: ()V flags: ACC_STATIC Code: stack=2, locals=1, args_size=0 0: new #2 // class Hello1 3: dup 4: invokespecial #52 // Method "<init>":()V 7: astore_0 8: aload_0 9: putstatic #54 // Field INSTANCE:LHello1; 12: return LineNumberTable: line 1: 0 } SourceFile: "Hello1.kt" SourceDebugExtension: SMAP Hello1.kt Kotlin *S Kotlin *F + 1 Hello1.kt Hello1 *L 1#1,6:1 *E RuntimeVisibleAnnotations: 0: #40(#41=[I#42,I#42,I#43],#44=[I#42,I#45,I#46],#47=I#42,#48=[s#49],#50=[s#37,s#51,s#33,s#5,s#51,s#9,s#51,s#51,s#6])
可以得到一些关键信息:
最终直观感受一下:
-rw-r--r-- 1 mac staff 415 8 24 09:37 Hello.class -rw-r--r--@ 1 mac staff 114 8 24 08:37 Hello.java -rw-r--r-- 1 mac staff 1236 8 24 09:37 Hello1.class -rw-r--r-- 1 mac staff 92 8 24 09:34 Hello1.kt
用 kotlin 实现和 java 代码相同的功能,kotlin 在源文件上是占优的,这也是 kotlin 的优势,代码简洁,但是产生的代价就是 class 文件的变大(当然这里的为了实现 static 的 main 方法,使用了类似单例的写法,可能不太恰当,但是总体趋势是类似的)
我们知道 javac 编译生成的 xxx.class 文件是可以通过 java xxx 直接运行的,那么 kotlinc 生成的 yyy.class 文件改怎么运行呢?可以直接用 java yyy 吗?我们试一下
──> java Hello1 ──(六, 824)─┘ Exception in thread "main" java.lang.NoClassDefFoundError: kotlin/jvm/internal/Intrinsics at Hello1.main(Hello1.kt)
结果是不行的,虽然源码可以互相调用,但是 class 文件还的各来各的。因此需要用 kotlin yyy
──> kotlin Hello1 1 ↵ ──(六, 824)─┘ hello world