java - serialization uid




serialVersionUID 란 무엇이며 왜 사용해야합니까? (14)

serialVersionUID 란 무엇이며 왜 사용해야합니까?

SerialVersionUID 는 각 클래스의 고유 한 식별자이며 JVM SerialVersionUID 클래스를 사용하여 직렬화 중에 직렬화가 진행되는 동안 동일한 클래스가 사용되도록 클래스의 버전을 비교합니다.

JVM이 지정하지 않으면 JVM에서 하나를 생성하지만 더 많은 제어를 제공합니다. 생성 된 값은 컴파일러마다 다를 수 있습니다. 게다가, 가끔은 낡은 직렬화 된 객체의 역 직렬화를 금지하는 것이 바람직한 경우가 있습니다.이 경우는 serialVersionUID를 변경하면됩니다.

Serializablejavadocs는 다음과 같이 말합니다 :

기본 serialVersionUID 계산은 컴파일러 구현에 따라 달라질 수있는 클래스 세부 사항에 매우 민감하므로 deserialization 중에 예기치 않은 InvalidClassException 이 발생할 수 있습니다.

따라서 serialVersionUID가 더 많은 제어력을 제공하므로 serialVersionUID를 선언해야합니다 .

이 기사 에는 주제에 대한 몇 가지 좋은 점이 있습니다.

serialVersionUID 가없는 경우 Eclipse에서 경고를 발행합니다.

serializable 클래스 Foo는 long 유형의 정적 final serialVersionUID 필드를 선언하지 않습니다.

serialVersionUID 란 무엇이며 왜 중요합니까? 누락 된 serialVersionUID 가 문제를 일으키는 예를 보여주십시오.


field serialVersionUID의 의미를 이해하려면 Serialization / Deserialization이 어떻게 작동하는지 이해해야합니다.

Serializable 클래스 객체가 직렬화 될 때 Java Runtime은 serialVersionUID로 불리는 일련 번호를이 직렬화 된 객체에 연결합니다. 이 직렬화 된 객체를 비 직렬화 할 때 Java Runtime은 직렬화 된 객체의 serialVersionUID와 해당 클래스의 serialVersionUID를 일치시킵니다. 양쪽 모두가 동등하면 (자), 직렬화의 후속 처리가 진행되어, InvalidClassException가 Throw됩니다.

따라서 Serialization / Deserialization 프로세스를 성공적으로 수행하려면 직렬화 된 객체의 serialVersionUID가 클래스의 serialVersionUID와 동일해야합니다. 프로그래머가 serialVersionUID 값을 명시 적으로 프로그램에 지정하면 serialization 및 deserialization 플랫폼과 관계없이 동일한 값이 serialize 된 객체 및 클래스와 연결됩니다 (예 : sun과 같은 플랫폼에서 직렬화가 수행 될 수 있음). MS JVM 및 직렬화 복원은 Zing JVM을 사용하는 다른 플랫폼의 Linux에있을 수 있습니다.

그러나 프로그래머가 serialVersionUID를 지정하지 않은 경우 Java Runtime은 객체의 Serialization \ DeSerialization을 수행하는 동안 자체 알고리즘을 사용하여 serialVersionUID를 계산합니다. 이 serialVersionUID 계산 알고리즘은 JRE마다 다릅니다. 객체가 직렬화되는 환경은 하나의 JRE (예 : SUN JVM)를 사용하고 deserialization이 발생하는 환경은 Linux JVM (zing)을 사용하는 것일 수도 있습니다. 이 경우 serialize 된 객체와 연결된 serialVersionUID는 deserialization 환경에서 계산 된 class의 serialVersionUID와 다릅니다. 차례대로 deserialization이 성공하지 못합니다. 따라서 프로그래머는 항상 그런 상황을 피하기 위해 Serializable 클래스의 serialVersionUID를 지정해야합니다.


구현을 위해 직렬화해야하기 때문에 직렬화하는 경우 (예를 들어 HTTPSession을 직렬화하는 경우 신경 쓰지 ... 저장되어 있는지 여부와 관계없이 양식 객체의 비 직렬화는 신경 쓰지 않아도됩니다) , 당신은 이것을 무시할 수 있습니다.

실제로 직렬화를 사용하는 경우 직렬화를 직접 사용하여 객체를 저장하고 검색하려는 경우에만 중요합니다. serialVersionUID는 클래스 버전을 나타내며 클래스의 현재 버전이 이전 버전과 역 호환되지 않는 경우이를 증가시켜야합니다.

대부분의 경우 직렬화를 직접 사용하지 않을 것입니다. 이 경우 빠른 수정 옵션을 클릭하여 기본 serializable uid를 생성하고 걱정하지 마십시오.


