1. 概要

serialVersionUID 属性は、Serializableクラスのオブジェクトをシリアル化/逆シリアル化するために使用される識別子です。

このクイックチュートリアルでは、 serialVersionUID とは何か、およびその使用方法について例を挙げて説明します。

2. シリアルバージョンUID

簡単に言うと、 serialVersionUID 属性を使用して、 Serializable クラスのバージョンを記憶し、ロードされたクラスとシリアル化されたオブジェクトに互換性があることを確認します。

異なるクラスのserialVersionUID属性は独立しています。 したがって、異なるクラスが一意の値を持つ必要はありません。

次に、いくつかの例を通してserialVersionUIDの使用方法を学びましょう。

シリアライズ可能なクラスを作成し、serialVersionUID識別子を宣言することから始めましょう。

public class AppleProduct implements Serializable {

    private static final long serialVersionUID = 1234567L;

    public String headphonePort;
    public String thunderboltPort;
}

次に、2つのユーティリティクラスが必要です。1つは AppleProductオブジェクトをString、にシリアル化するためのもので、もう1つはその String:からオブジェクトを逆シリアル化するためのものです。

public class SerializationUtility {

    public static void main(String[] args) {
        AppleProduct macBook = new AppleProduct();
        macBook.headphonePort = "headphonePort2020";
        macBook.thunderboltPort = "thunderboltPort2020";

        String serializedObj = serializeObjectToString(macBook);
 
        System.out.println("Serialized AppleProduct object to string:");
        System.out.println(serializedObj);
    }

    public static String serializeObjectToString(Serializable o) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(o);
        oos.close();
        
        return Base64.getEncoder().encodeToString(baos.toByteArray());
    }
}
public class DeserializationUtility {
 
    public static void main(String[] args) {
 
        String serializedObj = ... // ommited for clarity
        System.out.println(
          "Deserializing AppleProduct...");
 
        AppleProduct deserializedObj = (AppleProduct) deSerializeObjectFromString(
          serializedObj);
 
        System.out.println(
          "Headphone port of AppleProduct:"
            + deserializedObj.getHeadphonePort());
        System.out.println(
          "Thunderbolt port of AppleProduct:"
           + deserializedObj.getThunderboltPort());
    }
 
    public static Object deSerializeObjectFromString(String s)
      throws IOException, ClassNotFoundException {
  
        byte[] data = Base64.getDecoder().decode(s);
        ObjectInputStream ois = new ObjectInputStream(
          new ByteArrayInputStream(data));
        Object o = ois.readObject();
        ois.close();
        return o;
    }
}

まず、 SerializationUtility.java を実行します。これにより、 AppleProductオブジェクトがString instanc e、に保存(シリアル化)されます。 Base64

次に、その String を逆シリアル化メソッドの引数として使用して、 DeserializationUtility.java、を実行します。これは、指定されたから AppleProduct オブジェクトを再アセンブル(逆シリアル化)します。 ]文字列。

生成される出力は次のようになります。

Serialized AppleProduct object to string:
rO0ABXNyACljb20uYmFlbGR1bmcuZGVzZXJpYWxpemF0aW9uLkFwcGxlUHJvZHVjdAAAAAAAEta
HAgADTAANaGVhZHBob25lUG9ydHQAEkxqYXZhL2xhbmcvU3RyaW5nO0wADmxpZ2h0ZW5pbmdQb3
J0cQB+AAFMAA90aHVuZGVyYm9sdFBvcnRxAH4AAXhwdAARaGVhZHBob25lUG9ydDIwMjBwdAATd
Gh1bmRlcmJvbHRQb3J0MjAyMA==
Deserializing AppleProduct...
Headphone port of AppleProduct:headphonePort2020
Thunderbolt port of AppleProduct:thunderboltPort2020

ここで、AppleProduct。javaのserialVersionUID 定数を変更し、以前に生成された同じ文字列からAppleProductオブジェクトの逆シリアル化を再試行します。 DeserializationUtility.java を再実行すると、この出力が生成されます。

