java.io.Serializableの浅い分析

10804 ワード

java.io.Serializableの浅い分析
 
Java APIでjava.io.Serializableインタフェースのソース:
1 public interface Serializable { 2 }

クラスはjavaを実現する.io.Serializableインタフェースは、シーケンス化機能を有効にします.セカンダリインタフェースが実装されていないクラスでは、ステータスシーケンス化または逆シーケンス化はできません.シーケンス可能クラスのすべてのサブタイプ自体がシーケンス可能です.シーケンス化インタフェースにはメソッドまたはフィールドがなく、シーケンス化可能な意味を識別するためにのみ使用されます.
Javaの「オブジェクトシーケンス化」では、Serializableインタフェースを実現したオブジェクトをbyteストリームに変換できます.これにより、後でこのオブジェクトを使用する場合、これらのbyteデータを復元し、それに基づいてそのオブジェクトを再構築することができます.
オブジェクトをシーケンス化するには、まずOutputStreamを作成し、ObjectOutputStreamに埋め込む必要があります.このとき、writeObject()メソッドでオブジェクトをOutputStreamに書き込むことができます.
writeObject()メソッドは、対応するreadObject()メソッドが復元できるように、特定のクラスのオブジェクトの状態を書き込む役割を果たします.呼び出しでout.defaultWriteObjectは、Objectを保存するフィールドのデフォルトメカニズムを呼び出すことができます.この方法自体は、そのスーパークラスまたはサブクラスに属する状態に関与する必要はありません.ステータスは、writeObjectメソッドまたはDataOutputでサポートされている基本データ型メソッドを使用して、各フィールドをObjectOutputStreamに書き込むことで保存されます.
読むときは、InputStreamをObjectInputStreamに埋め込み、readObject()メソッドを呼び出さなければなりません.しかし、このように読んだのは、Objectのreferenceにすぎないので、使う前に、先に伝えなければなりません.readObject()メソッドは、ストリームからクラスフィールドを読み出して復元します.inを呼び出すことができます.defaultReadObjectは、デフォルトのメカニズムを呼び出して、オブジェクトの非静的および非過渡的フィールドを復元します.defaultReadObject()メソッドでは、ストリーム内の情報を使用して、ストリーム内の現在のオブジェクトの対応する名前付きフィールドで保存されているオブジェクトのフィールドを割り当てます.これは、クラスが開発された後に新しいフィールドを追加する必要がある場合に使用します.この方法自体は、そのスーパークラスまたはサブクラスに属する状態に関与する必要はありません.ステータスは、writeObjectメソッドまたはDataOutputでサポートされている基本データ型メソッドを使用して、各フィールドをObjectOutputStreamに書き込むことで保存されます.
シーケンス化では、次の点に注意してください.
1:オブジェクトがシーケンス化されると、privateとして宣言された変数を含むオブジェクトの非静的メンバー変数のみが保存され、メンバーメソッドと静的メンバー変数は保存されません.
2:オブジェクトのメンバー変数がオブジェクトである場合、そのオブジェクトのデータ・メンバーもシーケンス化されます.
3:シーケンス可能なオブジェクトにシーケンス不可能なオブジェクトへの参照が含まれている場合、シーケンス化操作全体が失敗し、NotSerializableExceptionが放出されます.この参照をtransientとマークすると、オブジェクトはシーケンス化されます.
 
1、シーケンス化って何?
簡単に言えば、メモリに保存されている様々なオブジェクトの状態を、保存されているオブジェクトの状態を読み返すためです.自分の様々な方法でObject Statesを保存することができますが、Javaはあなた自身よりもオブジェクトの状態を保存するメカニズムを提供します.それはシーケンス化です.
2、どのような場合にシーケンス化が必要か
a)メモリ内のオブジェクトを1つのファイルまたはデータベースに保存したい場合.b)ソケットでネットワーク上でオブジェクトを転送したい場合.c)RMIを介してオブジェクトを転送したい場合.
3、1つのオブジェクトをシーケンス化するとき、いったい何が起こったのか.
シーケンス化されない前に、ヒープ(Heap)に保存されている各オブジェクトには、インスタンス変数(instance ariable)のような対応する状態(state)があります.
1 Foo myFoo = new Foo(); 2 myFoo .setWidth(37); 3 myFoo.setHeight(70);

以下のコードでシーケンス化すると、MyFooオブジェクトのwidthおよびHeightインスタンス変数の値(37,70)がfooに保存される.serファイルでは、後でファイルから読み出し、スタックに元のオブジェクトを再作成できます.もちろん、オブジェクトのインスタンス変数の値を保存するだけでなく、JVMはクラスのタイプなど、小さな情報を保存して元のオブジェクトを復元します.
1 FileOutputStream fs = new FileOutputStream("foo.ser"); 2 ObjectOutputStream os = new ObjectOutputStream(fs); 3 os.writeObject(myFoo);