귀찮게하지 마라. 기본 계산은 정말로 좋고 99,9999 %의 경우에 충분하다. 문제가 생기면 이미 언급 한 바와 같이 UID를 도입해야 할 필요가 있음을 알 수 있습니다 (이는 거의 없을 것입니다)


누락 된 serialVersionUID가 문제를 일으킬 수있는 예는 다음과 같습니다.

EJB 모듈을 사용하는 웹 모듈로 구성된이 Java EE 애플리케이션에서 작업하고 있습니다. 웹 모듈은 EJB 모듈을 원격으로 호출하고 Serializable 을 구현하는 POJO 를 인수로 전달합니다.

POJO's 클래스는 EJB jar 내부에 패키징되어 있으며 내부에는 웹 모듈의 WEB-INF / lib에있는 jar 파일이 들어 있습니다. 그것들은 실제로 같은 클래스이지만 EJB 모듈을 패키징 할 때이 POJO의 jar를 압축하여 EJB 모듈과 함께 패키징합니다.

serialVersionUID 선언하지 않았기 때문에 아래 Exception으로 EJB 호출이 실패했습니다.

Caused by: java.io.IOException: Mismatched serialization UIDs : Source
 (Rep.
 IDRMI:com.hordine.pedra.softbudget.domain.Budget:5CF7CE11E6810A36:04A3FEBED5DA4588)
 = 04A3FEBED5DA4588 whereas Target (Rep. ID RMI:com.hordine.pedra.softbudget.domain.Budget:7AF5ED7A7CFDFF31:6227F23FA74A9A52)
 = 6227F23FA74A9A52

먼저 직렬화에 대해 설명해야합니다.
직렬화를 통해 개체를 스트리밍으로 변환하거나 네트워크를 통해 해당 개체를 보내거나 파일에 저장하거나 문자 사용을 위해 DB에 저장할 수 있습니다.

직렬화에 대한 몇 가지 규칙이 있습니다 .

  • 클래스 또는 그 슈퍼 클래스가 Serializable 인터페이스를 구현하는 경우에만, 객체는 직렬화 가능합니다

  • 슈퍼 클래스가 아닌 경우에서도, 오브젝트 자체는 Serializable 인터페이스를 구현하고 있습니다. 그러나 직렬화 가능 클래스의 계층 구조에서 첫 번째 수퍼 클래스는 Serializable 인터페이스를 구현하지 않으므로 인수가없는 생성자를 가져야합니다. 이것을 위반하면 readObject ()는 런타임시 java.io.InvalidClassException을 생성합니다.

  • 모든 원시 형은 직렬화 가능합니다.

  • 일시적인 필드 (일시적 변경 자 포함)는 직렬화되지 않습니다 (즉, 저장되거나 복원되지 않음). Serializable를 구현하는 클래스는 직렬화를 지원하지 않는 클래스의 일시적인 필드 (예 : 파일 스트림)를 표시해야합니다.

  • 정적 필드 (정적 수정 자 포함)는 직렬화되지 않습니다.

객체가 직렬화 될 때 JAVA 런타임 serialVersionID라고하는 일련 번호를 연결합니다.

serialVersionID가 필요한 곳 ​​: 송신자와 수신자가 직렬화에 대해 호환되는지 확인하기위한 직렬화 해제 중입니다. 수신자가 다른 serialVersionID를 사용하여 클래스를로드 한 경우 직렬화가 InvalidClassCastException로 끝납니다.
직렬화 가능 클래스는 static, final 및 long 유형 인 "serialVersionUID"라는 필드를 선언하여 명시 적으로 자체 serialVersionUID를 선언 할 수 있습니다.

예를 들어 이것을 시도해 봅시다.

import java.io.Serializable;    
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
private String empname;
private byte empage;

public String getEmpName() {
    return name;
}
public void setEmpName(String empname) {
    this.empname = empname;
}
public byte getEmpAge() {
    return empage;
}
public void setEmpAge(byte empage) {
    this.empage = empage;
}

public String whoIsThis() {
    StringBuffer employee = new StringBuffer();
    employee.append(getEmpName()).append(" is ).append(getEmpAge()).append("
years old  "));
    return employee.toString();
}
}

직렬화 객체 생성

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class Writer {
public static void main(String[] args) throws IOException {
    Employee employee = new Employee();
    employee.setEmpName("Jagdish");
    employee.setEmpAge((byte) 30);

    FileOutputStream fout = new 
FileOutputStream("/users/Jagdish.vala/employee.obj");
    ObjectOutputStream oos = new ObjectOutputStream(fout);
    oos.writeObject(employee);
    oos.close();
    System.out.println("Process complete");
}
}