Deserializing AppleProduct...
Exception in thread "main" java.io.InvalidClassException: com.baeldung.deserialization.AppleProduct; local class incompatible: stream classdesc serialVersionUID = 1234567, local class serialVersionUID = 7654321
	at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:616)
	at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1630)
	at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1521)
	at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1781)
	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1353)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:373)
	at com.baeldung.deserialization.DeserializationUtility.deSerializeObjectFromString(DeserializationUtility.java:24)
	at com.baeldung.deserialization.DeserializationUtility.main(DeserializationUtility.java:15)

クラスのserialVersionUIDを変更することにより、そのバージョン/状態を変更しました。 その結果、逆シリアル化中に互換性のあるクラスが見つからず、InvalidClassExceptionがスローされました。

serialVersionUIDSerializableクラスで提供されていない場合、JVMは自動的に生成します。 ただし、 serialVersionUID値を指定し、クラスの変更後に更新して、シリアル化/逆シリアル化プロセスを制御できるようにすることをお勧めします。 これについては、後のセクションで詳しく説明します。

3. 互換性のある変更

新しいフィールドlightningPortを既存のAppleProductクラスに追加する必要があるとします。

public class AppleProduct implements Serializable {
//...
    public String lightningPort;
}

新しいフィールドを追加するだけなので、serialVersionUIDを変更する必要はありません。 これは、逆シリアル化プロセス中に、lightningPortフィールドのデフォルト値としてnullが割り当てられるためです。

DeserializationUtility クラスを変更して、この新しいフィールドの値を出力してみましょう。

System.out.println("LightningPort port of AppleProduct:"
  + deserializedObj.getLightningPort());

ここで、 DeserializationUtility クラスを再実行すると、次のような出力が表示されます。

Deserializing AppleProduct...
Headphone port of AppleProduct:headphonePort2020
Thunderbolt port of AppleProduct:thunderboltPort2020
Lightning port of AppleProduct:null

4. デフォルトのシリアルバージョン

SerializableクラスのserialVersionUID状態を定義しない場合、Javaは、クラス名、インスタンスフィールド、等々。

単純なSerializableクラスを定義しましょう:

public class DefaultSerial implements Serializable {
}

このクラスのインスタンスを次のようにシリアル化すると、次のようになります。

DefaultSerial instance = new DefaultSerial();
System.out.println(SerializationUtility.serializeObjectToString(instance));

これにより、シリアル化されたバイナリのBase64ダイジェストが出力されます。

rO0ABXNyACpjb20uYmFlbGR1bmcuZGVzZXJpYWxpemF0aW9uLkRlZmF1bHRTZXJpYWx9iVz3Lz/mdAIAAHhw

前と同じように、このインスタンスをダイジェストから逆シリアル化できるはずです。

String digest = "rO0ABXNyACpjb20uYmFlbGR1bmcuZGVzZXJpY" 
  + "WxpemF0aW9uLkRlZmF1bHRTZXJpYWx9iVz3Lz/mdAIAAHhw";
DefaultSerial instance = (DefaultSerial) DeserializationUtility.deSerializeObjectFromString(digest);

ただし、このクラスにいくつかの変更を加えると、シリアル化の互換性が損なわれる可能性があります。たとえば、このクラスに private フィールドを追加すると、次のようになります。

public class DefaultSerial implements Serializable {
    private String name;
}

次に、同じBase64ダイジェストをクラスインスタンスに逆シリアル化しようとすると、 InvalidClassException:が発生します。

Exception in thread "main" java.io.InvalidClassException: 
  com.baeldung.deserialization.DefaultSerial; local class incompatible: 
  stream classdesc serialVersionUID = 9045863543269746292, 
  local class serialVersionUID = -2692722436255640434

この種の望ましくない非互換性のため、SerializableクラスでserialVersionUIDを宣言することは常に良い考えです。 このようにして、クラス自体が進化するときにバージョンを維持または進化させることができます。

5. 結論

この簡単な記事では、 serialVersionUID 定数を使用して、シリアル化されたデータのバージョン管理を容易にする方法を示しました。

いつものように、この記事全体で使用されているコードサンプルは、GitHubにあります。