【问题标题】:Serialization with singleton design pattern单例设计模式的序列化
【发布时间】:2016-02-25 16:13:14
【问题描述】:

我在使用单例模式对类进行序列化时遇到问题。先介绍一下代码:

import java.io.ObjectStreamException;
import java.io.Serializable;

import org.ejml.simple.SimpleMatrix;

public class Operation implements Serializable {

    private static final long serialVersionUID = 1L;

    private final static int CONSTANT = 10;

    private SimpleMatrix data;
    private Long timestamp;

    private static Operation instance = new Operation ();

    private Operation () {
        data = new SimpleMatrix(1, CONSTANT);
    }

    protected static Operation getInstance() {
        return instance;
    }

    //Hook for not breaking the singleton pattern while deserializing.
    private Object readResolve() throws ObjectStreamException {
          return instance;
    }


    protected void setData(SimpleMatrix matrix) {
        this.data = matrix;
    }

    protected SimpleMatrix getData() {
        return data;
    }

    public Long getTimestamp() {
        return timestamp;
    }

    public void setTimestamp(Long timestamp) {
        this.timestamp = timestamp;
    }
}

我有三个问题希望有人可以帮助我:

  1. 据我所知,静态字段没有序列化。因此,如果我反序列化,我的最终静态字段 CONSTANT 是否设置为 10?如果没有,我该怎么做?这很重要。

  2. 如您所见,在构造函数中创建了一个新矩阵。如果我反序列化,我的data 会被这个构造函数覆盖吗?对于反序列化,我想要序列化版本的 data 而不是新矩阵。构造函数我只需要在序列化前第一次实例化对象。

  3. 在序列化之前,我会将字段timestamp 设置为序列化时间。反序列化后,我想将此字段与某些文件的时间戳进行比较(以查看自序列化以来文件是否已更改)。我应该为文件的序列化时间和最后修改时间使用哪种时间戳,以便我可以轻松比较?

【问题讨论】:

  • 我不太清楚为什么需要序列化 ​​Operation 单例。但我建议:仅序列化矩阵(似乎它是单例中唯一改变的东西),并让单例正常构建。或者更好的是,不要使用单例,使用依赖注入;)
  • @PierreHenry 手术只是一个例子。是的,只有矩阵发生了变化。我怎样才能只序列化矩阵,然后再将矩阵反序列化回单例?
  • @PierreHenry 我还需要序列化时间戳。
  • 添加必要的逻辑作为你的单例方法:例如保存状态和恢复状态。或者在外面做,然后做 setMatrix 或类似的事情。

标签: java serialization deserialization


【解决方案1】:
  1. 静态常量与类相关联,因此您的实例的序列化和反序列化根本不会影响它。

  2. 为了使反序列化工作,您需要将单例的数据设置为反序列化的实例数据:

    private Object readResolve() throws ObjectStreamException {
        instance.setData(getData());
        return instance;
    }
    
  3. 时间戳可以保持为 Long,这很好。使用 System.currentTimeMillis(),您将能够与 File 对象 lastModified() 日期进行比较。序列化时设置字段即可:

    private void writeObject(java.io.ObjectOutputStream out)
        throws IOException{
        timestamp=System.currentTimeMillis();
        out.defaultWriteObject();
    }
    

为了确定我所说的内容,我做了一个测试,在代码中使用字符串而不是矩阵:

public static void main(String[] args) throws Exception {
    Operation op=getInstance();
    op.setData("test1");
    byte[] ds=serialize();
    System.out.println(new Date(getInstance().timestamp));
    op.setData("test2");
    deserialize(ds);
    System.out.println(getInstance().getData());

}

这给了我当前日期和test1,因为反序列化实例已经覆盖了当前实例。 serializedeserialize 只是在实例和字节之间进行转换。

【讨论】:

  • 感谢您的回答。我对第二点有疑问。我是否还必须添加 instance.setTimestamp(getTimestamp());如果我将时间戳设为私有并引入 setter 和 getter?在我的帖子中查看我更改的代码。
  • 是的,我没有在我的代码中这样做,但你也应该设置时间戳。
【解决方案2】:

我建议您采用 Enum Singleton 方法来实现 Singleton,因为处理序列化是免费的。在你的情况下,它会是

public enum Operation {
    INSTANCE; 
    // No need to handle Serialization 
}

Effective Java中引用Joshua Bloch单元素枚举类型是实现单例的最佳方式。

这种方法有很多好处,你可以找到here
还有For instance control, prefer enum types to readResolve

【讨论】:

    猜你喜欢
    • 2017-10-12
    • 2010-11-03
    • 1970-01-01
    • 2019-02-27
    • 1970-01-01
    相关资源
    最近更新 更多