对比一下 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