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()); } } }
テスト結果:
ここでは、オブジェクトをファイルに保存し、読み出すことに成功しました。この時点でシリアル化インターフェイスを実装しないと、例外が発生します。実装された 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 メソッドを再度テストします:
この時点でエラーが報告されていることがわかります。e を使用します。 .printStackTrace( );例外の詳細を表示します:
不明なソースが表示されます。シリアル化されていないため、保存したり読み取ったりすることはできません。
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
それを見てください。最もパフォーマンスが良いのは 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 + "]"; } }
テスト結果:
若要要整合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 サイトの他の関連記事を参照してください。