Effective Javaのシリアライゼーションに関する章を読んでいます。
誰がreadObject()およびwriteObject()を呼び出しますか?これらのメソッドがプライベートと宣言されているのはなぜですか?
以下は本のコードの一部です
// StringList with a reasonable custom serialized form
public final class StringList implements Serializable {
private transient int size = 0;
private transient Entry head = null;
//Other code
private void writeObject(ObjectOutputStream s)
throws IOException {
s.defaultWriteObject();
s.writeInt(size);
// Write out all elements in the proper order.
for (Entry e = head; e != null; e = e.next)
s.writeObject(e.data);
}
}
}
変数size
が一時的なものとして宣言され、writeObjectメソッドで明示的に書き込まれる特定の理由はありますか?それが一時的であると宣言されなかった場合、それはとにかく書かれたでしょうね?
(1)メソッドはどのクラスまたはインターフェースでも宣言されていません。 Serializable
インターフェイスを実装し、 シリアル化および非シリアル化プロセス中に特別な特別な処理を必要とするクラスは、実装する必要があります =これらのメソッドとシリアライザ/デシリアライザは、これらのメソッドをreflectしようとします。
これは、Javaのかなり奇妙なコーナーの1つです。ここで、APIは実際にはjavaDocで定義されています...しかしifメソッドがインターフェイスで定義されていた場合、それらはhadにpublic
になります(インターフェイスメソッドをロックに実装することはできません) private
修飾子を追加してください)。
Why private-javaDocはヒントを提供しません。多分、それらはプライベートとして指定されていますので、実装者以外の他のクラスはそれらを使用することを意図していません。それらは定義上プライベートです。
(2)この例は、特別な処理が機能するhowを示しています。この例では、size
は一時的であり、シリアル化されません。しかし、ここで特別なハンドラーを導入し、このハンドラーがsize
の値をストリームに追加します。非一時フィールドを使用した通常のアプローチとの違いは、結果のストリーム内の要素の順序である可能性があります(重要な場合)。
トランジェントフィールドがスーパークラスで定義されていて、サブクラスが値をシリアル化したい場合、この例は意味があります。
を別のパーティーで使用しないでくださいとは別に、これらのメソッドのプライバシーのもう1つの理由は次のとおりです。
これらのメソッドがサブクラスによってオーバーライドされることを望まない。代わりに、各クラスは独自のwriteObject
メソッドを持つことができ、シリアル化エンジンはそれらすべてを順番に呼び出します。これはプライベートメソッドでのみ可能です(これらはオーバーライドされません)。 (readObject
についても同様です。)
(これは、それ自体がSerializableを実装するスーパークラスにのみ適用されることに注意してください。)
このようにして、サブクラスとスーパークラスは独立して進化し、古いバージョンの格納されたオブジェクトとの互換性を維持できます。
ReadObject()/ writeObject()がプライベートであることについては、次のとおりです。クラスBarがクラスFooを拡張する場合。 FooはreadObject()/ writeObject()も実装し、BarもreadObject()/ writeObject()を実装しています。
これで、Barオブジェクトがシリアル化または逆シリアル化されると、JVMはFooとBarの両方に対してreadObject()/ writeObject()を自動的に呼び出す必要があります(つまり、これらのスーパークラスメソッドを明示的にクラス化する必要はありません)。ただし、これらのメソッドがプライベート、それはメソッドのオーバーライドとなり、JVMはサブクラスオブジェクトのスーパークラスメソッドを呼び出すことができなくなります。
したがって、それらはプライベートである必要があります。
readObject
とwriteObject
は、Object(Input/Output)Stream
クラスによって呼び出されます。
これらのメソッドは(独自に実装する場合は)プライベートとして宣言されており、実装によって継承もオーバーライドもオーバーロードもされていないことを証明/示します。ここでのトリックは、JVMが対応するメソッド呼び出し中にいずれかのメソッドが宣言されているかどうかを自動的にチェックすることです。 JVMは必要なときにいつでもクラスのプライベートメソッドを呼び出すことができますただし、他のオブジェクトはできません。したがって、クラスの整合性が維持され、シリアライゼーションプロトコルは通常どおり動作し続けることができます。
そして、一時的なint
については、オブジェクト全体のシリアライゼーションのシリアライゼーションを制御するだけです。ただし、技術的には、すべてのフィールドが一時的である場合、defaultWriteObject()
を呼び出すのは必要でもないことに注意してください。しかし、互換性を維持しながら、後でクラスに非一時的なメンバーを導入できるように、柔軟性の目的で呼び出すことをお勧めします。
ソケットを参照するクラスAがあるとします。 SocketがSerializableでないため、クラスAのオブジェクトをシリアル化する必要がある場合、直接行うことはできません。この場合、次のようにコードを記述します。
public class A implements implements Serializable {
// mark Socket as transient so that A can be serialized
private transient Socket socket;
private void writeObject(ObjectOutputStream out)throws IOException {
out.defaultWriteObject();
// take out ip address and port write them to out stream
InetAddress inetAddress = socket.getInetAddress();
int port = socket.getPort();
out.writeObject(inetAddress);
out.writeObject(port);
}
private void readObject(ObjectInputStream in)
throws IOException, ClassNotFoundException{
in.defaultReadObject();
// read the ip address and port form the stream and create a frest socket.
InetAddress inetAddress = (InetAddress) in.readObject();
int port = in.readInt();
socket = new Socket(inetAddress, port);
}
}
目的はwriteObject/readObjectメソッドの使用を示すことなので、ネットワーク関連の問題は無視してください。
一時変数に関して、一時変数を宣言し、後でwriteobjectメソッドでそれらをシリアル化する理由を理解するための最良の方法は、LinkedList/HashMap/etcクラスのreadobject/writeobjectメソッドを確認/分析/デバッグすることです。
これは通常、既定の動作/順序に依存せずに、事前定義された順序でクラス変数をシリアル化/逆シリアル化する場合に行われます。