阅读Effective Java时,我发现实现单例最有用的方法就是将enum
与单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;
}
}
}
据我所知,他建议只使用当前使用的实例替换序列化实例。
那么,为什么我们需要将单件序列化,因为它们的序列形式不会被使用。我们把它抛出并用当前的实例替换反序列化的实例。
对我来说,实现可序列化单例的问题看起来只是一个理论问题。根据我所说的,反序列化单例并没有多大意义(在enum
的情况下也是如此)。
那我错过了什么?难道你不能更详细地解释一下吗?
答案 0 :(得分:0)
如果在某个其他可序列化类中有一些字段存储此单例实例并希望序列化/反序列化该类,则必须始终标记该字段transient
并确保在反序列化时手动填充它。如果您在Singleton中处理它,则不必执行此操作。我猜这就是可序列化单例的原因。
答案 1 :(得分:0)
枚举也是可序列化的,而不是它们的字段。
我认为保持简单易维护非常重要,例如如果要保存它,请使用文本文件。
我会添加一个方法,如保存和加载(可能带有文件名)
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