serialVersionUIDとは何ですか?

serialVersionUIDとは何ですか?

1. 概要

簡単に言えば、the serialVersionUID is a unique identifier for Serializable classes.

これは、ロードされたクラスがシリアライズされたオブジェクトと互換性があることを保証するために、オブジェクトのデシリアライズ中に使用されます。 一致するクラスが見つからない場合、InvalidClassExceptionがスローされます。

2. 例

シリアライズ可能なクラスを作成することから始めて、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オブジェクトがBase64を使用してバイトをエンコードするStringインスタンスe,に保存(シリアル化)されます。

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

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

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

Now, let’s modify the serialVersionUIDconstant in AppleProduct.java, and reattempt to deserialize以前に生成された同じ文字列からのAppleProductオブジェクト。 DeserializationUtility.javaを再実行すると、この出力が生成されます。

Deserializing AppleProduct...
Exception in thread "main" java.io.InvalidClassException: com.example.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.example.deserialization.DeserializationUtility.deSerializeObjectFromString(DeserializationUtility.java:24)
    at com.example.deserialization.DeserializationUtility.main(DeserializationUtility.java:15)

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

3. 例–既存のクラスに新しいフィールドを追加する

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

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

新しいフィールドno change in the serialVersionUID will be requiredを追加しているだけなので。 これは、during the deserialization process, null will be assigned as the default value for the lightningPort fieldであるためです。

この新しいフィールドの値を出力するように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. 結論

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

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