すべてのクラスの最上位のスーパークラスとなる Object クラスは Serializable インターフェイスを実装してはいません。 ある Serializable 実装クラス Foo において Object 型から順にサブクラスを探していき最初に implements 節で Serializable インターフェイスの実装を宣言したクラスを S とします。 ここで S のスーパークラスを U とすると U は Serializable を実装していないことになります。 この U が Foo からアクセス可能な引数をとらないコンストラクタを定義していない場合には直列化の際に例外が発生します。
Object -> U -> S -> Foo上の継承関係で U, S, Foo クラスのクラス宣言は
public class U public class S extends U implements java.io.Serializable public class Foo extends Sとなっているとします。 ここで Foo のインスタンスを直列化する際に実際に直列化が行われるフィールドは S と Foo のフィールドだけです。 つまり最初に Serializable インターフェイスを実装したクラスとそのクラス以下のサブクラスのフィールドは直列化の対象になりますが最初に Serializable インターフェイスを実装したクラスのスーパークラス以上のクラスのフィールドは直列化されません。 当然復元時には直列化時の値は失われています。
上の例の場合は復元はまず U の引数のないコンストラクタが実行されます。 それから S, Foo の順で直列化されているフィールドの復元が行われます。 復元時に S, Foo のコンストラクタが実行されることはありません。 U より上位のクラスのフィールドは直列化時の値は失われ、 U のデフォルトコンストラクタ実行後の値が復元時の値となります。
ここで U のデフォルトコンストラクタが直列化の対象である Foo からアクセス可能でない場合には直列化の際に例外が発生します。 例えば U のデフォルトコンストラクタがアクセス修飾子を設定しない同じパッケージ内のみアクセス可能という場合に Foo が U とは別のパッケージの場合には直列化は失敗します。 U のデフォルトコンストラクタが private の場合にも同様に直列化の際に例外が発生します。 これらのエラーはコンパイル時でも復元時でもなく直列化を行おうとした時に発生します。
クラス宣言時に implements キーワードで Serializable インターフェイスを実装したクラスのスーパークラスが Object クラスの場合には Object クラスには public なデフォルトコンストラクタが定義されているのでまったく問題ありません。
下のプログラムは Serializable の実装を宣言した SubBook クラスのスーパークラスの
Book がデフォルトコンストラクタを用意していない為に SubBook
の直列化の際に例外が発生する例です。
BookSerializeSample の実行結果は以下です。
Exception in thread "main" java.io.InvalidClassException: SubBook; no valid constructor
at java.io.ObjectStreamClass.<init>(Unknown Source)
at java.io.ObjectStreamClass.lookup(Unknown Source)
at java.io.ObjectOutputStream.writeObject0(Unknown Source)
at java.io.ObjectOutputStream.writeObject(Unknown Source)
at BookSerializeSample.main(BookSerializeSample.java:12)
/**************************** Book.java ****************************/
public class Book {
protected String title;
protected String author;
//public Book() {}
public Book(String title, String author) {
this.title = title;
this.author = author;
}
//--------------------------------------------------------------------
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
//--------------------------------------------------------------------
public String toString() {
return "Book title:" + title + " author:" + author;
}
}
/**************************** SubBook.java ****************************/
public class SubBook extends Book implements java.io.Serializable {
protected int price;
public SubBook(String title, String author, int price) {
super(title, author);
this.price = price;
}
//--------------------------------------------------------------------
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
//--------------------------------------------------------------------
public String toString() {
return "SubBook title:" + title + " author:" + author +
" price:" + price;
}
}
/********************* BookSerializeSample.java *********************/
import java.io.*;
public class BookSerializeSample {
public static void main(String[] args) throws Exception {
SubBook book = new SubBook("きまぐれロボット", "星新一", 357);
ObjectOutputStream out = null;
ObjectInputStream in = null;
try {
out = new ObjectOutputStream(new BufferedOutputStream(
new FileOutputStream("data")));
out.writeObject(book);
out.close();
out = null;
in = new ObjectInputStream(new BufferedInputStream(
new FileInputStream("data")));
System.out.println(in.readObject());
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {}
}
if (in != null) {
try {
in.close();
} catch (IOException e) {}
}
}
}
}