ホームページ  >  記事  >  Java  >  Java オブジェクトのシリアル化と逆シリアル化

Java オブジェクトのシリアル化と逆シリアル化

青灯夜游
青灯夜游転載
2019-11-27 17:59:222528ブラウズ

Java オブジェクトのシリアル化と逆シリアル化

オブジェクトのシリアル化と逆シリアル化

1) オブジェクトのシリアル化では、Object オブジェクトをバイト シーケンスに変換します。それ以外の場合は、オブジェクトをバイト シーケンスに変換します。これはオブジェクトの逆シリアル化と呼ばれます。

2) シリアル化ストリーム (ObjectOutputStream) はフィルターされたバイトのストリームです - writeObject() メソッド

逆シリアル化ストリーム (ObjectInputStream) - readObject() メソッド

3) シリアル化可能なインターフェイス(シリアル化可能)

オブジェクトはシリアル化する前にシリアル化インターフェイスを実装する必要があります。実装しないと例外が発生します。

注: このインターフェイスにはメソッドはありません。これは単なる [標準]

1.最も基本的なシーケンス シリアル化と逆シリアル化のプロセス

シリアル化と逆シリアル化はすべて Object オブジェクトに対して操作されます。ここでは、オブジェクトのシリアル化と逆シリアル化のプロセスを示す簡単なケースを示します。

1. 新しい Student クラス (テスト クラス) を作成します

注: シリアル化操作を実行するには、シリアル化インターフェイスを実装するクラスが必要です。 !

@SuppressWarnings("serial")
public class Student implements Serializable{
    private String stuno;//id
    private String stuna;//姓名
    private int stuage;//年龄
    public String getStuno() {
        return stuno;
    }
    public void setStuno(String stuno) {
        this.stuno = stuno;
    }
    public String getStuna() {
        return stuna;
    }
    public void setStuna(String stuna) {
        this.stuna = stuna;
    }
    public Student() {
        super();
        // TODO Auto-generated constructor stub
    }
    public Student(String stuno, String stuna, int stuage) {
        super();
        this.stuno = stuno;
        this.stuna = stuna;
        this.stuage = stuage;
    }
    @Override
    public String toString() {
        return "Student [stuno=" + stuno + ", stuna=" + stuna + ", stuage=" + stuage + "]";
    }
    public int getStuage() {
        return stuage;
    }
    public void setStuage(int stuage) {
        this.stuage = stuage;
    }
}

2. Student クラスのインスタンスをファイルにシリアル化します

基本的な操作手順は次のとおりです:

1)、次のように指定します。シリアル化してファイルを保存します

#2)、ObjectOutputStream クラスを構築します

#3)、Student クラスを構築します

#4)、writeObject メソッドを使用してシリアル化します

5)、close() メソッドを使用してストリームを閉じます

String file="demo/obj.dat";
        //对象的序列化
        ObjectOutputStream oos=new ObjectOutputStream(
                new FileOutputStream(file));
        //把Student对象保存起来,就是对象的序列化
        Student stu=new Student("01","mike",18);
        //使用writeObject方法序列化
        oos.writeObject(stu);
        oos.close();
実行結果: シリアル化されたファイル obj.dat がデモ ディレクトリ

# に生成されたことがわかります。

## 3. ファイルをデシリアライズし、Student クラス オブジェクトを読み出します。

基本的な操作手順は次のとおりです。1)、ファイルを指定して、逆シリアル化する

#2)、ObjectInputStream クラスを構築する

#3)、readObject メソッドを使用して逆シリアル化する

#1)、close メソッドを使用してストリームを閉じる

String file="demo/obj.dat";
        ObjectInputStream ois =new ObjectInputStream(
                new FileInputStream(file));
        //使用readObject()方法序列化
        Student stu=(Student)ois.readObject();//强制类型转换
        System.out.println(stu);
        ois.close();
実行結果:

注: ファイルを逆シリアル化する場合、readObject メソッドによって取り出されるオブジェクトはデフォルトで Object 型であり、次のようにする必要があります。対応する型に強制的に設定されます。

2. Transient および ArrayList のソース コード分析

日々のプログラミング プロセスでは、すべての要素が必要でない場合があります。コンパイラによってシリアル化されるクラスですが、このときどうすればよいですか?

Java は、jvm によって自動的にシリアル化されたくない要素を変更するための transient キーワードを提供します。このキーワードについて簡単に説明しましょう。

transient キーワード: transient によって変更された要素は、デフォルトでは jvm によってシリアル化されませんが、この要素のシリアル化を自分で完了することができます。

注:

1) 将来のネットワーク プログラミングでは、送信する必要のない特定の要素がある場合、それらの要素をトランジェントで変更してトラフィックを節約できます。はい パフォーマンスを向上させるための効率的な要素のシリアル化。 2) writeObject を使用して、この要素のシリアル化を自分で完了できます。

ArrayList

はこの方法を使用して最適化されます。 ArrayList のコア コンテナー Object[] elementData は一時的な変更を使用しますが、writeObject は elementData 配列自体のシリアル化を実装します。配列内の有効な要素のみがシリアル化されます。

readObject も同様です。

#--------------私なりの連載方法 - --------------

シリアル化するクラスに 2 つのメソッドを追加します (

これら 2 つのメソッドは ArrayList ソース コードから派生しています。 JVM から抽出された特別なメソッドは、これら 2 つのメソッドを自動的に使用して、このアクションを完了します。

ここで別の質問があります。なぜシリアル化と逆シリアル化を手動で完了する必要があるのですか?どういう意味ですか?

この問題は ArrayList のソース コードから分析する必要があります:

それは確認できます。 ArrayList のソース コードを確認する 自己シリアル化の目的: ArrayList の最下位層は配列です。自己シリアル化により、配列内の無効な要素をフィルタリングし、配列内の有効な要素のみをシリアル化できます。 それにより、パフォーマンスが向上します

したがって、実際のプログラミング プロセス中に、パフォーマンスを向上させるために必要に応じてシリアル化を自分たちで完了できます。

三、序列化中子父类构造函数问题

在类的序列化和反序列化中,如果存在子类和父类的关系时,序列化和反序列化的过程又是怎么样的呢?

这里我写一个测试类来测试子类和父类实现序列化和反序列化时构造函数的实现变化。

public static void main(String[] args) throws IOException {
        // TODO Auto-generated method stub
        String file="demo/foo.dat";
        ObjectOutputStream oos=new ObjectOutputStream(
                new FileOutputStream(file));
        Foo2 foo2 =new Foo2();
        oos.writeObject(foo2);
        oos.flush();
        oos.close();
    }

}
class Foo implements Serializable{
    public Foo(){
        System.out.println("foo");
    }
}
class Foo1 extends Foo{
    public Foo1(){
        System.out.println("foo1");
    }
    
}
class Foo2 extends Foo1{
    public Foo2(){
        System.out.println("foo2");
    }
}

运行结果:这是序列化时递归调用了父类的构造函数

接来下看看反序列化时,是否递归调用父类的构造函数。

ObjectInputStream ois=new ObjectInputStream(
new FileInputStream(file));
Foo2 foo2=(Foo2)ois.readObject();
ois.close();

运行结果:控制台没有任何输出。

那么这个结果是否证明反序列化过程中父类的构造函数就是始终不调用的呢?

然而不能证明!!

因为再看下面这个不同的测试例子:

class Bar {
    public Bar(){
        System.out.println("bar");
    }
}
class Bar1 extends Bar implements Serializable{
    public Bar1(){
        System.out.println("bar1");
    }
}
class Bar2 extends Bar1{
    public Bar2(){
        System.out.println("bar2");
    }
}

我们用这个例子来测试序列化和反序列化。

序列化结果:

反序列化结果:没实现序列化接口的父类被显示调用构造函数

【反序列化时】,向上递归调用构造函数会从【可序列化的一级父类结束】。即谁实现了可序列化(包括继承实现的),谁的构造函数就不会调用。

总结:

1)父类实现了serializable接口,子类继承就可序列化。

子类在反序列化时,父类实现了序列化接口,则不会递归调用其构造函数。

2)父类未实现serializable接口,子类自行实现可序列化

子类在反序列化时,父类没有实现序列化接口,则会递归调用其构造函数。

本文来自 java入门 栏目,欢迎学习!

以上がJava オブジェクトのシリアル化と逆シリアル化の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はcnblogs.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。