枚举的hashcode跨JVM不稳定

本文探讨了在Java中使用枚举类型的HashCode作为版本号时遇到的问题:即枚举的HashCode在不同JVM实例间不稳定性。介绍了枚举HashCode的工作原理,并提供了一种通过转换为字符串来稳定HashCode的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

碰到的问题: 在工程中试图给你一个POJO添加一个版本号的属性,由于考虑时间因素因此选择了用这个对象的一些属性的hashcode作为版本号,如果那些属性都没有改变那么这个hashcode不应该改变,反之则改变. 在这些属性中偏巧..有一些是枚举类型. 在测试中发现,如果使用同一个JVM一直调用程序,那么该hashcode一直相同,但是如果重启服务器,开新的JVM,那么这个hashcode将会改变,即使对应的Object的任何属性都没有改变.这就是枚举的hashcode跨JVM不稳定.

调查发现: 在JDK中的Enum.class中 hashcode方法是final的并且直接调用父类Object.class的hashcode方法

/**
* Returns a hash code for this enum constant.
*
* @return a hash code for this enum constant.
*/
public final int hashCode() { //不允许子类重写
return super.hashCode();
}


这样,枚举的hashcode就和内存地址挂钩也就可能会产生不稳定.google了下这个问题,已经有人给oracle提出bug要求将上面的方法去掉final修饰,不过oracle拒绝了 - -

个人目前的解决方法是调用枚举的name()方法将其转化为String,String的hashcode是稳定的.

一个记录.
### 枚举类型实现单例模式 在 Java 中,枚举类型(`enum`)可以通过其语言特性来实现单例模式。这种实现方式不仅简单而且安全,能够防止反序列化攻击以及反射机制破坏单例的完整性。 #### 定义枚举单例 以下是一个典型的枚举单例模式的定义: ```java public enum SingletonEnum { INSTANCE; public void doSomething() { System.out.println("执行某些操作..."); } } ``` 上述代码中,`SingletonEnum` 是一个枚举类型,其中 `INSTANCE` 是它的唯一实例。由于枚举类型的特殊性,在 JVM 加载时会自动创建并初始化此实例,并且无法通过其他方式再创建新的实例[^1]。 #### 调用方法示例 下面展示了如何使用枚举单例模式中的实例及其方法: ```java package java_mode_06; public class Main { public static void main(String[] args) { // 获取单例实例 SingletonEnum singleton = SingletonEnum.INSTANCE; SingletonEnum singleton1 = SingletonEnum.INSTANCE; // 执行方法 singleton.doSomething(); // 验证是否为同一个实例 System.out.println("单例1:" + singleton); System.out.println("单例2:" + singleton1); // 对比非单例对象的行为 Test test = new Test(); Test test1 = new Test(); System.out.println("非单例1:" + test); System.out.println("非单例2:" + test1); } } class Test {} ``` 运行以上程序后,输出的结果如下所示: ``` 执行某些操作... 单例1:SingletonEnum.INSTANCE 单例2:SingletonEnum.INSTANCE 非单例1:Test@<hashcode> 非单例2:Test@<another_hashcode> ``` 可以看到,无论多少次访问 `SingletonEnum.INSTANCE`,得到的始终是同一个实例;而普通的类则每次都会生成不同的新实例[^2]。 #### 序列化的安全性 当涉及到对象的序列化与反序列化时,传统的单例模式可能会被破坏,因为反序列化过程通常会产生一个新的实例。然而,对于枚举类型的单例来说,即使经过反序列化处理,仍然只会返回原始的那个单一实例。这是因为 JDK 的内部机制已经针对这种情况进行了优化——即任何尝试重新构建枚举实例的操作都将失败,转而返回已存在的那个枚举成员[^3]。 如果需要进一步增强对序列化场景的支持,《Effective Java》一书中提到可以在必要情况下覆盖 `readResolve()` 方法以确保实例控制的有效性。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值