sub*_*his 26 java singleton serialization
在一次采访中,面试官问我以下问题:是否有可能序列化单个对象?我说是的,但在哪种情况下我们应该序列化一个单身人士?
是否可以设计一个对象无法序列化的类?
Mic*_*rdt 23
在哪种情况下我们应该序列化单例.
想象一下,你有一个长期运行的应用程序,并希望能够关闭它,然后继续关闭它(例如,为了进行硬件维护).如果应用程序使用有状态的单例,则必须能够保存和恢复sigleton的状态,这最容易通过序列化来完成.
是否可以设计一个无法序列化对象的类.
确实非常简单:只是不要实现Serializable和制作类final
Dan*_*ien 23
这个问题应该更好地表达为"是否有可能以不破坏单例模式的方式使用单例模式类C进行序列化和反序列化?"
答案基本上是肯定的:
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectStreamException;
import java.io.Serializable;
public class AppState implements Serializable
{
private static AppState s_instance = null;
public static synchronized AppState getInstance() {
if (s_instance == null) {
s_instance = new AppState();
}
return s_instance;
}
private AppState() {
// initialize
}
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
ois.defaultReadObject();
synchronized (AppState.class) {
if (s_instance == null) {
// re-initialize if needed
s_instance = this; // only if everything succeeds
}
}
}
// this function must not be called other than by the deserialization runtime
private Object readResolve() throws ObjectStreamException {
assert(s_instance != null);
return s_instance;
}
public static void main(String[] args) throws Throwable {
assert(getInstance() == getInstance());
java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
java.io.ObjectOutputStream oos = new java.io.ObjectOutputStream(baos);
oos.writeObject(getInstance());
oos.close();
java.io.InputStream is = new java.io.ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(is);
AppState s = (AppState)ois.readObject();
assert(s == getInstance());
}
}
Run Code Online (Sandbox Code Playgroud)
但要注意,它是可能的多个实例AppState使用此代码存在.但是,只引用了一个.其他符合垃圾收集条件,仅由反序列化运行时创建,因此它们不存在用于实际目的.
对于其他两个问题的答案(在哪种场景中我们应该序列化一个单例?是否可以设计一个其对象无法序列化的类?),请参阅@Michael Borgwardt的答案.
是否可以序列化单例对象?
这取决于单身人士的实施方式.如果您的单例实现为具有一个元素的枚举类型,则默认情况下:
// Enum singleton - the preferred approach
public enum Elvis {
INSTANCE;
public void leaveTheBuilding() { ... }
}
Run Code Online (Sandbox Code Playgroud)
如果您的单例不是使用单元素枚举类型实现的,而是使用静态工厂方法(变体是使用公共静态最终字段):
// Singleton with static factory
public class Elvis {
private static final Elvis INSTANCE = new Elvis();
private Elvis() { ... }
public static Elvis getInstance() { return INSTANCE; }
public void leaveTheBuilding() { ... }
}
Run Code Online (Sandbox Code Playgroud)
然后添加implements Serializable使其可序列化是不够的,您必须声明所有实例字段瞬态(以防止序列化攻击)并提供readResolve方法.
要保持单例保证,必须将所有实例字段声明为瞬态并提供
readResolve方法(第77项).否则,每次反序列化序列化实例时,都会创建一个新实例,在我们的示例中,将导致虚假的猫王目击.要防止这种情况,请将此readResolve方法添加到Elvis类:Run Code Online (Sandbox Code Playgroud)// readResolve method to preserve singleton property private Object readResolve() { // Return the one true Elvis and let the garbage collector // take care of the Elvis impersonator. return INSTANCE; }
这在Effective Java中有很多讨论(它也显示了序列化攻击):
我们应该在哪种情况下序列化单例
例如,用于临时,短期存储或通过网络传输对象(例如,使用RMI).
是否可以设计一个无法序列化对象的类.
正如其他人所说,不要实施Serializable.即使对象或其超工具之一Serializable,你仍然可以阻止它抛出一个正在连载NotSerializableException 的writeObject().
我说是
不是默认的.在实现之后,java.io.Serializable您需要覆盖readObject()和方法,因为您无法序列化静态字段.单例将其实例保存在静态字段中.writeObject()readResolve()
但在哪种情况下我们应该序列化一个单身人士.
实际上没有想到有用的现实世界场景.单例通常在其整个生命周期内不会改变状态,也不包含您想要保存/恢复的任何状态.如果确实如此,那么将其作为单身人士已经是错误的.
Java SE API中两个单例模式的真实示例是java.lang.Runtime#getRuntime()和java.awt.Desktop#getDesktop().它们都没有实现可序列化.它也没有任何意义,因为它们在每次调用时都返回正确/期望/预期的实例.如果序列化和反序列化,最终可能会出现多个实例.如果同时从环境切换,则实例可能根本不起作用.
是否可以设计一个无法序列化对象的类.
是.只是不要让类实现java.io.Serializable接口.
| 归档时间: |
|
| 查看次数: |
33281 次 |
| 最近记录: |