사물을 deserializ

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class Reader {
public static void main(String[] args) throws ClassNotFoundException, 
IOException {
    Employee employee = new Employee();
    FileInputStream fin = new 
    FileInputStream("/users/Jagdish.vala/employee.obj");
    ObjectInputStream ois = new ObjectInputStream(fin);
    employee = (Employee) ois.readObject();
    ois.close();
    System.out.println(employee.whoIsThis());
 }
}    

참고 : 이제 Employee 클래스의 serialVersionUID를 변경하고 다음을 저장하십시오.

private static final long serialVersionUID = **4L**;

그리고 Reader 클래스를 실행합니다. Writer 클래스를 실행하지 않으면 예외가 발생합니다.

Exception in thread "main" java.io.InvalidClassException: 
com.jagdish.vala.java.serialVersion.Employee; local class incompatible: 
stream classdesc serialVersionUID = 1, local class serialVersionUID = 4
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:616)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1623)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1518)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1774)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1351)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:371)
at com.krishantha.sample.java.serialVersion.Reader.main(Reader.java:14)

이 serialVersionUID 경고를 무시하도록 Eclipse에 지시 할 수 있습니다.

윈도우> 환경 설정> Java> 컴파일러> 오류 / 경고> 잠재적 인 프로그래밍 문제

혹시 알지 못했다면이 섹션에서 사용할 수있는 다른 많은 경고가 있습니다 (또는 일부는 오류로보고 할 수도 있음) 많은 것이 매우 유용합니다.

  • 잠재적 인 프로그래밍 문제 : 실수로 부울 할당 가능
  • 잠재적 인 프로그래밍 문제 : 널 포인터 액세스
  • 불필요한 코드 : 지역 변수가 읽히지 않습니다.
  • 불필요한 코드 : 중복 null 체크
  • 불필요한 코드 : 불필요한 캐스트 또는 'instanceof'

그리고 더 많은.


조쉬 블로흐 (Josh Bloch)의 책 Effective Java (제 2 판)에이 기회를 놓칠 수 없습니다. 11 장은 Java 직렬화에 없어서는 안될 자원입니다.

Josh에 따르면 자동 생성 된 UID는 클래스 이름, 구현 된 인터페이스 및 모든 공용 및 보호 된 멤버를 기반으로 생성됩니다. 어떤 식 으로든 이것들을 변경하면 serialVersionUID 가 변경됩니다. 따라서 클래스의 하나 이상의 버전 만 직렬화 (프로세스에서 또는 나중에 저장소에서 검색) 할 수 없다는 확신이있는 경우에만 이들을 처리 할 필요가 없습니다.

지금은 무시하고 나중에 클래스를 변경해야하지만 클래스의 이전 버전과의 호환성을 유지해야하는 경우 JDK 도구 serialver 를 사용하여 이전 클래스에서 serialVersionUID 를 생성하고 명시 적으로 설정합니다 새로운 수업에 대해서. (변경에 따라서는, writeObject 메소드 및 readObject 메소드를 추가해 커스텀 직렬화를 구현할 필요가있는 경우가 있습니다. readObject Serializable javadoc」또는 전술의 제 11 장을 참조 해주세요)


필드 데이터는 클래스에 저장된 일부 정보를 나타냅니다. Class는 Serializable 인터페이스를 구현하므로 serialVersionUID 필드를 선언하도록 eclipse가 자동으로 제공됩니다. 그곳에 설정된 값 1부터 시작할 수 있습니다.

해당 경고를 원하지 않으면 다음을 사용하십시오.

@SuppressWarnings("serial")

java.io.Serializable 위한 문서는 아마도 당신이 얻을 수있는 좋은 설명 일 것이다.

직렬화 런타임은 각 직렬화 가능 클래스에 serialVersionUID라고하는 버전 번호를 연결합니다. 직렬화 된 객체의 송신자와 수신자가 직렬화와 관련하여 해당 객체에 대한 클래스를로드했는지 확인하기 위해 직렬화 해제 중에 사용됩니다. 수신자가 해당 송신자의 클래스와 다른 serialVersionUID를 가진 객체의 클래스를로드 한 경우 deserialization으로 인해 InvalidClassException 이 발생합니다. 직렬화 가능 클래스는 " serialVersionUID "라는 필드를 정적, 최종 및 long 유형으로 선언하여 명시 적으로 자체 serialVersionUID를 선언 할 수 있습니다.

ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;

직렬화 가능 클래스가 serialVersionUID를 명시 적으로 선언하지 않은 경우, 직렬화 런타임은 Java (TM) 객체 직렬화 사양에 설명 된대로 클래스의 다양한 측면을 기반으로 해당 클래스의 기본 serialVersionUID 값을 계산합니다. 그러나 기본 serialVersionUID 계산은 컴파일러 구현에 따라 달라질 수있는 클래스 세부 정보에 매우 민감하므로 deserialization 중에 예기치 않은 InvalidClassExceptions 이 발생할 수 있으므로 모든 serializable 클래스에서 serialVersionUID 값을 명시 적으로 선언 하는 것이 좋습니다 . 따라서 서로 다른 Java 컴파일러 구현에서 일관된 serialVersionUID 값을 보장하려면 직렬화 가능 클래스가 명시 적 serialVersionUID 값을 선언해야합니다. 또한 명시 적 serialVersionUID 선언은 가능한 경우 private 한정자를 사용합니다. 이러한 선언은 즉시 선언하는 클래스에만 적용되므로 serialVersionUID 필드는 상속 된 멤버로 유용하지 않습니다.


