Javaでシリアル化する方法

爱喝马黛茶的安东尼
リリース: 2019-11-12 17:44:30
オリジナル
2445 人が閲覧しました

Javaでシリアル化する方法

1. シリアル化と逆シリアル化

シリアル化: ヒープ メモリ内の Java オブジェクト データを指します。何らかの方法でディスク ファイルにペアを作成するか、他のネットワーク ノードに渡します (ネットワーク送信)。このプロセスはシリアル化と呼ばれ、通常はデータ構造またはオブジェクトをバイナリに変換するプロセスを指します。

即将对象转化为二进制,用于保存,或者网络传输。
ログイン後にコピー

デシリアライゼーション: ディスク ファイル内のオブジェクト データまたはネットワーク ノード上のオブジェクト データを Java オブジェクト モデルに復元するプロセス。つまり、シリアル化の過程で生成されたバイナリ文字列をデータ構造またはオブジェクトに変換するプロセスです。

与序列化相反,将二进制转化成对象。
ログイン後にコピー

2. シリアル化の役割

① 保存したいメモリ内のオブジェクトをファイルまたはデータベースに保存する場合;

② ソケットを使用してネットワーク上でオブジェクトを送信したい場合;

③ RMI 経由でオブジェクトを送信したい場合

一些应用场景,涉及到将对象转化成二进制,序列化保证了能够成功读取到保存的对象。
ログイン後にコピー

3. Java シリアル化の実装

オブジェクトのシリアル化を実現するための最も直接的な操作は、Serializable インターフェイスを実装することです

IO ストリームでオブジェクトを使用しますストリームはシリアル化操作を実装し、オブジェクトをファイルに保存して、それらを読み出すことができます。

最初にオブジェクトを作成し、Serializable インターフェイスを実装します:

import java.io.Serializable;
public class User implements Serializable{
    private static final long serialVersionUID = 1L;
    private String name;
    private int age;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "User [name=" + name + ", age=" + age + "]";
    }
}
ログイン後にコピー

オブジェクト ストリームを使用して、オブジェクトの保存と読み取りのためのツール クラスを作成します:

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class SerializeUtil {
    // 保存对象,序列化
    public static void saveObject(Object object) throws Exception {
        ObjectOutputStream out = null;
        FileOutputStream fout = null;
        try {
            fout = new FileOutputStream("D:/1.txt");
            out = new ObjectOutputStream(fout);
            out.writeObject(object);
        } finally {
            fout.close();
            out.close();
        }
    }
    // 读取对象,反序列化
    public static Object readObject() throws Exception {
        ObjectInputStream in = null;
        FileInputStream fin = null;
        try {
            fin = new FileInputStream("D:/1.txt");
            in = new ObjectInputStream(fin);
            Object object = in.readObject();
            return object;
        } finally {
            fin.close();
            in.close();
        }
    }
}
ログイン後にコピー

テスト:

public class Main {
    public static void main(String[] args) {
        User user = new User();
        user.setName("旭旭宝宝");
        user.setAge(33);
        // 保存
        try {
            SerializeUtil.saveObject(user);
        } catch (Exception e) {
            System.out.println("保存时异常:" + e.getMessage());
        }
        // 读取
        User userObject;
        try {
            userObject = (User) SerializeUtil.readObject();
            System.out.println(userObject);
        } catch (Exception e) {
            System.out.println("读取时异常:" + e.getMessage());
        }
    }
}
ログイン後にコピー

テスト結果:

Javaでシリアル化する方法

ここでは、オブジェクトをファイルに保存し、読み出すことに成功しました。この時点でシリアル化インターフェイスを実装しないと、例外が発生します。実装された Serialiable インターフェイス コードをキャンセルします:

public class User {
    private String name;
    private int age;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "User [name=" + name + ", age=" + age + "]";
    }
}
ログイン後にコピー
ログイン後にコピー

Main メソッドを再度テストします:

Javaでシリアル化する方法

この時点でエラーが報告されていることがわかります。e を使用します。 .printStackTrace( );例外の詳細を表示します:

Javaでシリアル化する方法

不明なソースが表示されます。シリアル化されていないため、保存したり読み取ったりすることはできません。

4. シリアル化 ID の役割

ご覧のとおり、シリアル化するときに、シリアル化 ID である SerialVersionUID フィールドを追加します

private static final long serialVersionUID = 1L;
ログイン後にコピー

このシリアル化 ID は重要な役割を果たし、逆シリアル化が成功するかどうかを決定します。 Java のシリアル化メカニズムは、ランタイム クラスの SerialVersionUID を判断することによってバージョンの一貫性を検証します。逆シリアル化中に、JVM は受信バイト ストリームの SerialVersionUID をローカル エンティティ クラスの SerialVersionUID と比較します。それらが同じである場合、それらは一貫しているとみなされ、そうしないと、シリアル化されたバージョンに一貫性がないという例外が報告されます。

即序列化ID是为了保证成功进行反序列化
ログイン後にコピー

5. デフォルトのシリアル化 ID

「serialVersionUID」という名前の変数を明示的に定義せず、エンティティ クラスに long と入力すると、Java シリアル化メカニズムが自動的にシリアル化バージョンの比較として、コンパイルされたクラスに基づいて SerialVersionUID を生成します。この場合、同じコンパイルで生成されたクラスのみが同じ SerialVersionUID を生成します。たとえば、クラスを作成するときに時間が経つと、要件の変更によりローカル クラスに他のフィールドを追加する必要が生じますが、このとき、デシリアライズ中に SerialVersionUID が不一致になり、デシリアライズが失敗します。では、どうすれば解決できるのでしょうか? 「serialVersionUID」変数をローカルクラスに追加するだけで、値は変更されず、シリアル化と逆シリアル化を実行できます。

