由于对象反序列化对象,总会生出一个新的实例,这使得原本的singleton对象,一旦实现了Serializable接口,就不能正常工作了,看代码:
public class Singleton implements Serializable {
/**
*
*/
private static final long serialVersionUID = -5902363018159528162L;
private int count = 0;
private static Singleton instance = new Singleton();
static {
System.out.println("static");
instance = new Singleton();
}
private Singleton(){
}
public static Singleton getInstance(){
return instance;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
}
测试代码:
@Test
public void testSingletonWithSerializable() {
storeSingleton();
Singleton singleton = null;
singleton = restore();
singleton.setCount(10);
Singleton singleton1 = restore();
Assert.assertEquals(10, singleton1.getCount());
}
private Singleton restore() {
Singleton singleton=null;
try {
FileInputStream fis = new FileInputStream("d:\\singleton.txt");
ObjectInputStream ois = new ObjectInputStream(fis);
singleton = (Singleton)ois.readObject();
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
return singleton;
}
private void storeSingleton() {
Singleton singleton = Singleton.getInstance();
try {
FileOutputStream fos = new FileOutputStream("d:\\singleton.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(singleton);
oos.flush();
oos.close();
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
运行后结果:
写道
junit.framework.AssertionFailedError: expected:<10> but was:<0>
at junit.framework.Assert.fail(Assert.java:47)
at junit.framework.Assert.failNotEquals(Assert.java:283)
at junit.framework.Assert.assertEquals(Assert.java:64)
at junit.framework.Assert.assertEquals(Assert.java:195)
at junit.framework.Assert.assertEquals(Assert.java:201)
at ully.designpattern.singleton.SingletonTest.testSingletonWithSerializable(SingletonTest.java:32)
显然,第二次读取的singleton对象是新的。
如此,如何解决此问题呢,答案是需要在Singleton类中添加一个回调方法:
private Object readResolve(){
return instance;
}
这其实就相当于做了一个hack,代码在执行ObjectInputStream的readObject()方法时,会先去检查该序列化类是否有readResolve方法,如果有会将object替换成ReadResolve方法返回的对象。
/**
* Invokes the readResolve method of the represented serializable class and
* returns the result. Throws UnsupportedOperationException if this class
* descriptor is not associated with a class, or if the class is
* non-serializable or does not define readResolve.
*/
Object invokeReadResolve(Object obj)
throws IOException, UnsupportedOperationException
{
if (readResolveMethod != null) {
try {
return readResolveMethod.invoke(obj, (Object[]) null);
} catch (InvocationTargetException ex) {
Throwable th = ex.getTargetException();
if (th instanceof ObjectStreamException) {
throw (ObjectStreamException) th;
} else {
throwMiscException(th);
throw new InternalError(); // never reached
}
} catch (IllegalAccessException ex) {
// should not occur, as access checks have been suppressed
throw new InternalError();
}
} else {
throw new UnsupportedOperationException();
}
}
如此,添加了ReadResolve方法,即可保证每次反序列化的对象均是单例的。
分享到:
相关推荐
在实际开发中,我们还需要考虑Singleton的序列化问题,因为默认的序列化机制会破坏单例。可以通过实现Serializable接口并重写readResolve()方法来解决这个问题。 此外,Singleton模式虽然简单易用,但也有一些缺点...
这是Joshua Bloch在《Effective Java》中推荐的单例实现方式,既线程安全,又避免了序列化导致的多实例问题。 ```java public enum Singleton { INSTANCE; } ``` 单例模式虽然简单,但在某些情况下可能会带来...
这在某些情况下可能引发问题,比如测试和序列化。为了解决这些问题,可以考虑使用依赖注入或者使用Prototype模式作为替代。 总的来说,Singleton模式是一种常见的设计模式,它在适当的情况下可以简化系统设计,提高...
5. **枚举方式**:这是最安全且推荐的实现方式,天然线程安全,避免了序列化和反射攻击。 ```java public enum Singleton { INSTANCE; public void someMethod() { // ... } } ``` **单例模式的应用场景...
2. **序列化**:如果Singleton实现了Serializable接口,需处理反序列化时的实例化问题,否则可能会创建多个实例。 3. **测试**:测试Singleton类时,需考虑静态初始化可能导致的问题,可能需要特殊的测试策略。 4. *...
默认情况下,单例对象是可序列化的。然而,序列化和反序列化可能会破坏单例模式,因为每次反序列化都会创建一个新的实例。为了防止这种情况,我们需要在Singleton类中实现`readResolve()`方法: ```java import ...
使用枚举来实现单例模式是一种既简单又安全的方式,同时防止反射和序列化攻击。 ```java public enum Singleton { INSTANCE; public void whateverMethod() { // ... } } ``` 以上六种单例模式各有优缺点...
枚举类型是天然的单例,既保证了线程安全,又防止了反射和序列化攻击。 ```java public enum Singleton { INSTANCE; public void someMethod() { //... } } ``` 以上就是单例设计模式在Java中的多种实现方式...
另外,枚举类型的单例实现是Java中最安全且推荐的方式,因为它自动处理线程安全问题,同时也避免了反射和序列化攻击: ```java public enum Singleton { INSTANCE; } ``` 在这种实现中,`Singleton`是一个枚举...
此外,通过使用枚举方式实现单例,可以防止反射和序列化带来的多实例问题。 总结来说,单例模式是一种重要的设计模式,用于控制类实例的数量,以优化资源管理和提高效率。在实际开发中,我们需要根据具体需求选择...
4. **枚举 Singleton**:这是 Bill Pugh 提出的一种创建 Singleton 实例的方法,既能防止反序列化重新创建新的实例,又避免了同步问题。代码如下: ```java public enum Singleton { INSTANCE; public void ...
这里,`$person`就是一个`Person`类的对象,`"张三"`和`25`作为参数传递给了构造函数,用于初始化对象的属性。 三、克隆对象:clone 关键字 有时候,我们可能需要复制一个已存在的对象而不希望改变原对象。这时...
5. **枚举**:JDK5之后引入的一种更简单的单例实现方式,天然支持序列化机制,能避免反射和反序列化的攻击。 ```java public enum Singleton { INSTANCE; public void showMessage() { System.out.println(...
2. **序列化**:如果单例类实现了`Serializable`接口,需要处理反序列化时可能创建新实例的问题,通常在`readResolve()`方法中返回现有实例。 3. **懒汉式与饿汉式**:懒汉式(延迟初始化)在类加载时不创建实例,...
- 需要考虑反序列化时是否仍保持单例特性,否则可能创建多个实例。 总结来说,单例模式是一种常用的创建型设计模式,旨在保证类的实例只有一个,同时提供全局访问点。不同的实现方式有不同的性能和线程安全性考虑,...
在实际使用中,除了考虑线程安全,还需要注意内存管理、序列化、测试等方面的问题。在C++中,单例模式常常与智能指针(如`std::unique_ptr`或`std::shared_ptr`)结合使用,以更好地管理对象的生命周期。
7. 枚举:最简单且推荐的实现方式,天然线程安全,避免反射和序列化攻击。 ```java public enum Singleton { INSTANCE; public void whateverMethod() { // ... } } ``` 以上就是Java中实现单例模式的常见方法...
- `__wakeup`方法用于防止对象在反序列化时重新生成实例。 - 如果在多线程环境下,需要考虑线程安全问题,可能需要添加锁来保证同步。 通过Composer安装`darkfriend/php7-singleton`库,可以利用这个库提供的预封装...
在实际开发中,我们还需要考虑反序列化和反射攻击破坏单例的情况,通常会在构造函数上添加`private`修饰符并使用`readResolve()`方法或`equals()`和`hashCode()`方法来防止这种情况发生。 总结起来,Java单例模式的...