Technology & Programming Article
よくある誤解、あやふやな知識をQ&A形式で総整理!
Javaの“常識”、“非常識”
第3回 プログラミング編
日ごろ、「何となくあやふやなまま」にプログラミングを行っていることはないでしょうか。今回は、そうした事柄をいくつかピックアップし、可能な限り明確にしていきます。
2005年12月21日更新
Q.01
クラスをシリアライズ可能にするかどうかは、どのような基準で決めればよいのでしょうか?
A.01
ファイルへの保存の対象とするオブジェクトや、ネットワーク転送の対象とするようなオブジェクトをシリアライズ可能にします
Javaのオブジェクトを、後で復元できるような1次元のデータ列に変換することを「シリアライズ(Serialize)」、あるいは「シリアライゼーション(Serialization)」、「直列化」と呼びます。逆に、シリアライズしたデータ列をオブジェクトに変換(復元)することを、「デシリアライズ(Deserialize)」、「デシリアライゼーション(Deserialization)」と呼びます。
インスタンスの状態をファイルに保存したり、ネットワークを介してインスタンスの転送を行ったりする場合は、このシリアライズを利用します。そのとき、対象となるオブジェクトには、インタフェースSerializableをimplementsすることとなります。
インタフェースSerializableは、メソッドやフィールドを1つも持たない空のインタフェース(マーカ・インタフェース)です。このインタフェースは、それをimplementsするオブジェクトがシリアライズ可能であることを示すためだけに機能します。
インタフェースSerializableの使い方は、クラスの宣言部分に同インタフェースをimplementsすると記述するだけなので、安易に使用してしまいがちです。しかし、実際には、以下に示す点を考慮したうえでシリアライズ可能にするか否かを決める必要があります※1。
※1 以下の記述については、『Effective Java プログラミング言語ガイド』(著者:ジョシュア・ブロック氏/発行:ピアソン・エデュケーション)を参考にした。
●いったんそのクラスをリリースすると、そのクラスの実装を後から変更しづらくなる:シリアライズを行う環境と、デシリアライズを行う環境とで、そのクラスのバージョンが異なると、インスタンスを復元できなくなるため※2
●バグやセキュリティ・ホールの可能性が増える:通常、オブジェクト・インスタンスはコンストラクタを使用して生成するが、デシリアライズによるオブジェクトの生成は、メソッドreadObjectで行う(脚注2を参照)。そのため、finalフィールドに対する書き換えなど、不正なアクセスがなされる危険性がある
●新しいバージョンのクラスのリリースに際して、テストの負荷が大きくなる:シリアライズ可能なクラスが修正された場合には、異なるバージョン間でシリアライズ⇔デシリアライズが行えるか否かを検証する必要がある
ちなみに、Java EE(J2EE)アプリケーションの開発では、DTO(Data Transfer Object)など、直列化の対象となるクラスを大量に作成しなければならないことがあります。そのすべてにインタフェースSerializableをimplementsするのは面倒なため、「デフォルトで直列化可能になっていればよいのに」と感じる人もいるでしょう。しかし、上述したこと以外の理由からも、安易に直列化できないことが多々あるため、安全な動作を保証すべく、インタフェースSerializableを用いたオプション対応としているのでしょう。
※2 ただし、以下の方法により、異なるバージョンでも復元できる場合がある。
●メソッドwriteObject/readObjectを実装し、バージョン間で整合性を保証したカスタム・シリアライズを実現する。両メソッドはprivateメソッドであり、またインタフェースSerializableはメソッドが用意されていないマーカ・インタフェースだが、同インタフェースをimplementsしたときにだけ、両メソッドを特別に定義できる仕組みになっている
●シリアル・バージョンUIDをカスタム定義する。このシリアル・バージョンUIDは、「private static final long serialVersionUID」という名前で宣言する必要がある。また、ほかのクラスのシリアル・バージョンUIDと重複しないようにしなければならない









