[Kotlin] 单例对象是如何实现的?

Kotlin 中的单例对象是如何实现的?

结论

在类初始化的时候,会对单例对象进行赋值,所以是"饿汉式"。

代码

我们用如下代码进行探索。(请将代码保存为 AreaCalculator.kt)

kotlin 复制代码
object AreaCalculator {
  val PI = Math.PI;

  fun calculateArea(r: Double): Double {
    return PI * r * r;
  }
}

fun foo() {
  AreaCalculator.calculateArea(1.0)
}

如下命令可以编译 AreaCalculator.kt

bash 复制代码
kotlinc AreaCalculator.kt

编译后,会生成以下 class 文件

text 复制代码
AreaCalculator.class
AreaCalculatorKt.class

查看 AreaCalculatorKt 类

如下命令可以查看 AreaCalculatorKt.class 的内容。

bash 复制代码
javap -v -p AreaCalculatorKt

部分结果粘贴如下(常量池等部分已略去)

text 复制代码
{
  public static final void foo();
    descriptor: ()V
    flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
    Code:
      stack=3, locals=0, args_size=0
         0: getstatic     #12                 // Field AreaCalculator.INSTANCE:LAreaCalculator;
         3: dconst_1
         4: invokevirtual #16                 // Method AreaCalculator.calculateArea:(D)D
         7: pop2
         8: return
      LineNumberTable:
        line 10: 0
        line 11: 8
}

假如源码是 java 的,那么它应该是这个样子的 ⬇️

java 复制代码
// 以下代码是我手动转化的,不保证绝对准确,仅供参考

public final class AreaCalculatorKt {
  public static final void foo() {
    AreaCalculator.INSTANCE.calculateArea(1.0d);
  }
}

看来 foo() 方法会

  • 先获取 AreaCalculator 类的单例对象(在 INSTANCE 这个 静态字段 中),
  • 再通过它来调用 calculateArea(double) 方法

那我们继续查看 AreaCalculator 类的内容吧。

查看 AreaCalculator 类

如下命令可以查看 AreaCalculator.class 的内容。

bash 复制代码
javap -v -p AreaCalculator

部分结果粘贴如下(常量池等部分已略去)

text 复制代码
{
  public static final AreaCalculator INSTANCE;
    descriptor: LAreaCalculator;
    flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
    RuntimeInvisibleAnnotations:
      0: #27()
        org.jetbrains.annotations.NotNull

  private static final double PI;
    descriptor: D
    flags: (0x001a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL

  private AreaCalculator();
    descriptor: ()V
    flags: (0x0002) ACC_PRIVATE
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #8                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 1: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   LAreaCalculator;

  public final double getPI();
    descriptor: ()D
    flags: (0x0011) ACC_PUBLIC, ACC_FINAL
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #16                 // Field PI:D
         3: dreturn
      LineNumberTable:
        line 2: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       4     0  this   LAreaCalculator;

  public final double calculateArea(double);
    descriptor: (D)D
    flags: (0x0011) ACC_PUBLIC, ACC_FINAL
    Code:
      stack=4, locals=3, args_size=2
         0: getstatic     #16                 // Field PI:D
         3: dload_1
         4: dmul
         5: dload_1
         6: dmul
         7: dreturn
      LineNumberTable:
        line 5: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       8     0  this   LAreaCalculator;
            0       8     1     r   D

  static {};
    descriptor: ()V
    flags: (0x0008) ACC_STATIC
    Code:
      stack=2, locals=0, args_size=0
         0: new           #2                  // class AreaCalculator
         3: dup
         4: invokespecial #21                 // Method "<init>":()V
         7: putstatic     #24                 // Field INSTANCE:LAreaCalculator;
        10: ldc2_w        #25                 // double 3.141592653589793d
        13: putstatic     #16                 // Field PI:D
        16: return
      LineNumberTable:
        line 2: 10
}

假如源码是 java 的,那么它应该是这个样子的 ⬇️

java 复制代码
// 以下代码是我手动转化的,不保证绝对准确,仅供参考

public final class AreaCalculator {
  // 单例对象
  @org.jetbrains.annotations.NotNull
  public static final AreaCalculator INSTANCE;
  
  private static final double PI;
  
  private AreaCalculator() {
    super();
  }
  
  public final double getPI() {
    return AreaCalculator.PI;
  }
  
  public final double calculateArea(double r) {
    return AreaCalculator.PI * r * r;
  }
  
  // 在初始化的时候,对单例对象进行赋值,所以是"饿汉式"
  static {
    INSTANCE = new AreaCalculator();
    PI = 3.141592653589793d;
  }
}

由此可见,在 AreaCalculator 类进行初始化的时候,INSTANCE 字段会被赋值。 所以是"饿汉式"。

用 Java 代码验证

我们可以用如下 java 代码来进行验证。(请将代码保存为 SimpleTest.java)

java 复制代码
public class SimpleTest {
  public static void main(String[] args) {
    System.out.println(AreaCalculator.INSTANCE.calculateArea(1.0d));
  }
}

如下命令可以编译 SimpleTest.java 并运行其中的 main 方法。

bash 复制代码
javac SimpleTest.java && java SimpleTest

运行结果如下

相关推荐
AntsData24 分钟前
用 12K+ 条亚马逊评论信号,定位机械键盘下一版该改什么
java·大数据·计算机外设
code2cat31 分钟前
【随笔】连续批处理:长短请求怎样在迭代之间补位
java·人工智能·后端·大语言模型·vllm
最强小杰34 分钟前
DeepSeek V4 Flash + Codex CLI 接入教程:原生 Responses API 适配,从配置到跑通收藏这篇就够了(2026)
java·前端·python·ai
Flynn Liang1 小时前
Spring AI 2.0 深度解析:ChatClient、Advisor 链与 MCP——Java 工程师的 AI 落地最优解
java·人工智能·spring·大模型·架构设计·spring ai
灿飞1 小时前
Kotlin与Java虚拟机六:基础与类型系统:一切皆对象,但对象有代价
java·kotlin
计算机毕设定制辅导-无忧学长1 小时前
《基于微信小程序健康习惯激励系统设计与实现》
java·spring boot·mysql·微信小程序·uniapp·健康习惯激励系统
雪芽蓝域zzs2 小时前
第 1 章:IDEA 创建 SpringBoot3.5.16 Maven 项目
java·maven·intellij-idea
golang学习记2 小时前
IDEA 开始换芯,发布全新AI版终端
java·人工智能·intellij-idea
vipxieliang2 小时前
ValidX 正则表达式预编译优化:Pattern 复用策略
java·spring boot·后端
Wang's Blog2 小时前
Java 项目实战:交友项目 - FastDFS 工作原理
java·大数据·交友