[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|