1. 개요

의 serialVersionUID의 속성 직렬화하는 데 사용되는 식별자이다 / A의 객체 직렬화 직렬화 클래스.

이 빠른 사용방법(예제)에서는 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;
}

다음으로 두 개의 유틸리티 클래스가 필요합니다. 하나는 AppleProduct 객체를 String 으로 직렬화하고 다른 하나는 해당 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의 에 객체 문자열 instanc , 전자 바이트 사용하여 인코딩 Base64로를 .

그런 다음 해당 String 을 deserialization 메서드에 대한 인수로 사용하여 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

이제 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 를 변경하여 버전 / 상태를 수정했습니다. 결과적으로 deserialization 중에 호환되는 클래스가 발견되지 않았고 InvalidClassException 이 발생했습니다.

경우 의 serialVersionUID가 A의 제공되지 직렬화 클래스, JVM은 자동으로 하나를 생성합니다. 그러나 serialVersionUID을 제공 하고 클래스 변경 후이를 업데이트하여 serialization / deserialization 프로세스를 제어 할 수 있도록하는 것이 좋습니다. 이후 섹션에서 자세히 살펴 보겠습니다.

3. 호환 가능한 변경 사항

기존 AppleProduct 클래스에 새 필드 lightningPort추가해야한다고 가정 해 보겠습니다 .

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

새 필드를 추가하는 중이므로 serialVersionUID변경할 필요가 없습니다 . 이는 deserialization 프로세스 중에 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 에서 찾을 수 있습니다 .