[an error occurred while processing this directive]
[an error occurred while processing this directive]「メモリ上のオブジェクトをバイト列 (ストリーム) にする」
絵で描くと、こんな感じです。class Moo { int v; // デフォルトコンストラクタ (引数無しコンストラクタ) public Moo() {} public Moo(int v) { this.v = v; } public void print(){ System.out.println("v = " + Integer.toHexString(v)); } }インスタンスを作る、MooMain クラスを書きます。
public class MooMain { public static void main(String[] args){ Moo moo = new Moo(0x12345678); } }この moo というインスタンスを、
class Moo implements Serializable {
int v;
public Moo() {}
public Moo(int v) { this.v = v;}
public void print(){ System.out.println("v = " + Integer.toHexString(v)); }
}
次に、シリアライズして、ファイルに保存するコードを書きます。
今回は dumpToFile 関数に記述してみました。
public class MooMain { public static void dumpToFile(Moo moo, String name){ try { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(name)); oos.writeObject(moo); } catch(IOException ie){ ie.printStackTrace(); } } public static void main(String[] args){ Moo moo = new Moo(0x12345678); MooMain.dumpToFile(moo, "moo.txt"); } }これで、MooMain を実行すると、ファイルに保存されるようなりました。
public class MooMain { public static Moo dumpToFile(String name){...} public static Moo restoreFromFile(String name){ try { ObjectInputStream ois = new ObjectInputStream(new FileInputStream(name)); return (Moo)ois.readObject(); } catch(IOException ie){ ie.printStackTrace(); } catch(ClassNotFoundException cfe){ cfe.printStackTrace(); } return null; } public static void main(String[] args){ Moo moo = new Moo(0x12345678); MooMain.dumpToFile(moo, "moo.txt"); Moo moo2 = MooMain.restoreFromFile("moo.txt"); moo2.print(); } }実行すると、moo.txt に moo インスタンスがシリアライズされ、さらにそれが moo2 に読み込まれて復元されます。
$ javac MooMain.java $ java MooMain v = 12345678
class MyClass { int[] entries; public MyClass(int a, int b, int c){ entries = new int[2]; entries[0] = a; entries[1] = b; entries[2] = c; } }インスタンスを作ります。
MyClass m = new MyClass(10, 20, 30);これをシリアライズしたら、こんな風になりました。 図に書くと、こんなかんじです。
class MyClass { class MyList { int v; MyList next = null; public MyList(int v){ this.v = v; } void add(int v){ next = new MyList(v); } } MyList l; public MyClass(int a, int b, int c){ l = new MyList(a); l.add(b); l.add(c); } }やりたいことは前と一緒で、int 3つを保持することです。 しかし、この新しい MyClass を Java 標準のシリアライザでシリアライズすると、とても大きなデータになってしまいます。
class MyClass implements Serializable { transient int[] entries; public MyClass(int a, int b, int c){ entries = new int[3]; entries[0] = a; entries[1] = b; entries[2] = c; } private void writeObject(ObjectOutputStream out) throws IOException { out.defaultWriteObject(); out.writeInt(entries.length); for(int v: entries) out.writeInt(v); } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); int size = in.readInt(); entries = new int[size]; for(int i = 0; i < size; i++) entries[i] = in.readInt(); } }まず、シリアライズを標準のシリアライザに任せない、という宣言である、transient を int[] entries の前に書きます。これにより、そのままでは entries がシリアライズされなくなります。さらに、readObject() と writeObject() をオーバーライドして、データの書き込みと読み込みを実装しています。
class MyClass implement Serializable { transient int a; int b; }こうすると、a はシリアライズ対象に入りません。b は通常通りシリアライズされます。
class X implemnts Serializable { private static final long serialVersionUID = 12345; ... }これによって、もしも serialVersionUID が異なるクラスのデータを保存しようとした場合、エラーが発生します。
# hexdump -B moo.txt
バイト数 バイトコード (16進数で、左から右に) ascii で読んだもの
-------- ------------------------------------------------ ----------------
00000000 ac ed 00 05 73 72 00 03 4d 6f 6f 00 00 00 00 33 |・..sr..Moo....3|
00000010 33 33 33 02 00 01 49 00 01 76 78 70 12 34 56 78 |333...I..vxp.4Vx|
どうも、v の値 (0x12345678) がそのまま出ているほど単純でもないですね。
あと、Moo って文字列も見えます。以下の文書を参考に、この Java 標準のシリアライズフォーマットを読み解いてみます。
ここの、下の方に書いてある文法定義を当てはめれば、自然と読めます。ここでは、解析した結果を見ていきます。
ac ed 00 05 73 72 00 03 4d 6f 6f 00 00 00 00 33 |・..sr..Moo....3| 33 33 33 02 00 01 49 00 01 76 78 70 12 34 56 78 |333...I..vxp.4Vx|
ac ed 00 05 73 72 00 03 4d 6f 6f 00 00 00 00 33 |・..sr..Moo....3| 33 33 33 02 00 01 49 00 01 76 78 70 12 34 56 78 |333...I..vxp.4Vx|
ac ed 00 05 73 72 00 03 4d 6f 6f 00 00 00 00 33 |・..sr..Moo....3| 33 33 33 02 00 01 49 00 01 76 78 70 12 34 56 78 |333...I..vxp.4Vx|このオブジェクトは、このストリームで初めて出てきたクラスなので、クラス定義が必要です。 以降、クラス定義に移ります。(入れ子になっているので、あとでオブジェクト定義に戻ります)
ac ed 00 05 73 72 00 03 4d 6f 6f 00 00 00 00 33 |・..sr..Moo....3| 33 33 33 02 00 01 49 00 01 76 78 70 12 34 56 78 |333...I..vxp.4Vx|
ac ed 00 05 73 72 00 03 4d 6f 6f 00 00 00 00 33 |・..sr..Moo....3| 33 33 33 02 00 01 49 00 01 76 78 70 12 34 56 78 |333...I..vxp.4Vx|
ac ed 00 05 73 72 00 03 4d 6f 6f 00 00 00 00 33 |・..sr..Moo....3| 33 33 33 02 00 01 49 00 01 76 78 70 12 34 56 78 |333...I..vxp.4Vx|
ac ed 00 05 73 72 00 03 4d 6f 6f 00 00 00 00 33 |・..sr..Moo....3| 33 33 33 02 00 01 49 00 01 76 78 70 12 34 56 78 |333...I..vxp.4Vx|
ac ed 00 05 73 72 00 03 4d 6f 6f 00 00 00 00 33 |・..sr..Moo....3| 33 33 33 02 00 01 49 00 01 76 78 70 12 34 56 78 |333...I..vxp.4Vx|
ac ed 00 05 73 72 00 03 4d 6f 6f 00 00 00 00 33 |・..sr..Moo....3| 33 33 33 02 00 01 49 00 01 76 78 70 12 34 56 78 |333...I..vxp.4Vx|
ac ed 00 05 73 72 00 03 4d 6f 6f 00 00 00 00 33 |・..sr..Moo....3| 33 33 33 02 00 01 49 00 01 76 78 70 12 34 56 78 |333...I..vxp.4Vx|
ac ed 00 05 73 72 00 03 4d 6f 6f 00 00 00 00 33 |・..sr..Moo....3| 33 33 33 02 00 01 49 00 01 76 78 70 12 34 56 78 |333...I..vxp.4Vx|次に変数名 ('v' : 76) です。
ac ed 00 05 73 72 00 03 4d 6f 6f 00 00 00 00 33 |・..sr..Moo....3| 33 33 33 02 00 01 49 00 01 76 78 70 12 34 56 78 |333...I..vxp.4Vx|
ac ed 00 05 73 72 00 03 4d 6f 6f 00 00 00 00 33 |・..sr..Moo....3| 33 33 33 02 00 01 49 00 01 76 78 70 12 34 56 78 |333...I..vxp.4Vx|スーパークラスの情報 (superClassDesc) です。今回は特に明示的な継承はないので、TC_NULL (70) を入れます。
ac ed 00 05 73 72 00 03 4d 6f 6f 00 00 00 00 33 |・..sr..Moo....3| 33 33 33 02 00 01 49 00 01 76 78 70 12 34 56 78 |333...I..vxp.4Vx|これで、ようやくクラス定義が終わりです。オブジェクトの定義に戻ります。
ac ed 00 05 73 72 00 03 4d 6f 6f 00 00 00 00 33 |・..sr..Moo....3| 33 33 33 02 00 01 49 00 01 76 78 70 12 34 56 78 |333...I..vxp.4Vx|