단순한 1L 대신 긴 serialVersionUID를 생성하는 이유는 무엇입니까?


210

클래스가 Eclipse에서 Serializable을 구현할 때 add default serialVersionUID(1L)또는 generated 옵션이 있습니다 serialVersionUID(3567653491060394677L). 나는 첫 번째 것이 더 시원하다고 생각하지만, 두 번째 옵션을 사용하는 사람들을 여러 번 보았습니다. 생성 할 이유가 long serialVersionUID있습니까?


49
정확히 어떻게 중복됩니까? 왜 그것을 전혀 생성하지는 않지만 긴 serialVersionUID를 생성하는 이유는 묻지 않습니다.
IAdapter

13
존 소총이의 serialVersionUID를 사용하는 경우, 그는 0L를 사용 stackoverflow.com/questions/605828/...를 )
하노 Fietz

8
@HannoFietz : 정확한 문장은 다음과 같습니다. "간단 성을 위해 필요할 때마다 0 부터 시작 하여 1늘리는 것이 좋습니다 ." 그는 0L처음에 사용하는 것처럼 들립니다 .
OR Mapper

7
@ ORMapper : Jon Skeet이 돌아가서 그가 작성한 코드를 업데이트해야한다는 것을 의미합니까? 심지어 구조적 비 호환성까지. p! 이교!
Thilo

답변:


90

내가 알 수있는 한, 그것은 이전 릴리스와의 호환성을위한 것입니다. 이전에 serialVersionUID 사용을 무시한 다음 호환 가능 해야 하지만 직렬화가 중단 되도록 변경 한 경우에만 유용합니다 .

자세한 내용은 Java Serialization Spec 을 참조하십시오.


69

직렬화 버전 UID의 목적은 객체의 유효한 직렬화를 수행하기 위해 클래스의 다른 버전을 추적하는 것입니다.

아이디어는 클래스의 특정 버전에 고유 한 ID를 생성하는 것이며, 이는 직렬화 된 객체의 구조에 영향을주는 새 필드와 같이 클래스에 추가 된 새로운 세부 사항이있을 때 변경됩니다.

1L미래에 클래스 정의가 변경되어 직렬화 된 객체의 구조가 변경되면 객체를 직렬화 해제하려고 할 때 문제가 발생할 가능성이 있음 을 의미하는 것과 같은 항상 동일한 ID를 사용하십시오 .

ID가 생략되면 Java는 실제로 객체의 필드를 기반으로 ID를 계산하지만 비싼 프로세스이므로 수동으로 제공하면 성능이 향상됩니다.

다음은 클래스의 직렬화 및 버전 관리에 대해 설명하는 기사 링크입니다.


50
1L 사용의 기본 개념은 클래스 속성이나 메서드를 변경할 때마다이를 늘리는 것입니다.
Powerlord

39
serialversionUID가 자동으로 생성되도록하는 런타임 성능 영향은 없습니다. javac에 의해 컴파일 타임에 생성됩니다 ... 클래스의 바이트 코드를 디 컴파일하면 실제로 바이트 코드에 변수가 정적으로 표시됩니다.
Jared

11
참고 사항 : 숫자를 명시 적으로 관리하면 클래스 정의를 정확히 동일하게 요구하지 않고 클래스의 버전을 "호환"할 수있는 시점을 결정할 수 있습니다.
Scott Stanchfield

23
@ Jared Josh Bloch의 Effective Java : 2nd Edition의 항목 75에 따르면 : "당신이 작성하는 모든 직렬화 가능 클래스에 명시 적 직렬 버전 UID를 선언하십시오 .... 직렬 버전 UID가 제공되지 않으면 런타임에 UID를 생성하기 위해 비싼 계산이 필요합니다. "
Colin K

12
@coobird 이것이 기본 serialVersionUID가 권장되지 않는 주된 이유 인 것 같습니다 Note - It is strongly recommended that all serializable classes explicitly declare serialVersionUID values, since the default serialVersionUID computation is highly sensitive to class details that may vary depending on compiler implementations, and can thus result in unexpected serialVersionUID conflicts during deserialization, causing deserialization to fail. . 위의 주석은 Java Object Serialization Specification 버전 6.0
user624558

20

생성 된 것의 주된 이유는 이미 사본을 보유한 기존 버전의 클래스와 호환되도록하기 위해서입니다.


1
OK 그러나 항상 1L을 가지고 있다면 동일합니다. 내가 변경하더라도 모든 것이 호환됩니다.
grep

@grep은 필드의 이름을 바꾸고 무슨 일이 일어나는지보십시오.
Trejkaz

1
@grep 요점은 이전에 serialVersionUID를 생략 한 클래스가 있으면 생성 된 클래스를 자동으로 얻었을 것입니다. 이제 명시 적으로 설정을 시작하고 1L로 설정하면 기존 클래스와 호환되지 않지만 생성 된 값을 사용하면 호환됩니다.
marc82ch

15

의 "long"기본값은 기본 직렬화 동작에서 계산 된 Java Serialization Specification에serialVersionUID 정의 된 기본값입니다.

