なぜ多くの種類の下位ソースコードはimplements Serializableを必要としますか?

6433 ワード

なぜ多くの種類の下位ソースコードはimplements Serializableを必要としますか?
異常クラスRuntimeExceptionに遭遇したとき、ThrowableがSerializableを実現していることに気づきました.また、私たちが平進しているjavabeanも一般的にSerializableを実現していることに気づきました.なぜですか.以下にまとめます.
public class Throwable implements Serializable {
    /** use serialVersionUID from JDK 1.0.2 for interoperability */
    private static final long serialVersionUID = -3042686055658047285L;
}
  • serializationでは、Serializableインタフェースを実装したオブジェクトをバイトシーケンスに変換できます.これらのバイトシーケンスは、後で元のオブジェクトを再生成するために完全に格納できます. 
  • serializationは、本機で行うだけでなく、ネットワークを介して操作することができます.この利点は、オペレーティングシステムの違い、バイト順などを自動的に遮断するため、大きなものです.例えば、Windowプラットフォームでオブジェクトを生成してシーケンス化し、ネットワークを介してUnixマシンに転送し、このUnixマシン上でこのオブジェクトを正しく再構築することができます. 

  • Object serializationは主に2つの主要な特性をサポートするために使用される:
  • JavaのRMI(remote method invocation).RMIは,ネイティブのようにリモートマシン上のオブジェクトを操作できる.メッセージをリモート・オブジェクトに送信する場合、serializaitonメカニズムを使用してパラメータを送信し、戻り値を受信する必要があります. 
  • JavaのJavaBeans.Beanのステータス情報は、通常、設計時に構成されます.Beanのステータス情報は、プログラムの実行時にこれらのステータス情報を復元できるように保存する必要があります.これもserializaitonメカニズムが必要です.つまり、ネットワーク環境でクラス転送を行う場合は、implements Serializableであるべきです.implements Serializableがないと、rmi(ejbを含む)からリモートコールを提供できません.

  • さらにC/S(socket)プログラムのように、clientはserverセグメントのデータを表示します.通常、serverでsocketを取得したoutputstream inputstreamを使用して、取得したデータを文字列に処理してクライアントで文字列を分割することができますが、これは明らかに効率が低下し、server側のデータをclass implements Serializableにパッケージすることができます.次にobjectoutputstream,objectinputstreamを直接渡します.
    Serizlizable作用
    Java仮想マシンに存在するオブジェクトの内部ステータスは、メモリにのみ保持されます.JVMが停止すると、これらの状態は失われます.多くの場合、オブジェクトの内部状態は永続化される必要があります.永続化といえば、ファイルシステムやデータベースに保存するのが最も直接的です.たとえば、オブジェクト関係マッピング(Object-relational mapping).オブジェクトシーケンス化メカニズム(object serialization)は、Java言語内で構築されたオブジェクト永続化方式であり、JVM内のアクティブオブジェクトとバイト配列(ストリーム)との間で容易に変換することができる.永続化を簡単に実現できることに加えて、追加のシーケンス化メカニズムのもう一つの重要な用途は、リモートメソッド呼び出しにおいて、開発者に最下位実装の詳細を遮断することである. 
    基本オブジェクトのシーケンス化
    シーケンス化されるJavaクラスはSerializableインタフェースを実装するだけでよい.実際のシーケンス化および逆シーケンス化は、ObjectOpuputStreamおよびObjectInputStreamによって行われる.Object OutputStreamのwriteObjectメソッドはJavaオブジェクトをストリームに書き込むことができ、Object InputStreamのreadObjectメソッドはストリームからJavaオブジェクトを読み込むことができます.書き込みおよび読み取りの際に使用されるパラメータまたは戻り値は単一のオブジェクトですが、実際には、そのオブジェクトが参照する他のオブジェクトと、これらのオブジェクトが参照する他のオブジェクトを含むオブジェクト図を操作します.Javaは、オブジェクト図を自動的に巡回し、シーケンス化します.オブジェクトに加えて、Javaの基本タイプと配列もObjectOutputStreamとObjectInputStreamでシーケンス化できます. 
    シーケンス化時のオブジェクトの置換
    現在のオブジェクトの代わりに、シーケンス化時に別のオブジェクトを使用する場合があります.その動機は、現在のオブジェクトにシーケンス化されたくないドメインが含まれている可能性があります.個の受注システムでは,受注に関する情報をシーケンス化した後,ネットワークを介して伝送する必要がある.オーダークラスOrderは、顧客クラスCustomerを参照します.デフォルトのシーケンス化の場合、Orderクラスオブジェクトがシーケンス化されると、参照しているCustomerクラスオブジェクトもシーケンス化され、ユーザー情報が漏洩する可能性があります.
    private static class OrderReplace implements Serializable{
        private static final long serialVersionUID=47832438;
        private String orderId;
        public OrderReplace(Order order){
            this.orderId = order.getId();
        }
    
        private Object readResolve(){
            //  orderId  Order      。
            return null;
        }
    
        public Object writeReplace(){
            return new OrderReplace(this);
        }
    }

    この置換対象クラスOrderReplaceはOrderのIDのみを保存している.OrderクラスのwriteReplaceメソッドでは、OrderReplaceオブジェクトが返されます.このオブジェクトはストリームに代替として書き込まれます.同様に、OrderReplaceクラスでreadResolveメソッドを定義して、読み込み時にOrderクラスオブジェクトに戻す必要があります.このように呼び出し者にとって,置換オブジェクトの存在は透明である.
     
    シーケンス化とオブジェクトの作成
    ObjectInputStreamのreadObjectメソッドによってオブジェクトが読み込まれた後、このオブジェクトは新しいインスタンスですが、その構築メソッドは呼び出されず、ドメインの初期化コードも実行されません.呼び出し元は、オブジェクトが一般的なnewオペレータによって作成されるのか、逆シーケンス化によって作成されるのか分からない.解決策はクラスのreadObjectメソッドで,必要なオブジェクト初期化ロジックを実行することである.一般的なJavaクラスでは,構築方法に初期化の論理が含まれている.これらの論理を1つのメソッドに抽出し、readObjectメソッドでこのメソッドを呼び出すことができます. 
    バージョンの更新
    Javaオブジェクトをシーケンス化すると、得られるバイト配列は通常、ディスクまたはデータベースに保存されます.保存が完了すると、追加のドメインが追加されるなど、元のJavaクラスが更新される可能性があります.この場合,互換性の観点から,古いバージョンのシーケンス化データを読み取ることが要求される.読み込み中に、ObjectInputStreamがオブジェクトの定義を見つけたときに、現在のJVMでJavaクラス定義を検索しようとします.この検索プロセスは、Javaクラスのフルネームのみに基づいて判断することはできません.現在のJVMには同じ名前が存在する可能性がありますが、意味が全く異なるJavaクラスが存在します.この対応関係は、グローバル唯一の識別子serialVersionUIDによって実現される.Serializableインタフェースを実装したクラスでドメインを定義することで、Javaクラスの唯一のシーケンス化バージョン番号が宣言されます.JVMは、バイト配列から取得したクラスのバージョン番号と、JVMで検索したクラスのバージョン番号とが一致するかどうかを比較して、2つのクラスが互換性があるかどうかを決定します.開発者にとって、Serializableインタフェースを実装したクラスでこのようなドメインを定義し、バージョン更新中に値を変更しないことを覚えておく必要があります.もちろん、このような後方互換性を維持したくない場合は、バージョン番号を変更すればいいです.このドメインの値は、一般にJavaクラスの各特性を統合して計算されるハッシュ値である.Javaが提供するserialverコマンドで生成できます.EclipseでJavaクラスがSerializableインタフェースを実装した場合、EclipseはこのserialVersionUIDを生成するためにプロンプトを表示します.
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    import java.io.Serializable;
    
    public class Book implements Serializable {
        // private static final long serialVersionUID = 5805574648076667828L;
        String name = "marsXTU";
    
        // int vale = 7;
    
        //        
        public void save() throws IOException {
            FileOutputStream f = new FileOutputStream("d://book.txt");
            ObjectOutputStream oos = new ObjectOutputStream(f);
            oos.writeObject(this);
            oos.close();
        }
    
        //          
        public void read() throws IOException, ClassNotFoundException {
            FileInputStream f = new FileInputStream("d://book.txt");
            ObjectInputStream ois = new ObjectInputStream(f);
            Book b = (Book) ois.readObject();
            ois.close();
        }
    
        public static void main(String[] args) {
            Book b = new Book();
            try {
                //    
                b.save();
                //   
                b.read();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
  • int vale=7を増加すると、b.save()をコメントし、再度プログラムを実行すると以下の異常が投げ出されます.java.io.InvalidClassException: cn.com.chenlly.Book; local class incompatible: stream classdesc serialVersionUID = -8127777334808352611, local class serialVersionUID = 6452469819260868051
  • javaのデフォルトでserialVersionUIDは、Javaクラスの各特性を統合して算出されるハッシュ値です.2回の属性値が同じであるため、serialVersionUIDも異なります.
  • private static final long serialVersionUID=580574648076667828 Lを加えると、このクラスには一意の識別子があり、2回のserialVersionUIDは変更されません.逆シーケンス化中でもエラーは報告されません.
  • 文字列をint name=34に変更すると;逆シリアル化操作を実行すると、この値の処理方法が分からず、javaというエラーメッセージが表示されます.io.InvalidClassException: cn.com.chenlly.Book; incompatible types for field name.簡単に言えば、ファイルに必要なすべてのデータが確実に保存されている場合、シリアル化されたUIDを処理しなければならないことを前提として、ファイルを読み取る可能性があります.