4、シーケンス化(ファイルに保存)を実現するには
  a)Make a FileOutputStream
JAvaコードFileOutputStreamfs=new FileOutputStream(「foo.ser」)
  b)Make a ObjectOutputStream
JAvaコードObjectOutputStream os=new ObjectOutputStream(fs);
  c)write the object
JAvaコードos.writeObject(myObject1);  os.writeObject(myObject2);  os.writeObject(myObject3);
  d) close the ObjectOutputStream
JAvaコードos.close();
5、例を挙げて説明する
 
 1 public class Box implements Serializable {  2     private static final long serialVersionUID = -3450064362986273896L;  3     
 4     private int width;  5     private int height;  6     
 7     public static void main(String[] args) {  8         Box myBox=new Box();  9         myBox.setWidth(50); 10         myBox.setHeight(30); 11         try { 12             FileOutputStream fs=new FileOutputStream("F:\\foo.ser"); 13             ObjectOutputStream os=new ObjectOutputStream(fs); 14  os.writeObject(myBox); 15  os.close(); 16             FileInputStream fi=new FileInputStream("F:\\foo.ser"); 17             ObjectInputStream oi=new ObjectInputStream(fi); 18             Box box=(Box)oi.readObject(); 19  oi.close(); 20             System.out.println(box.height+","+box.width); 21         } catch (Exception e) { 22  e.printStackTrace(); 23  } 24  } 25     
26     public int getWidth() { 27         return width; 28  } 29     public void setWidth(int width) { 30         this.width = width; 31  } 32     public int getHeight() { 33         return height; 34  } 35     public void setHeight(int height) { 36         this.height = height; 37  } 38 }

6、関連注意事項
a)親クラスがシーケンス化を実現すると、子クラスは自動的にシーケンス化を実現し、Serializableインタフェースを明示的に実現する必要はない.b)あるオブジェクトのインスタンス変数が他のオブジェクトを参照し、そのオブジェクトをシーケンス化するときも参照オブジェクトをシーケンス化する.c)すべてのオブジェクトがシーケンス化できるわけではありません.なぜできないのかについては、次のような理由があります.
  1.セキュリティ上の理由は、オブジェクトがprivate、publicなどのfieldを持っていること、ファイルへの書き込みやrmi転送など、転送するオブジェクトに対して、シーケンス化されて転送される過程で、このオブジェクトのprivateなどのドメインは保護されません.  2. socket,threadクラスのようなリソース割り当ての原因は,シーケンス化,転送,保存が可能であれば,それらを再リソース割り当てすることもできず,また,このように実現する必要もない.
 
  serialVersionUID
シーケンス化の実行時にserialVersionUIDと呼ばれるバージョン番号を使用すると、シーケンス化可能な各クラスに関連付けられます.このシーケンス番号は、逆シーケンス化中にシーケンス化されたオブジェクトの送信者と受信者がシーケンス化と互換性のあるクラスをロードしたかどうかを検証するために使用されます.受信者がロードしたオブジェクトのクラスのserialVersionUIDが対応する送信者のクラスのバージョン番号と異なる場合、逆シーケンス化はInvalidClassExceptionになる.直列可能クラスは、"serialVersionUID"という名前のフィールド(このフィールドは静的(static)、最終(final)のlong型フィールドである必要がある)を宣言することによって、独自のserialVersionUIDを明示的に宣言することができる.
 ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;

シーケンシング可能クラスがserialVersionUIDを明示的に宣言していない場合、「Java(TM)オブジェクトシーケンシング仕様」に記載されているように、シーケンシング実行時にクラスのさまざまな態様に基づいてクラスのデフォルトserialVersionUID値が計算されます.ただし、デフォルトのserialVersionUIDを計算することは、クラスの詳細情報に高い感度を有し、コンパイラによっては千差万別の可能性があり、逆シーケンス化中に予期せぬInvalidClassExceptionを引き起こす可能性があるため、すべてのシーケンス化可能なクラスがserialVersionUID値を明示的に宣言することを強くお勧めします.したがって、serialVersionUID値が異なるjavaコンパイラで実現される一貫性を保証するために、シーケンス化クラスは明確なserialVersionUID値を宣言する必要があります.また、private修飾子を使用して、直接宣言クラスであるserialVersionUIDフィールドが継承メンバーとして使用されないため、宣言serialVersionUID(可能な場合)を表示することを強くお勧めします.配列クラスは明確なserialVersionUIDを宣言できないため、常にデフォルトの計算値を有しますが、配列クラスはserialVersionUID値に一致する必要はありません.