原代码
java
// Singleton.java
public enum Singleton {
INSTANCE;
private String data = "Hello World";
public void doSomething() {
System.out.println("Doing something with: " + data);
}
}
反编译后的结果
java
// 反编译后的 Singleton 类
public final class Singleton extends java.lang.Enum<Singleton> {
// 枚举实例 - 静态final常量
public static final Singleton INSTANCE;
// 静态代码块 - 在类加载时初始化枚举实例
static {
// 创建枚举实例,传入名称和序号
INSTANCE = new Singleton("INSTANCE", 0);
// 将所有枚举值放入数组
$VALUES = new Singleton[]{INSTANCE};
}
// 私有字段
private String data;
// 私有构造方法 - 这是关键!
private Singleton(String name, int ordinal) {
// 调用父类Enum的构造方法
super(name, ordinal);
// 初始化实例字段
this.data = "Hello World";
}
// 我们自定义的方法
public void doSomething() {
System.out.println("Doing something with: " + this.data);
}
// 自动生成的方法 - 获取所有枚举值
public static Singleton[] values() {
return (Singleton[])$VALUES.clone();
}
// 自动生成的方法 - 通过名称获取枚举值
public static Singleton valueOf(String name) {
return (Singleton)java.lang.Enum.valueOf(Singleton.class, name);
}
// 内部使用的数组
private static final Singleton[] $VALUES;
}
为什么枚举是完美的单例
java
1、final 类 - 无法被继承
public final class Singleton extends java.lang.Enum<Singleton>
// final 修饰符防止被继承,保护单例完整性
2、私有构造方法 - 防止外部实例化
private Singleton(String name, int ordinal)
// 构造方法是私有的,且接受两个参数(名称和序号)
// 这解释了为什么不能通过反射创建枚举实例:
// 反射需要知道确切的参数类型,但枚举的构造器是特殊的
3、静态代码块初始化 - 线程安全
static {
INSTANCE = new Singleton("INSTANCE", 0);
$VALUES = new Singleton[]{INSTANCE};
}
// JVM 保证静态代码块的线程安全
// 枚举实例在类加载时创建,且只创建一次
4、特殊的序列化处理
枚举的序列化机制是特殊的:
序列化时:只写入枚举的名称
反序列化时:通过 valueOf()方法根据名称获取已有的枚举实例
这保证了反序列化后仍然是同一个实例