問題の歴史:
Javaは最初からSerializableインターフェースを通じてオブジェクトのシリアル化メカニズムをサポートしています。オブジェクトの状態を保存する必要がある場合がありますが、すべてのフィールドをシリアル化するべきではありません。例えば、安全性に関わる情報や動的に計算されるフィールドなどです。このような場合のために、transient修飾子が考案されました。
問題:
オブジェクト全体をシリアル化すると、プライベートなデータやシリアル化に対して不安定なデータが漏洩する可能性があります。さらに、重いフィールドや一時的なフィールド(例えばスレッドやデータベース接続)のシリアル化およびデシリアル化は、エラーやパフォーマンスの低下を引き起こす可能性があります。
解決策:
オブジェクトの保存時にシリアル化されるべきでないフィールドにはtransient修飾子を使用します。このようなフィールドはシリアル化メカニズムによって単に無視され、デシリアル化時にはデフォルト値(例えば、参照型ならnull、数値なら0)が与えられます。
コード例:
import java.io.*; class User implements Serializable { private String username; private transient String password; // シリアル化されません! public User(String username, String password) { this.username = username; this.password = password; } }
主な特徴:
静的フィールドはtransientにできますか?
回答: static transientとしてフィールドを宣言することは可能ですが、意味がありません:静的フィールドはすでにシリアル化されず、クラスに属し、オブジェクトには属しません。
transientフィールドのシリアル化を完全に制御できますか?
回答: はい、private void writeObject(ObjectOutputStream out)とprivate void readObject(ObjectInputStream in)メソッドを実装することで、必要に応じてtransientフィールドを手動でシリアル化することができます。
コード例:
private void writeObject(ObjectOutputStream out) throws IOException { out.defaultWriteObject(); // out.writeObject(password); // transientフィールドを明示的にシリアル化する必要がある場合 }
final transientフィールドはデシリアル化後どうなりますか?
回答: final transientフィールドもデフォルト値(通常は0またはnull)を受け取りますが、その後、特別な工夫がなければ変更することはできません。これがしばしばバグを引き起こします。
DatabaseConnectionというtransientフィールドを持つオブジェクトをシリアル化しました。デシリアル化後にその接続のメソッドを呼び出そうとしたところ、NullPointerExceptionが発生しました。
メリット:
デメリット:
transientパスワードを持つUserオブジェクトをシリアル化し、デシリアル化時にreadObjectメソッドでユーザーにパスワードを問い合わせるか、接続を再構築しました。
メリット:
デメリット: