問題の歴史
Javaの始まりから、アプリケーションセッション間でオブジェクトを保存する必要が生じました。ネットワークを介して送信したり、データベースやディスクに保存したりするためです。それを実現するために、オブジェクトをバイトストリームに変換し、逆に変換することを可能にするインターフェースSerializableが考案されました(シリアライズとデシリアライズ)。
問題
クラスがSerializableインターフェースを実装していない場合、そのインスタンスをシリアル化しようとすると例外がスローされます。さらに、シリアル化はフィールドの値だけでなく、その状態も保存するため、不正な実装が予期しない脆弱性やオブジェクトの復元エラー、さらにはデータの損失を引き起こす可能性があります。
解決策
Serializableインターフェースはマーカーインターフェースであり、メソッドを含まないため、正しいシリアライズを行うにはserialVersionUIDを明示的に指定し、シリアル化されるべきでないフィールドにはtransientキーワードを使用することが重要です。
コードの例:
import java.io.Serializable; public class User implements Serializable { private static final long serialVersionUID = 1L; private String username; private transient String password; // シリアル化されない public User(String username, String password) { this.username = username; this.password = password; } }
主な特徴:
transientを使用することで特定のフィールドのシリアル化を防止できるserialVersionUIDを明示的に宣言することは必須ですか?
いいえ、必須ではありませんが、宣言しないと、自動的に生成されます。ただし、クラスが変更された際に古いシリアル化バージョンを復元しようとするとInvalidClassExceptionが発生する可能性があるため、常にこのフィールドを明示的に宣言することが望ましいです。
静的フィールドはシリアル化されますか?
いいえ、シリアル化されるのは非静的(インスタンス)フィールドのみです。静的フィールドはクラスに属し、オブジェクトには属さないため、シリアル化やデシリアライズの際に値は保存・復元されません。
Serializableを実装していないフィールドを持つオブジェクトをシリアル化できますか?
いいえ、少なくとも1つの非静的フィールドがシリアライズ不可能でtransientとして宣言されていない場合、シリアル化を試みるとNotSerializableExceptionがスローされます。
serialVersionUIDの未宣言によるオブジェクトのバージョン不一致transientなしで機密データをシリアル化する(例えば、パスワード)ユーザーキャッシュ用アプリケーションではserialVersionUIDが指定されておらず、コードの拡張時にUserクラスの構造が変更されました。アプリケーション起動時にInvalidClassExceptionが発生し、すべてのシリアル化データが失われました。
利点:
欠点:
類似のプロジェクトでは常にserialVersionUIDを明示的に指定し、シリアル化されないすべてのフィールドをtransientにしました。このおかげで、クラスのわずかな構造の変更後もシリアル化されたオブジェクトが正常に読み込まれました。
利点:
欠点: