復元したSerializableなオブジェクト

外部ファイルに出力したSerializableなオブジェクトをデシリアライズして得たオブジェクトについて少し試してみました。

package net.seratch.sample.serializable;
import java.io.Serializable;
public class Weblog implements Serializable {

	private static final long serialVersionUID = -1360885083528904586L;
	private String name;
	private String url;
	private Author author;
	
	public String getName() { return name; }
	public void setName(String name) { this.name = name; }
	public String getUrl() { return url; }
	public void setUrl(String url) { this.url = url; }
	public Author getAuthor() { return author; }
	public void setAuthor(Author author) { this.author = author; }
}

package net.seratch.sample.serializable;
import java.io.Serializable;
public class Author implements Serializable {

	private static final long serialVersionUID = 7606178097016478516L;
	private String name;

	public Author(String name) { this.name = name; }

	public String getName() { return name; }
	public void setName(String name) { this.name = name; }
}

package net.seratch.sample.serializable;
import java.lang.reflect.Field;
public class AbstractMain {
	public static final String outputFile  = "output/sample/serializable/obj.txt";
	public static void p(Object obj) { System.out.println(obj); }
	public static void dump(Object obj, String fieldName) {
		if ( fieldName == null ) fieldName = "";
		Class<?> clazz = obj.getClass();
		Field[] fields = clazz.getDeclaredFields();
		for (Field field : fields ) {
			field.setAccessible(true);
			try {
				String prefix = (fieldName.equals("")) ? "/" : "/" + fieldName + "/"; 
				p(prefix + field.getName() + " : " + field.get(obj));
				if ( field.get(obj).toString().startsWith(field.get(obj).getClass().getName()) ) {
					dump(field.get(obj), field.getName());
				}
			} catch (IllegalAccessException iae) {
				iae.printStackTrace();
			}
		}
	}
}

package net.seratch.sample.serializable;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class WriteMain extends AbstractMain {

	public static void main(String[] args) {

		// byte streamに吐き出すオブジェクト
		Weblog blog = new Weblog();
		blog.setName("半径5メートル");
		blog.setUrl("http://d.hatena.ne.jp/srkzhr/");
		blog.setAuthor(new Author("srkzhr"));

		// ファイルに書き込むオブジェクトのハッシュコード
		p(blog.toString());
		// フィールドの中身
		dump(blog, null);
		
		// ファイル出力
		FileOutputStream fos = null;
		ObjectOutputStream oos = null;
		try {
			fos = new FileOutputStream(outputFile);
			oos = new ObjectOutputStream(fos);
			oos.writeObject(blog);
			oos.close();
		} catch (IOException ioe) { 
			ioe.printStackTrace();
		} finally {
			try { fos.close(); }
			catch (IOException ioe) { ioe.printStackTrace(); }
		}

		// ファイルからオブジェクト読み込み
		FileInputStream fis = null;
		ObjectInputStream ois = null;
		Weblog readObject = null;
		try {
			fis = new FileInputStream(outputFile);
			ois = new ObjectInputStream(fis);
			readObject = (Weblog)ois.readObject();
			ois.close();
			// 取り出したオブジェクトのハッシュコード
			p(readObject.toString());
			// フィールドの中身
			dump(readObject, null);
		} catch (IOException ioe) {
			ioe.printStackTrace();
		} catch (ClassCastException cce) {
			cce.printStackTrace();
		} catch (ClassNotFoundException cnfe) {
			cnfe.printStackTrace();
		} finally {
			try { fis.close(); } 
			catch (IOException ioe) { ioe.printStackTrace(); }
		}
		
		// 同一オブジェクトかどうか
		if ( blog == readObject ) p("same");
	}
}

package net.seratch.sample.serializable;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class ReadMain extends AbstractMain {

	public static void main(String[] args) {

		// ファイルからオブジェクト読み込み
		FileInputStream fis = null;
		ObjectInputStream ois = null;
		Weblog readObject = null;
		try {
			fis = new FileInputStream(outputFile);
			ois = new ObjectInputStream(fis);
			readObject = (Weblog)ois.readObject();
			ois.close();
			// 取り出したオブジェクトのハッシュコード
			p(readObject.toString());
			// フィールドの中身
			dump(readObject, null);
		} catch (IOException ioe) {
			ioe.printStackTrace();
		} catch (ClassCastException cce) {
			cce.printStackTrace();
		} catch (ClassNotFoundException cnfe) {
			cnfe.printStackTrace();
		} finally {
			try { fis.close(); } 
			catch (IOException ioe) { ioe.printStackTrace(); }
		}
	}
}

まずはWriteMainの実行結果は以下の通り。ファイル出力後、すぐにファイルからオブジェクトを復元しています。

net.seratch.sample.serializable.Weblog@1b67f74
/serialVersionUID : -1360885083528904586
/name : 半径5メートル
/url : http://d.hatena.ne.jp/srkzhr/
/author : net.seratch.sample.serializable.Author@530daa
/author/serialVersionUID : 7606178097016478516
/author/name : srkzhr
net.seratch.sample.serializable.Weblog@e0e1c6
/serialVersionUID : -1360885083528904586
/name : 半径5メートル
/url : http://d.hatena.ne.jp/srkzhr/
/author : net.seratch.sample.serializable.Author@6ca1c
/author/serialVersionUID : 7606178097016478516
/author/name : srkzhr

復元したオブジェクトは、元のオブジェクトとは別の実体が確保されて内部のフィールドに同値が突っ込まれたもののようです。元のオブジェクトが存在していてもその参照が取れたりはしなくて、あくまで新しいオブジェクトとして取得されています。

次にReadMainを実行して別のJVM起動でオブジェクト復元のみ実行した場合の結果(ファイルは上記で出力したものを読み込む)。

net.seratch.sample.serializable.Weblog@60aeb0
/serialVersionUID : -1360885083528904586
/name : 半径5メートル
/url : http://d.hatena.ne.jp/srkzhr/
/author : net.seratch.sample.serializable.Author@16caf43
/author/serialVersionUID : 7606178097016478516
/author/name : srkzhr

VMの起動が別なので、同一かどうかは問題ではないのですが、ハッシュコード値は異なる値になるようです。

このハッシュコード値の組み合わせは毎回固定なのですが、この辺のメカニズムがまだいまいちわかっていない気がします。


追記(3/23)

以下のページの解説を読んでいて、だいぶ理解できたような気がするけど、デシリアイラズして復元したオブジェクトは、オブジェクト実体自体を復元しているから、ディープコピーという事ですね。

http://blog.livedoor.jp/lalha/archives/50095930.html

というか、過去のメモリ状態への参照を復元するなど、普通に考えればとてもできなさそうな事なわけでして。