シーケンス化オブジェクトのsingleton
4277 ワード
オブジェクトが逆シーケンス化されると、新しいインスタンスが生成されます.これにより、元のsingletonオブジェクトは、Serializableインタフェースが実装されると、コードを見て正常に動作しません.
テストコード:
実行後の結果:
と書く
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)
2回目に読み込まれたsingletonオブジェクトが新しいことは明らかです.
では、この問題をどのように解決するかというと、Singletonクラスにコールバックメソッドを追加する必要があります.
これは、ObjectInputStreamのreadObject()メソッドを実行するときに、シーケンス化クラスにreadResolveメソッドがあるかどうかをチェックし、objectをReadResolveメソッドに置き換えて返されるオブジェクトがある場合はhackに相当します.
これにより、ReadResolveメソッドが追加され、逆シーケンス化されるたびにオブジェクトが一例であることが保証されます.
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)
2回目に読み込まれたsingletonオブジェクトが新しいことは明らかです.
では、この問題をどのように解決するかというと、Singletonクラスにコールバックメソッドを追加する必要があります.
private Object readResolve(){
return instance;
}
これは、ObjectInputStreamのreadObject()メソッドを実行するときに、シーケンス化クラスにreadResolveメソッドがあるかどうかをチェックし、objectをReadResolveメソッドに置き換えて返されるオブジェクトがある場合はhackに相当します.
/**
* 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メソッドが追加され、逆シーケンス化されるたびにオブジェクトが一例であることが保証されます.