1. 概述
在本篇教程中,我们将深入探讨 Kotlin 中 类(class) 与 对象(object) 的区别,尤其是它们在语言层面和字节码层面的不同表现。
我们会通过具体代码和反编译分析,来帮助我们理解其背后的设计机制。
2. 类的表示方式
Kotlin 中的类与 Java 类非常相似:它们是用于创建实例的模板。
class Person {
// 省略内容
}
在编译后,Kotlin 编译器会生成一个标准的 Java 字节码类,例如:
>> kotlinc Person.kt
>> javap -c -p -v com.baeldung.classobject.Person
public final class com.baeldung.classobject.Person
// omitted
flags: (0x0031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER
this_class: #2 // com/baeldung/classobject/Person
super_class: #4 // java/lang/Object
interfaces: 0, fields: 0, methods: 1, attributes: 2
✅ 注意: Kotlin 类默认是 final
的,这意味着它们不能被继承,除非你显式使用 open
关键字。
我们可以像 Java 那样从具体类中创建实例:
val p = Person()
同时,只要类是 open
的,我们也可以继承它:
open class Person {
// 省略内容
}
class User : Person() {
// 省略内容
}
3. 单例对象(Singleton Objects)
在某些场景下,我们希望某个类只能有一个实例存在,这就是所谓的 单例模式。在 Kotlin 中,我们可以通过 object
声明来实现这一点:
object FileSystem {
fun createTempFile() {
// 省略实现
}
}
由于 FileSystem
是一个单例对象,它的成员可以像 Java 中的静态成员一样被访问,无需实例化:
fun main() {
FileSystem.createTempFile()
}
❌ 不能通过构造函数创建实例:
val f = FileSystem() // ❌ 编译错误
✅ 可以实现接口或继承类:
object FileSystem : Serializable {
// 省略内容
}
❌ 但不能被继承:
class NTFS : FileSystem() // ❌ 编译错误
3.1. 字节码表示
在底层,Kotlin 编译器会将 object
编译成一个 final 类,并且构造函数是 private 的:
>> kotlinc FileSystem.kt
>> javap -c -p com.baeldung.classobject.FileSystem
public final class com.baeldung.classobject.FileSystem {
private com.baeldung.classobject.FileSystem();
Code:
0: aload_0
1: invokespecial #11 // Method java/lang/Object."<init>":()V
4: return
}
它通过一个 static final 变量来持有该单例:
public final class com.baeldung.classobject.FileSystem {
public static final com.baeldung.classobject.FileSystem INSTANCE;
// omitted
}
初始化时,Kotlin 会生成一个静态代码块来创建实例并赋值:
static {};
Code:
0: new #2 // class com/baeldung/classobject/FileSystem
3: dup
4: invokespecial #26 // Method "<init>":()V
7: astore_0
8: aload_0
9: putstatic #28 // Field INSTANCE:Lcom/baeldung/classobject/FileSystem;
12: return
这与 Java 中常见的单例实现方式非常类似。
4. 总结
本文我们从语言层面和字节码层面对比了 Kotlin 中类与对象的区别,总结如下:
- ✅ 类可以被实例化,而对象是单例,不能被实例化
- ✅ 类可以被继承(前提是
open
),而对象不能被继承 - ✅ 对象的底层实现是通过私有构造器 + 静态实例完成的,类似于 Java 单例
如果你希望设计一个全局唯一实例的组件,使用 object
是最简洁、安全的方式。
如需查看完整示例代码,可以访问 GitHub 仓库。