Java에서 SerialVersionUID내부 Serializable클래스를 사용하는 이유는 무엇 입니까?

serialization는 나중에 해제 직렬화 할 수 있도록 자바 런타임은 클래스의 버전 번호를 생성합니다. 이 버전 번호는 SerialVersionUIDJava 로 알려져 있습니다 .

SerialVersionUID직렬화 된 데이터를 버전 화하는 데 사용됩니다. 클래스 SerialVersionUID가 직렬화 된 인스턴스와 일치하는 경우에만 클래스를 직렬화 해제 할 수 있습니다 . SerialVersionUID클래스에서 선언하지 않으면 Java 런타임에서 생성하지만 권장하지는 않습니다. 기본 메커니즘을 피하기 위해 변수 로 선언 SerialVersionUID하는 것이 좋습니다 private static final long.

Serializable마커 인터페이스를 구현 하여 클래스를 선언 할 때 java.io.SerializableJava 런타임은 기본 직렬화 기법을 사용하여 해당 클래스의 인스턴스를 디스크에 유지합니다 ( Externalizable인터페이스를 사용하여 프로세스를 사용자 정의하지 않았다면) .

또한 Java에서 Serializable 클래스 내에서 SerialVersionUID를 사용하는 이유

코드 : javassist.SerialVersionUID


CheckStyle이 Serializable을 구현하는 클래스의 serialVersionUID가 올바른 값, 즉 직렬 버전 ID 생성기에서 생성되는 값과 일치하는지 확인할 수 있다면 좋을 것입니다. 일련의 DTO가있는 프로젝트가있는 경우 (예 : 기존 serialVersionUID를 삭제하고 다시 생성해야한다는 것을 기억해야합니다.) 현재이를 확인하는 유일한 방법은 각 클래스에 대해 다시 생성하고 비교하는 것입니다. 오래된 것. 이것은 매우 고통 스럽습니다.


SerialVersionUID는 객체의 버전 제어에 사용됩니다. 클래스 파일에 serialVersionUID를 지정할 수도 있습니다. serialVersionUID를 지정하지 않으면 클래스에 필드를 추가하거나 수정할 때 새로운 클래스 및 이전 serialize 된 객체에 대해 생성 된 serialVersionUID가 달라 지므로 이미 직렬화 된 클래스를 복구 할 수 없습니다. Java 직렬화 프로세스는, 직렬화 된 오브젝트의 상태를 복원하기 위해서 (때문에) 올바른 serialVersionUID에 의존해, serialVersionUID가 일치하지 않는 경우는 java.io.InvalidClassException를 Throw합니다.

더 읽기 : http://javarevisited.blogspot.com/2011/04/top-10-java-serialization-interview.html#ixzz3VQxnpOPZ


객체가 직렬화 될 때마다 객체는 객체의 클래스에 대한 버전 ID 번호로 스탬프 처리됩니다.이 ID는 serialVersionUID 라고 하며 클래스 구조에 대한 정보를 기반으로 계산됩니다. 당신이 Employee 클래스를 만들고 그것이 # 333 (JVM에 의해 할당 된) 버전 ID를 가지고 있다고 가정하자. 그 클래스의 객체를 직렬화 할 때 (JVM은 # 333으로 UID를 할당 할 것이다.

상황을 고려해보십시오 - 나중에 클래스를 편집하거나 변경해야하며,이 경우 클래스를 수정하면 JVM에서 새로운 UID를 할당합니다 (# 444). 이제 employee 객체를 deserialize하려고하면 JVM은 직렬화 된 객체 (Employee 객체) 버전 ID (# 333)와 # 444 (변경된 이후) 클래스의 버전 ID를 비교합니다. 비교에서 JVM은 두 버전 UID가 다르다는 것을 알게 될 것이므로 Deserialization은 실패 할 것이다. 따라서 각 클래스의 serialVersionID가 프로그래머 자체에 의해 정의되는 경우. 장래에 클래스가 진화해도 같은 일이되므로 JVM에서는 클래스가 변경 되어도 그 클래스가 직렬화 된 오브젝트와 호환성이있는 것을 항상 인식합니다. 자세한 내용은 HEAD FIRST JAVA의 14 장을 참조하십시오.







serialversionuid