따라서 기본 버전 번호를 추가하면 구조적으로 변경된 것이없는 한 클래스의 직렬화가 더 디 직렬화되지만 클래스를 변경하면 (필드 추가 / 제거) 클래스를 업데이트해야합니다. 일련 번호.

기존 비트 스트림과 호환되지 않아도되는 1L경우 변경 사항이있을 때 필요에 따라 버전을 추가하고 버전을 늘릴 수 있습니다. 즉, 변경된 클래스의 기본 직렬화 버전이 이전 클래스의 기본 버전과 다른 경우입니다.


11

구현하는 클래스를 정의 할 때마다 반드시 serialVersionUID를 작성해야합니다 java.io.Serializable. 그렇지 않으면 자동으로 생성되지만 이것은 나쁘다. 자동 생성 된 serialVersionUID는 클래스의 메서드 서명을 기반으로하므로 나중에 클래스를 변경하여 메서드를 추가하면 (예 :) "이전"버전의 클래스를 직렬화 해제하는 데 실패합니다. 일어날 수있는 일은 다음과 같습니다.

  1. serialVersionUID를 정의하지 않고 클래스의 첫 번째 버전을 만듭니다.
  2. 클래스의 인스턴스를 영구 저장소로 직렬화하십시오. serialVersionUID가 자동으로 생성됩니다.
  3. 클래스를 수정하여 새 메소드를 추가하고 애플리케이션을 재배치하십시오.
  4. 2 단계에서 직렬화 된 인스턴스를 직렬화 해제하려고 시도했지만 자동 생성 된 serialVersionUID가 다르기 때문에 이제는 실패합니다 (성공해야 할 경우).

1
사실, 이전 버전의 클래스를 역 직렬화하는 것은 더 이상 동일하지 않기 때문에 실패해야합니다. 클래스 서명이 변경 될 때 직렬화 해제 실패를 방지하기 위해 serialVersionUID를 스스로 생성하는 것이 좋습니다. 귀하의 제안은 적절하지만 그 목적에 대한 귀하의 설명은 단순히 잘못되고 오도됩니다. 답을 수정하는 것이 현명합니다.
mostruash

6

serialVersionUID를 지정하지 않으면 Java가 즉시 작성합니다. 생성 된 serialVersionUID는 해당 번호입니다. 클래스에서 이전의 직렬화 된 버전과 호환되지 않지만 해시를 변경하는 것을 변경하는 경우 생성 된 매우 큰 serialVersionUID (또는 오류 메시지의 "예상"번호)를 사용해야합니다 . 그렇지 않으면 모든 것을 스스로 추적하는 경우 0, 1, 2 ...가 더 좋습니다.


==> 1을 의미했습니다. 다른 클래스 변경 사항을 호환하려면 생성 된 클래스를 사용하십시오. 2. 다른 버전의 클래스가 호환되지 않게하려면 기본 버전을 사용하고 증가에 신중해야합니다. 내가 제대로 이해 했습니까?
JavaDeveloper 2019 년

4

serialVersionUID (3567653491060394677L)를 생성하는 대신 serialVersionUID (1L)를 사용하면 무언가를 말하는 것입니다.

이 클래스의 버전 번호 1과 호환되지 않는이 클래스의 직렬화 된 버전을 가진이 클래스를 다루지 않는 시스템이 없다고 100 % 확신한다고합니다.

직렬화 된 버전 기록이 알려지지 않았다는 변명을 생각할 수 있다면 자신감을 가지고 말하기가 어려울 수 있습니다. 평생 동안 성공적인 수업은 많은 사람들에 의해 유지되고 많은 프로젝트에 살고 많은 시스템에 상주 할 것입니다.

당신은 그것을 고민 할 수 있습니다. 또는 당신은 잃고 싶어 복권을 재생할 수 있습니다. 버전을 생성하면 문제가 발생할 가능성이 거의 없습니다. "아직 아무도 1을 사용하지 않았다"고 가정하면 확률은 작은 것보다 큽니다. 우리 모두가 0과 1이 멋지다고 생각하기 때문입니다.

-

serialVersionUID (1L)를 사용하지 않고 serialVersionUID (3567653491060394677L)를 생성하면 무언가를 말하는 것입니다.

사람들 이이 클래스의 역사에서 다른 버전 번호를 수동으로 만들거나 생성했을 수 있으며 Longs가 큰 숫자이기 때문에 걱정하지 않아도됩니다.

어떤 방식 으로든 클래스가 존재하거나 존재하는 전체 유니버스에서 클래스를 직렬화 할 때 사용 된 버전 번호 기록을 완벽하게 알지 못하면 기회가 생깁니다. 1이 AOK인지 100 % 확신 할 시간이 있다면 그것을 찾으십시오. 그것이 많은 일이라면, 계속해서 맹목적으로 숫자를 생성하십시오. 복권 당첨 가능성이 더 높습니다. 그렇다면, 알려주세요. 맥주를 사겠습니다.

추첨에 대한이 모든 이야기를 통해 serialVersionUID가 무작위로 생성된다는 인상을 받았을 것입니다. 실제로 숫자 범위가 Long의 가능한 모든 값에 균등하게 분배되는 한 괜찮습니다. 그러나 실제로는 다음과 같이 수행됩니다.

http://docs.oracle.com/javase/6/docs/platform/serialization/spec/class.html#4100

당신이 얻는 유일한 차이점은 무작위 소스가 필요 없다는 것입니다. 클래스 자체의 변경 사항을 사용하여 결과를 변경하고 있습니다. 그러나 비둘기 구멍 원리에 따르면 여전히 잘못되어 충돌을 일으킬 가능성이 있습니다. 정말 믿을 수 없을 것입니다. 나 한테 맥주를 가져다 줘서 행운을 빌어

그러나 클래스가 하나의 시스템과 하나의 코드베이스에만 살더라도 손으로 숫자를 늘리면 충돌 가능성이 전혀 없다고 생각하면 인간을 이해하지 못한다는 것을 의미합니다. :)


"시스템"이 클래스에 닿으면, 즉 직렬화가 호환되지 않는 방식으로 클래스를 변경하면 해당 시스템이 serialVersionUID도 변경하는지 여부는 문제입니다. 나는 그것이 길 때 그것을 변경하는 것을 기억할 확률이 더 작다고 생각하지 않습니다. 변경 사항을 기억하기가 더 쉽다면 실수로 변경하지 않았 음을 알기 때문에 그 반대가 사실이라고 생각합니다.
Reto Gmür

2
이것은 거짓입니다! serialVersionUID를 생성하고 1L이 아닌 소스 코드에서 해당 값을 선언하거나 실제로 말하고있는 것이 아닙니다 : 미래에 정의되지 않은 효과로 감지되지 않는 충돌을 원하며 Java 또는 다른 사람이 이것을 막지 않기를 바랍니다. . 자바는 편집증이지만 순종적입니다. 인간은 보통 큰 숫자를 엉망으로 만들지 않습니다. 이런 식으로 클래스가 변경 될 때 java는 여전히 호환되지 않는 이전 버전을 직렬화 해제 할 수 있습니다. MwoaHaHa ...;)
Superole

1

serialVersionUID는“정적 필드가 직렬화되지 않습니다”라는 규칙의 예외입니다. ObjectOutputStream은 serialVersionUID의 값을 매번 출력 스트림에 기록합니다. ObjectInputStream은이를 다시 읽고 스트림에서 읽은 값이 현재 클래스 버전의 serialVersionUID 값과 일치하지 않으면 InvalidClassException을 발생시킵니다. 또한 직렬화 할 클래스에 공식적으로 선언 된 serialVersionUID가없는 경우 컴파일러는 클래스에 선언 된 필드를 기반으로 생성 된 값을 사용하여이를 자동으로 추가합니다.


0

많은 경우 기본 ID는 고유하지 않기 때문입니다. 독창적 인 컨셉을 만들기위한 id를 만듭니다.


답을 좀 더 육체적으로 편집 할 수 있습니까? 이것은 주석처럼 보입니다. 감사.
Gray

0

@David Schmitts 답변에 추가하기 위해, 나는 원칙적으로 항상 기본 1L을 사용합니다. 나는 돌아가서 몇 번만 변경해야했지만 변경을하고 매번 기본 번호를 하나씩 업데이트 할 때 알았습니다.

현재 회사에서는 자동 생성 번호가 필요하므로 규칙에 사용하지만 기본값을 선호합니다. 내가 취한 것은 그것이 일하는 규칙이 아닌 경우 어떤 이유로 직렬화 된 클래스의 구조를 지속적으로 변경한다고 생각하지 않는 한 기본값을 사용하십시오.


0

직렬화 버전 UID의 목적은 객체의 유효한 직렬화를 수행하기 위해 클래스의 다른 버전을 추적하는 것입니다.

아이디어는 클래스의 특정 버전에 고유 한 ID를 생성하는 것이며, 이는 직렬화 된 객체의 구조에 영향을주는 새 필드와 같이 클래스에 추가 된 새로운 세부 사항이있을 때 변경됩니다.

간단한 설명 :

데이터를 직렬화하고 있습니까?

직렬화는 기본적으로 클래스 데이터를 파일 / 스트림 등에 작성합니다. 역 직렬화는 해당 데이터를 클래스로 다시 읽습니다.

생산을 시작 하시겠습니까?

중요하지 않은 / 가짜 데이터로 무언가를 테스트하는 경우 직렬화를 직접 테스트하지 않는 한 걱정하지 마십시오.

이것이 첫 번째 버전입니까?

그렇다면 serialVersionUID = 1L을 설정하십시오.

이것이 두 번째, 세 번째 등의 자극적 인 버전입니까?

이제 serialVersionUID에 대해 걱정할 필요가 있고 깊이 살펴보아야합니다.

기본적으로, 쓰기 / 읽기가 필요한 클래스를 업데이트 할 때 버전을 올바르게 업데이트하지 않으면 오래된 데이터를 읽으려고 할 때 오류가 발생합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.