【问题标题】:How to serialize static data members of a Java class?如何序列化 Java 类的静态数据成员?
【发布时间】:2010-11-03 17:28:45
【问题描述】:

我们序列化对象的时候,静态成员是不序列化的,但是如果需要,有什么办法吗?

【问题讨论】:

  • 停止对应该序列化的数据使用静态成员?
  • 附录:停止使用静态成员来保持可变状态。
  • 共享和可变不是相互排斥的。序列化静态常量没有意义(因为它们是不可变的),但序列化静态变量有时是有意义的(因为它们应该与反序列化时的其余状态同步)。
  • 弗拉基米尔,不,序列化静态字段永远没有意义。
  • 说“从不”是没有意义的。

标签: java serialization static-members


【解决方案1】:

是的,我们可以序列化静态变量。但是我们可以编写自己的writeObject()readObject()。我认为这可以解决问题。

【讨论】:

    【解决方案2】:

    这是静态字段的序列化:newBookingNumber。

    class Booking implements Serializable
    {
    
        /**
         * Generated serial version ID.
         */
    
        private static final long serialVersionUID = 5316748056989930874L;
    
        // To hold new booking number.
        private static int newBookingNumber = 0;
    
        // The booking number.
        private int bookingNumber;
    
    
        /* 
         * Default serializable fields of a class are defined to be 
         * the non-transient and non-static fields. So, we have to 
         * write and read the static field separately.
         */
        private void writeObject(ObjectOutputStream oos)
            throws IOException 
        {
            oos.defaultWriteObject();
            oos.writeObject(new Integer(newBookingNumber));
        }
    
        private void readObject(ObjectInputStream ois)
        throws ClassNotFoundException, IOException 
        {
            ois.defaultReadObject();
            newBookingNumber = (Integer)ois.readObject();
        }
    }
    

    【讨论】:

      【解决方案3】:

      您无需在每次更改字段时手动更新您的课程即可执行此操作。 如果您希望静态成员能够轻松访问应用程序中的设置,但也希望保存这些设置,则可能需要执行此操作。 在这种情况下,您还希望可以选择随心所欲地应用它们,而不是默认加载,因为这里的其他解决方案需要,因为它们是静态的。这允许出于显而易见的原因回滚设置。

      基本上,使用字段方法获取类中的所有成员,然后将这些字段的全名映射到内容。由于 Field 本身不可序列化,因此需要全名。序列化此映射,然后恢复它以获取保存的设置。

      谜题的第二部分是 apply() 类型的函数。这通过映射,并将它可以应用到静态类。

      您还必须确保静态成员的内容本身是可序列化的。

      希望从这个示例类中可以看出,静态成员可以很容易地保存和返回。我将让实现者来担心类的 UID、安全措施等。 isSameAs() 用于单元测试。 AppSettings 是包含您希望序列化的所有静态字段的类。

      public class AppSettingsReflectorSaver implements Serializable {
      
      HashMap<String, Object> genericNamesAndContents = new HashMap<String, Object>();
      private AppSettingsReflectorSaver() {
      }
      
      static AppSettingsReflectorSaver createAppSettingsSaver() {
          AppSettingsReflectorSaver ret = new AppSettingsReflectorSaver();
          ret.copyAppSettings();
          return ret;
      }
      
      private void copyAppSettings() {
          Field[] fields = AppSettings.class.getFields();
          for (Field field : fields) {
              mapContentsForSerialization(field);
          }
      }
      
      private void mapContentsForSerialization(Field field) {
          try {
              Object fieldContents = field.get(AppSettings.class);
              genericNamesAndContents.put(field.toGenericString(), fieldContents);
          } catch (IllegalArgumentException ex) {
              Logger.getLogger(AppSettingsReflectorSaver.class.getName()).log(Level.SEVERE, null, ex);
          } catch (IllegalAccessException ex) {
              Logger.getLogger(AppSettingsReflectorSaver.class.getName()).log(Level.SEVERE, null, ex);
          }
      }
      
      boolean isSameAs(AppSettingsReflectorSaver now) {
          for( String thisKey : genericNamesAndContents.keySet()){
              boolean otherHasThisKey = now.genericNamesAndContents.containsKey(thisKey);
              Object thisObject = genericNamesAndContents.get(thisKey);
              Object otherObject = now.genericNamesAndContents.get(thisKey);
              boolean otherHasThisValue = thisObject.equals(otherObject);
              if (!otherHasThisKey || !otherHasThisValue){
                  return false;
              }
          }
          return true;
      }
      
      void applySavedSettingsToStatic() {
          Field[] fields = AppSettings.class.getFields();
          for (Field field : fields) {
              if (!genericNamesAndContents.containsKey(field.toGenericString())){
                  continue;
              }
              Object content = genericNamesAndContents.get(field.toGenericString() );
              try {
                  field.set(AppSettings.class, content);
              } catch (IllegalArgumentException ex) {
                  Logger.getLogger(AppSettingsReflectorSaver.class.getName()).log(Level.SEVERE, null, ex);
              } catch (IllegalAccessException ex) {
                  Logger.getLogger(AppSettingsReflectorSaver.class.getName()).log(Level.SEVERE, null, ex);
              }
          }
      }
      

      }

      这是我的第一篇文章 - 放轻松:P~

      【讨论】:

        【解决方案4】:

        要实现紧凑的实现,请在您的类中实现 readObject 和 writeObject,在处理正常序列化的方法中调用 defaultReadObject 和 defaultWriteObject 方法,然后继续序列化和反序列化您需要的任何其他字段。

        问候, GK

        【讨论】:

          【解决方案5】:

          伙计们,静态并不意味着不可变。例如,我可能想要序列化整个计算状态(是的,包括静态字段——计数器等),以便稍后在 JVM 和/或主机重新启动后恢复。

          如前所述,正确的答案是使用 Externalizable,而不是 Serializable 接口。然后你就可以完全控制你外化的内容和方式。

          【讨论】:

          • 如果计算状态没有嵌入到您正在序列化的对象中,那么您不应该将其存储为该对象序列化的一部分。
          • 可变静态一般意味着损坏。
          • 再想一想。例如,多线程遗传算法怎么样?共享(静态)存储用于保存评估变体的注册表。让它成为单身人士?可能,但它有自己的缺点。
          • 是的。使其成为单例。序列化单例。或者,更好的是,摆脱共享静态存储,将其存储在显式上下文对象中。
          • ... 然后另一群纯粹主义者会抱怨 Singleton 是反模式,我也应该摆脱它。至于“显式上下文对象”,不同线程如何访问呢?通过静态方法?好点了吗?
          【解决方案6】:

          好的答案和cmets——不要这样做。但是怎么做呢?

          您最好创建一个对象来保存所有“静态”。该对象可能也应该具有您的类中的任何静态方法。

          您的类的每个实例都可以容纳另一个类——或者,如果您确实需要,您可以将其设置为任何成员都可以访问的单例。

          在你做了这个重构之后,你会发现它应该一直这样做。您甚至可能会发现,之前在 subconsicionce 级别上困扰您的一些设计约束已经消失。

          您可能会发现此解决方案还解决了您尚未注意到的其他序列化问题。

          【讨论】:

            【解决方案7】:

            您可以通过实现来控制序列化:

            private void writeObject(ObjectOutputStream out) throws IOException;
            
            private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException;
            

            有序列化的完整描述http://java.sun.com/developer/technicalArticles/Programming/serialization/

            正如其他答案所说,序列化静态数据并没有真正意义,因为它是对象而不是您要序列化的类,并且需要这样做闻起来就像您的代码对我有其他问题。

            【讨论】:

            • 您可以控制序列化,但是放置静态数据没有任何意义,因为它在上下文中没有任何意义。
            • 由于我们不知道上下文,我们不能说它是否有意义。例如,在使用默认序列化之后,将静态成员添加到流中可能是有意义的。反序列化时,可以设置静态成员为null,否则丢弃序列化对象。
            • 汤姆,你在滥用“重复证明”,不是吗? ;)
            • 有没有办法通过序列化我的类的类对象来序列化静态成员。
            【解决方案8】:

            静态成员属于类,不属于单个对象。

            你应该重新考虑你的数据结构。

            【讨论】:

              【解决方案9】:

              第一个问题是为什么需要序列化静态成员?

              静态成员与类相关联,而不是与实例相关联,因此在序列化实例时包含它们没有意义。

              第一个解决方案是使这些成员不是静态的。或者,如果这些成员在原始类和目标类中是相同的(相同的类,但可能是不同的运行时环境),则根本不要序列化它们。

              我对如何跨静态成员发送有一些想法,但我首先需要查看用例,因为在所有情况下都意味着更新目标类,我还没有找到这样做的充分理由.

              【讨论】:

              • 假设我想计算像 Car 这样的类的实例数。而且我没有使用数据库。所以在这种情况下,应用程序关闭后我需要将此信息与其他信息一起存储。
              • 您需要直接存储该号码吗?如果您在反序列化每辆汽车时更新数字(通过覆盖 readObject),它将正确反映序列化中的汽车数量。
              • 静态变量在类对象的所有实例之间共享(共享内存)。如果你需要保存一些静态的东西,比如让你有一个不同值的枚举,并且不时更改该枚举类型的静态值,更改保存的值可能就像让所有类设置一个局部变量一样简单到那种类型。或者你可以更进一步,让一堆类扩展类Whatever,并且类Whatever可以只与它的所有子类共享一个局部变量。当您反序列化时,您可以将静态值重新加载回原来的状态。
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2016-10-15
              • 2016-07-13
              • 2010-10-14
              • 1970-01-01
              • 2014-09-01
              相关资源
              最近更新 更多