如果没有显示指定serialVersionUID,会自动生成一个。
只有同一次编译生成的class才会生成相同的serialVersionUID。
但是如果出现需求变动,Bean类发生改变,则会导致反序列化失败。为了不出现这类的问题,所以我们最好还是显式的指定一个
serialVersionUID。
ログイン後にコピー

6. シリアル化に関するその他の問題

1. 静的変数はシリアル化されません (静的、一時的)

2. 親がクラスはシリアル化を実装し、サブクラスは自動的にシリアル化を実装するため、Serializable インターフェイスを明示的に実装する必要はありません。

3. オブジェクトのインスタンス変数が他のオブジェクトを参照する場合、オブジェクトがシリアル化されると、参照されるオブジェクトもシリアル化されます。

子类序列化时:
如果父类没有实现Serializable接口,没有提供默认构造函数,那么子类的序列化会出错;
如果父类没有实现Serializable接口,提供了默认的构造函数,那么子类可以序列化,父类的成员变量不会被序列化。如果父类
实现了Serializable接口,则父类和子类都可以序列化。
ログイン後にコピー

7. より効率的なシリアル化フレームワークを使用する — Protostuff

実際、Java のネイティブ シリアル化メソッド (Serialable インターフェイスの実装による) はそれほど効率的ではありません。最高ではありません。

シリアル化の効率を分析するためのプロジェクトが github にあります: https://github.com/eishay/jvm-serializers/wiki

Javaでシリアル化する方法

それを見てください。最もパフォーマンスが良いのは Google が開発した Colfer ですが、Colfer は使いにくいため、ほとんどの人が protostuff シリアル化フレームワークを使用しています。フレームワークを適用するには、2 つのライブラリ (コアとランタイム) を導入する必要があります。

①github アドレス: https://github.com/protostuff/protostuff

③Maven を使用する場合は、依存関係を追加します:

<dependency>
  <groupId>io.protostuff</groupId>
  <artifactId>protostuff-core</artifactId>
  <version>1.5.9</version>
</dependency>
ログイン後にコピー
ログイン後にコピー
<dependency>
  <groupId>io.protostuff</groupId>
  <artifactId>protostuff-core</artifactId>
  <version>1.5.9</version>
</dependency>
ログイン後にコピー
ログイン後にコピー

メイン コードを変更

import com.dyuproject.protostuff.LinkedBuffer;
import com.dyuproject.protostuff.ProtobufIOUtil;
import com.dyuproject.protostuff.ProtostuffIOUtil;
import com.dyuproject.protostuff.Schema;
import com.dyuproject.protostuff.runtime.RuntimeSchema;
public class Main {
    public static void main(String[] args) {
        User user = new User();
        user.setName("旭旭宝宝");
        user.setAge(33);
        Schema<User> schema = RuntimeSchema.getSchema(User.class);
        // 保存对象,序列化,转化二进制数据
        LinkedBuffer buffer = LinkedBuffer.allocate(512);
        final byte[] protostuff;
        try {
            protostuff = ProtobufIOUtil.toByteArray(user, schema, buffer);
        } finally {
            buffer.clear();
        }
        // 读取对象,反序列化
        User userObject = schema.newMessage();
        ProtostuffIOUtil.mergeFrom(protostuff, userObject, schema);
        System.out.println(userObject);
    }
}
ログイン後にコピー

User クラスは Serializable インターフェイスを実装していません

public class User {
    private String name;
    private int age;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "User [name=" + name + ", age=" + age + "]";
    }
}
ログイン後にコピー
ログイン後にコピー

テスト結果:

Javaでシリアル化する方法

若要要整合Redis使用,也可以写成一个工具类:

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import com.dyuproject.protostuff.LinkedBuffer;
import com.dyuproject.protostuff.ProtobufIOUtil;
import com.dyuproject.protostuff.Schema;
import com.dyuproject.protostuff.runtime.RuntimeSchema;
public class SerializeUtil {
    private static Map<Class<?>, Schema<?>> cachedSchema = new ConcurrentHashMap<>();
    @SuppressWarnings("unchecked")
    public static <T> byte[] serializer(T obj) {
        Class<T> clazz = (Class<T>) obj.getClass();
        Schema<T> schema = getSchema(clazz);
        return ProtobufIOUtil.toByteArray(obj, schema, LinkedBuffer.allocate(256));
    }
    public static <T> T deSerializer(byte[] bytes, Class<T> clazz) {
        T message;
        try {
            message = clazz.newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
        Schema<T> schema = getSchema(clazz);
        ProtobufIOUtil.mergeFrom(bytes, message, schema);
        return message;
    }
    @SuppressWarnings("unchecked")
    public static <T> Schema<T> getSchema(Class<T> clazz) {
        Schema<T> schema = (Schema<T>) cachedSchema.get(clazz);
        if (schema == null) {
            schema = RuntimeSchema.createFrom(clazz);
            if (schema != null) {
                cachedSchema.put(clazz, schema);
            }
        }
        return schema;
    }
}
ログイン後にコピー

这样即使我们的User类就不用再实现Serialiable接口了,同样可以进行序列化,效率也更高。

php中文网,大量的免费Java入门教程,欢迎在线学习!

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

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート