Javaオブジェクトのシーケンス化と逆シーケンス化
一、シーケンス化と逆シーケンス化の概念
オブジェクトをバイトシーケンスに変換するプロセスをオブジェクトのシーケンス化と呼ぶ.バイトシーケンスをオブジェクトに復元するプロセスをオブジェクトの逆シーケンス化と呼ぶ.オブジェクトのシーケンス化には主に2つの用途がある:1)オブジェクトのバイトシーケンスをハードディスクに永続的に保存し、通常は1つのファイルに保存する.2)ネットワーク上でオブジェクトのバイトシーケンスを転送する.
多くのアプリケーションでは、メモリ領域から離れ、長期保存のために物理ハードディスク(HDD)にチェックインするオブジェクトをシーケンス化する必要があります.例えば、最も一般的なのはWebサーバのSessionオブジェクトで、10万人のユーザーが同時にアクセスすると、10万人のSessionオブジェクトが現れる可能性があります.メモリが消費されない可能性があります.そこで、Webコンテナはいくつかのseesionをハードディスクにシーケンス化し、使用する必要がある場合は、ハードディスクに保存されているオブジェクトをメモリに復元します.
2つのプロセスがリモート通信を行う場合、互いに様々なタイプのデータを送信することができる.どのタイプのデータでも、ネットワーク上でバイナリシーケンスとして転送されます.送信側はこのJavaオブジェクトをバイトシーケンスに変換してこそ、ネットワーク上で伝送することができる.受信者はバイトシーケンスをJavaオブジェクトに復元する必要がある.
二、JDKクラスライブラリにおけるシーケンス化API
JAva.io.ObjectOutputStreamはオブジェクト出力ストリームを表し、そのwriteObject(Object obj)メソッドはパラメータで指定されたobjオブジェクトをシーケンス化し、得られたバイトシーケンスをターゲット出力ストリームに書き込むことができる.JAva.io.ObjectInputStreamはオブジェクト入力ストリームを表し、readObject()メソッドはソース入力ストリームからバイトシーケンスを読み出し、逆シーケンス化してオブジェクトに戻します.SerializableとExternalizableインタフェースを実装したクラスのオブジェクトのみがシーケンス化されます.ExternalizableインタフェースはSerializableインタフェースから継承され、Externalizableインタフェースを実現するクラスは完全に自身でシーケンス化の動作を制御し、Serializableインタフェースを実現するクラスだけはデフォルトのシーケンス化方式を採用することができる.オブジェクトシーケンス化には、1)ファイル出力ストリームなどの他のタイプのターゲット出力ストリームをパッケージできるオブジェクト出力ストリームを作成するステップと、2)オブジェクト出力ストリームのwriteObject()メソッドでオブジェクトを書き込みます.オブジェクトを逆シーケンス化するには、1)ファイル入力ストリームなどの他のタイプのソース入力ストリームをパッケージできるオブジェクト入力ストリームを作成します.2)オブジェクト入力ストリームのreadObject()メソッドでオブジェクトを読み込む.
オブジェクトのシーケンス化と逆シーケンスの例:
Personクラスを定義し、Serializableインタフェースを実現
import java.io.Serializable;
/**
* ClassName: Person
*
Description:
* @author xudp
* @version 1.0 V
* @createTime 2014-6-9 02:33:25
*/
public class Person implements Serializable {
/**
* ID
*/
private static final long serialVersionUID = -5809782578272943999L;
private int age;
private String name;
private String sex;
public int getAge() {
return age;
}
public String getName() {
return name;
}
public String getSex() {
return sex;
}
public void setAge(int age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public void setSex(String sex) {
this.sex = sex;
}
}
Personクラスオブジェクトのシーケンス化と逆シーケンス化
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.text.MessageFormat;
/**
* ClassName: TestObjSerializeAndDeserialize
*
Description:
* @author xudp
* @version 1.0 V
* @createTime 2014-6-9 03:17:25
*/
public class TestObjSerializeAndDeserialize {
public static void main(String[] args) throws Exception {
SerializePerson();// Person
Person p = DeserializePerson();// Perons
System.out.println(MessageFormat.format("name={0},age={1},sex={2}",
p.getName(), p.getAge(), p.getSex()));
}
/**
* MethodName: SerializePerson
* Description: Person
* @author xudp
* @throws FileNotFoundException
* @throws IOException
*/
private static void SerializePerson() throws FileNotFoundException,
IOException {
Person person = new Person();
person.setName("gacl");
person.setAge(25);
person.setSex(" ");
// ObjectOutputStream , Person E Person.txt , Person
ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(
new File("E:/Person.txt")));
oo.writeObject(person);
System.out.println("Person !");
oo.close();
}
/**
* MethodName: DeserializePerson
* Description: Perons
* @author xudp
* @return
* @throws Exception
* @throws IOException
*/
private static Person DeserializePerson() throws Exception, IOException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
new File("E:/Person.txt")));
Person person = (Person) ois.readObject();
System.out.println("Person !");
return person;
}
}
コードの実行結果は次のとおりです.
シーケンス化Personが成功するとEディスクでPerson.txtファイルが生成され、逆シーケンス化PersonはEディスクのPerson.txtを読み出してPersonオブジェクトが生成される
三、serialVersionUIDの役割
s e r i a l V e r s i o n U I D:字面の意味ではシーケンス化されたバージョン番号であり、Serializableインタフェースを実現するクラスにはシーケンス化されたバージョン識別子を表す静的変数がある
1 private static final long serialVersionUID
Serializableインタフェースを実装するクラスserialVersionUIDがクラスに追加されていない場合、次の警告メッセージが表示されます.
マウスクリックするとserialVersionUIDを生成するダイアログボックスが表示されます.次の図に示します.
serialVersionUIDには、次の2つの生成方法があります.
このようにして生成されるserialVersionUIDは、1 Lであり、例えば、
1 private static final long serialVersionUID = 1L;
このようにして生成されるserialVersionUIDは、クラス名、インタフェース名、メソッド、属性などに基づいて生成される.
1 private static final long serialVersionUID = 4603642343377807741L;
追加するとその警告メッセージは表示されません.次のようになります.
では、serialVersionUID(シリアル化バージョン番号)はいったい何の役に立つのでしょうか.serialVersionUIDの役割を以下の例で説明します.次のコードを見てみましょう.
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class TestSerialversionUID {
public static void main(String[] args) throws Exception {
SerializeCustomer();// Customer
Customer customer = DeserializeCustomer();// Customer
System.out.println(customer);
}
/**
* MethodName: SerializeCustomer
* Description: Customer
* @author xudp
* @throws FileNotFoundException
* @throws IOException
*/
private static void SerializeCustomer() throws FileNotFoundException,
IOException {
Customer customer = new Customer("gacl",25);
// ObjectOutputStream
ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(
new File("E:/Customer.txt")));
oo.writeObject(customer);
System.out.println("Customer !");
oo.close();
}
/**
* MethodName: DeserializeCustomer
* Description: Customer
* @author xudp
* @return
* @throws Exception
* @throws IOException
*/
private static Customer DeserializeCustomer() throws Exception, IOException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
new File("E:/Customer.txt")));
Customer customer = (Customer) ois.readObject();
System.out.println("Customer !");
return customer;
}
}
/**
* ClassName: Customer
*
Description: Customer Serializable ,
* @author xudp
* @version 1.0 V
* @createTime 2014-6-9 04:20:17
*/
class Customer implements Serializable {
//Customer serialVersionUID
private String name;
private int age;
public Customer(String name, int age) {
this.name = name;
this.age = age;
}
/*
* @MethodName toString
* @Description Object toString()
* @author xudp
* @return string
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "name=" + name + ", age=" + age;
}
}
実行結果:
シーケンス化と逆シーケンス化は成功しました.
次に、Customerクラスを変更し、次のようにsexプロパティを追加します.
class Customer implements Serializable {
//Customer serialVersionUID
private String name;
private int age;
// sex
private String sex;
public Customer(String name, int age) {
this.name = name;
this.age = age;
}
public Customer(String name, int age,String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
/*
* @MethodName toString
* @Description Object toString()
* @author xudp
* @return string
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "name=" + name + ", age=" + age;
}
}
その後、逆シーケンス操作を実行すると、次のような異常情報が放出されます.
1 Exception in thread "main" java.io.InvalidClassException: Customer;
2 local class incompatible:
3 stream classdesc serialVersionUID = -88175599799432325,
4 local class serialVersionUID = -5182532647273106745
つまり、ファイルストリームのclassとclasspathのclass、つまり修正されたclassは、互換性がなく、セキュリティメカニズムの考慮でプログラムがエラーを投げ出し、ロードを拒否しているということです.では、シーケンス化後にフィールドやメソッドを追加する必要がある場合は?どうすればいいですか.それは自分でserialVersionUIDを指定することです.TestSerialversionUIDの例では、CustomerクラスのserialVersionUIDを指定していない場合、javaコンパイラは自動的にこのclassに要約アルゴリズムを行い、指紋アルゴリズムに似ています.このファイルにスペースが1つ増えると、得られるUIDはまったく異なり、このような多くのクラスでは、この番号が唯一であることが保証されます.したがって、フィールドを追加すると、serialVersionUIDが明示的に指定されていないため、コンパイラはまたUIDを生成してくれました.もちろん、前にファイルに保存したものとは異なり、2つのシーケンス化バージョン番号が一致しないエラーが発生しました.したがって、serialVersionUIDを自分で指定すれば、シーケンス化後、後期のリストアに影響を与えることなく、フィールドやメソッドを追加することができ、リストア後のオブジェクトはそのまま使用でき、メソッドやプロパティも多く使用できます.
次に、Customerクラスの変更を続行し、CustomerにserialVersionUIDを指定します.変更後のコードは次のとおりです.
class Customer implements Serializable {
/**
* Customer serialVersionUID( )
*/
private static final long serialVersionUID = -5182532647273106745L;
private String name;
private int age;
// sex
//private String sex;
public Customer(String name, int age) {
this.name = name;
this.age = age;
}
/*public Customer(String name, int age,String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}*/
/*
* @MethodName toString
* @Description Object toString()
* @author xudp
* @return string
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "name=" + name + ", age=" + age;
}
}
シーケンス化操作を再実行し、Customerオブジェクトをローカルハードディスク(HDD)のCustomer.txtファイルストレージにシーケンス化し、Customerクラスを変更し、sexプロパティを追加します.変更後のCustomerクラスコードは次のとおりです.
class Customer implements Serializable {
/**
* Customer serialVersionUID( )
*/
private static final long serialVersionUID = -5182532647273106745L;
private String name;
private int age;
// sex
private String sex;
public Customer(String name, int age) {
this.name = name;
this.age = age;
}
public Customer(String name, int age,String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
/*
* @MethodName toString
* @Description Object toString()
* @author xudp
* @return string
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "name=" + name + ", age=" + age;
}
}
逆シーケンス操作を実行すると、次のように逆シーケンスに成功します.
四、serialVersionUIDの取値
serialVersionUIDの値は、Javaランタイム環境がクラスの内部詳細に基づいて自動的に生成します.クラスのソースコードを修正して再コンパイルすると、新しく生成されたクラスファイルのserialVersionUIDの値が変わる可能性があります.クラスのserialVersionUIDのデフォルト値はJavaコンパイラの実装に完全に依存し、同じクラスに対して異なるJavaコンパイラでコンパイルすると、異なるserialVersionUIDにつながる可能性があり、同じである可能性もあります.serialVersionUIDの独立性と決定性を向上させるために、シーケンス化可能なクラスに表示される定義serialVersionUIDに明確な値を与えることを強くお勧めします.
serialVersionUIDを明示的に定義するには、2つの用途があります.1、場合によっては、クラスの異なるバージョンがシーケンス化されて互換性があることが望ましいため、クラスの異なるバージョンが同じserialVersionUIDを持っていることを確認する必要があります.2、場合によっては、クラスの異なるバージョンがシーケンス化に互換性を持たないようにする必要があります.そのため、クラスの異なるバージョンが異なるserialVersionUIDを持っていることを確認する必要があります.