【问题标题】:Why Enum singleton are serialization safe?为什么枚举单例是序列化安全的?
【发布时间】:2015-08-20 16:47:11
【问题描述】:

在内部如何在 Enum 中进行序列化/反序列化? jvm如何在(序列化)之前和之后(反序列化)生成相同的哈希码?

【问题讨论】:

    标签: java serialization enums deserialization hashcode


    【解决方案1】:

    序列化特别对待enums。基本上,它只存储对其class 的引用和常量的名称。反序列化时,此信息用于查找 enum 类型的现有运行时对象。

    因此,如果您在同一运行时内反序列化 enum 常量,您将获得已序列化的同一运行时实例。

    但是,在另一个 JVM 中反序列化时,哈希码可能会有所不同。但是具有相同的哈希码并不是单例的必需标准。重要的一点是永远不要拥有该类的另一个实例,这是可以保证的,因为序列化实现永远不会创建 enum 类型的实例,而只会查找现有常量。

    【讨论】:

      【解决方案2】:

      在内部如何在 Enum 中进行序列化/反序列化?

      您不能在 Java 中序列化枚举。您只能序列化对枚举的引用。这意味着如果您的枚举有任何私有字段,它们将不会被写入或恢复。

      jvm如何在(序列化)之前和之后(反序列化)生成相同的hashcode?​​p>

      在 JLS 中并没有具体说明 JVM 如何生成 hashCode,实际上在源代码中没有指定多个实现。但是Oracle 8 update 45 JVM在重启后会以相同的顺序生成相同的hashCodes。

      Object[] objs = new Object[5];
      for(int i=0;i<objs.length;i++) {
          objs[i] = new Object();
      }
      RetentionPolicy[] values = RetentionPolicy.values();
      System.out.println(objs+": "+objs.hashCode());
      for (Object obj : objs) {
          System.out.println(obj+": "+obj.hashCode());
      }
      for (RetentionPolicy policy : values) {
          System.out.println(policy+": "+policy.hashCode());
      }
      

      如果你用new Object[5] 运行它,它会打印出来

      [Ljava.lang.Object;@677327b6: 1735600054
      java.lang.Object@14ae5a5: 21685669
      java.lang.Object@7f31245a: 2133927002
      java.lang.Object@6d6f6e28: 1836019240
      java.lang.Object@135fbaa4: 325040804
      java.lang.Object@45ee12a7: 1173230247
      SOURCE: 856419764
      CLASS: 621009875
      RUNTIME: 1265094477
      

      如果我将数组大小减小到 2,它会打印出来。

      [Ljava.lang.Object;@677327b6: 1735600054
      java.lang.Object@14ae5a5: 21685669
      java.lang.Object@7f31245a: 2133927002
      SOURCE: 1836019240
      CLASS: 325040804
      RUNTIME: 1173230247
      

      注意它是如何以相同的顺序产生相同的 hashCodes 的。如果我将Object[] 代码完全注释掉。

      SOURCE: 1735600054
      CLASS: 21685669
      RUNTIME: 2133927002
      

      每次运行程序时看到相同的 hashCode 的原因是它在检查枚举的 hashCode 之前创建了相同数量的 hashCode。

      注意:对象没有系统 hashCode,除非您要求它以降低创建对象的成本。

      【讨论】:

        【解决方案3】:

        我写了一个简单的枚举类。 例如:

        public enum EnumTest{
            
            IND(1),
            ORG(2);
            
            int deger;
            
            EnumTest(int deger){
                this.deger = deger;
            }
            
        }
        

        编译完这个枚举类后,我们可以通过javap EnumTest命令使用javap工具。

          public final class EnumTest extends java.lang.Enum{
            public static final EnumTest IND;
            public static final EnumTest ORG;
            int deger;
            public static EnumTest[] values();
            public static EnumTest valueOf(java.lang.String);
            static {};
        }
        

        从上面javap工具生成的文本可以看出,枚举实例是静态的,静态变量不能序列化。

        【讨论】:

        • 如果您可以选择从控制台生成的文本并将其粘贴,而不是图像,这将很有帮助,这样它就可以被例如阅读。屏幕阅读器和那些选择使用不同/更大字体的人。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-10-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多