web-dev-qa-db-ja.com

readObjectとwriteObjectがプライベートであり、一時変数を明示的に書き込むのはなぜですか?

Effective Javaのシリアライゼーションに関する章を読んでいます。

  1. 誰がreadObject()およびwriteObject()を呼び出しますか?これらのメソッドがプライベートと宣言されているのはなぜですか?

  2. 以下は本のコードの一部です

    // 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メソッドで明示的に書き込まれる特定の理由はありますか?それが一時的であると宣言されなかった場合、それはとにかく書かれたでしょうね?

37

(1)メソッドはどのクラスまたはインターフェースでも宣言されていません。 Serializableインターフェイスを実装し、 シリアル化および非シリアル化プロセス中に特別な特別な処理を必要とするクラスは、実装する必要があります =これらのメソッドとシリアライザ/デシリアライザは、これらのメソッドをreflectしようとします。

これは、Javaのかなり奇妙なコーナーの1つです。ここで、APIは実際にはjavaDocで定義されています...しかしifメソッドがインターフェイスで定義されていた場合、それらはhadpublicになります(インターフェイスメソッドをロックに実装することはできません) private修飾子を追加してください)。

Why private-javaDocはヒントを提供しません。多分、それらはプライベートとして指定されていますので、実装者以外の他のクラスはそれらを使用することを意図していません。それらは定義上プライベートです

(2)この例は、特別な処理が機能するhowを示しています。この例では、sizeは一時的であり、シリアル化されません。しかし、ここで特別なハンドラーを導入し、このハンドラーがsizeの値をストリームに追加します。非一時フィールドを使用した通常のアプローチとの違いは、結果のストリーム内の要素の順序である可能性があります(重要な場合)。

トランジェントフィールドがスーパークラスで定義されていて、サブクラスが値をシリアル化したい場合、この例は意味があります。

36
Andreas_D

を別のパーティーで使用しないでくださいとは別に、これらのメソッドのプライバシーのもう1つの理由は次のとおりです。

これらのメソッドがサブクラスによってオーバーライドされることを望まない。代わりに、各クラスは独自のwriteObjectメソッドを持つことができ、シリアル化エンジンはそれらすべてを順番に呼び出します。これはプライベートメソッドでのみ可能です(これらはオーバーライドされません)。 (readObjectについても同様です。)

(これは、それ自体がSerializableを実装するスーパークラスにのみ適用されることに注意してください。)

このようにして、サブクラスとスーパークラスは独立して進化し、古いバージョンの格納されたオブジェクトとの互換性を維持できます。

17
Paŭlo Ebermann

ReadObject()/ writeObject()がプライベートであることについては、次のとおりです。クラスBarがクラスFooを拡張する場合。 FooはreadObject()/ writeObject()も実装し、BarもreadObject()/ writeObject()を実装しています。

これで、Barオブジェクトがシリアル化または逆シリアル化されると、JVMはFooとBarの両方に対してreadObject()/ writeObject()を自動的に呼び出す必要があります(つまり、これらのスーパークラスメソッドを明示的にクラス化する必要はありません)。ただし、これらのメソッドがプライベート、それはメソッドのオーバーライドとなり、JVMはサブクラスオブジェクトのスーパークラスメソッドを呼び出すことができなくなります。

したがって、それらはプライベートである必要があります。

11
shrini1000

readObjectwriteObjectは、Object(Input/Output)Streamクラスによって呼び出されます。

これらのメソッドは(独自に実装する場合は)プライベートとして宣言されており、実装によって継承もオーバーライドもオーバーロードもされていないことを証明/示します。ここでのトリックは、JVMが対応するメソッド呼び出し中にいずれかのメソッドが宣言されているかどうかを自動的にチェックすることです。 JVMは必要なときにいつでもクラスのプライベートメソッドを呼び出すことができますただし、他のオブジェクトはできません。したがって、クラスの整合性が維持され、シリアライゼーションプロトコルは通常どおり動作し続けることができます。

そして、一時的なintについては、オブジェクト全体のシリアライゼーションのシリアライゼーションを制御するだけです。ただし、技術的には、すべてのフィールドが一時的である場合、defaultWriteObject()を呼び出すのは必要でもないことに注意してください。しかし、互換性を維持しながら、後でクラスに非一時的なメンバーを導入できるように、柔軟性の目的で呼び出すことをお勧めします。

7
Saket

ソケットを参照するクラス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メソッドの使用を示すことなので、ネットワーク関連の問題は無視してください。

0
Saket

一時変数に関して、一時変数を宣言し、後でwriteobjectメソッドでそれらをシリアル化する理由を理解するための最良の方法は、LinkedList/HashMap/etcクラスのreadobject/writeobjectメソッドを確認/分析/デバッグすることです。

これは通常、既定の動作/順序に依存せずに、事前定義された順序でクラス変数をシリアル化/逆シリアル化する場合に行われます。

0
Uday Ogra