【问题标题】:Understanding singletons class in Java理解 Java 中的单例类
【发布时间】:2016-05-06 10:11:52
【问题描述】:

在阅读 Effective Java 时,我发现实现单例最有用的方法就是使用 enums 和单个 INSTANCE。但如果我们需要将其设为一个类,根据他的一些建议,完美的可序列化线程安全单例类应该如下所示:

public class SerializableSingletonClass implements Serializable{
    private static final long serialVersionUID = 1L;
    private int value;
    private String name;

    private SerializableSingletonClass(int value, String name) {
        if( value < 0 ) throw new IllegalArgumentException("Value may not be less than 0");
        this.value = value;
        this.name = Validate.notNull(name, "Name may not be null");
    }

    private static class SerializableSingletonHolder{
        public static final SerializableSingletonClass INSTANCE;
        static {
            INSTANCE = new SerializableSingletonClass(0, "default");
        }
    }

    private void readObject(ObjectInputStream stream) throws InvalidObjectException{
        throw new InvalidObjectException("proxy required");
    }

    private Object writeReplace(){
        return new SerializationProxy(this);
    }

    private static class SerializationProxy implements Serializable{
        private static final long serialVersionUID = 1L;
        public SerializationProxy(SerializableSingletonClass ignored) { } //Here is the question

        private Object readResolve(){
            return SerializableSingletonHolder.INSTANCE;
        }
    }
}

据我所知,他建议将序列化实例替换为当前使用的实例。

那么,为什么我们需要使单例序列化,因为它们的序列形式永远不会被使用。我们只是把它扔掉,用当前的实例替换反序列化的实例。

对我来说,实现可序列化单例的问题看起来只是一个理论上的问题。根据我所说,反序列化单例没有多大意义(在enums 的情况下也是如此)。

那么我错过了什么?不能再详细一点吗?

【问题讨论】:

    标签: java serialization singleton


    【解决方案1】:

    枚举也是可序列化的,但不是它们的字段。

    我认为保持简单和易于维护很重要,例如如果要保存,请使用文本文件。

    我会添加一个方法,如保存和加载(可能带有文件名)

    enum SaveableSingleton {
        INSTANCE;
    
        String name;
        int value;
    
        public void load(String filename) throws IOException {
            try (Scanner scanner = new Scanner(new File(filename))) {
                name = scanner.nexLine();
                value = scanner.nextInt();
            }
        }
    
        public void save(String filename) throws IOException {
            try (PrintWriter pw = new PrintWriter(new File(filename))) {
                pw.println(name);
                pw.println(value);
            }
        }
    }
    

    创建一个类似的文件

    my-name-is
    12345
    

    【讨论】:

    • 是的,但是为什么我们需要序列化INSTANCE,然后再反序列化呢?在您的示例中,您并没有完全序列化单例实例,而是将一些有用的信息保存到文件中。这绝对是有道理的。但是序列化INSTANCE 对我来说似乎完全没有意义。
    • @DmitriiBundin 只有在您计划序列化多个对象时才有意义,例如单例映射,但是当您反序列化对象时,您必须再次设置单例。
    • @DmitriiBundin 当你反序列化一个单例时,你会得到另一个。 o_O
    【解决方案2】:

    如果您在存储此单例实例的其他可序列化类中有一些字段并想要序列化/反序列化该类,则始终必须标记该字段transient 并确保在反序列化时手动填充它。如果您在 Singleton 中处理它,则不必这样做。我想这就是可序列化单例的原因。

    【讨论】:

    • 其实是的,但是如果是序列化代理,我认为不需要。我们不完全序列化类,而是它的代理。所以,默认的序列化表单效果很好。
    猜你喜欢
    • 2015-02-18
    • 1970-01-01
    • 1970-01-01
    • 2023-01-08
    • 1970-01-01
    • 2013-03-13
    • 1970-01-01
    • 2023-03-11
    • 1970-01-01
    相关资源
    最近更新 更多