Por que gerar serialVersionUID longo em vez de um 1L simples?


210

Quando a classe implementa Serializable no Eclipse, tenho duas opções: adicionar padrão serialVersionUID(1L)ou gerado serialVersionUID(3567653491060394677L). Eu acho que o primeiro é mais legal, mas muitas vezes eu vi pessoas usando a segunda opção. Existe algum motivo para gerar long serialVersionUID?


49
Como é essa duplicata exata? Eu não pergunto por que gerá-lo, mas por que gerar serialVersionUID longo.
IAdapter 20/05/09

13
Quando Jon Skeet usa serialVersionUID, ele usa 0D: stackoverflow.com/questions/605828/... ;)
Hanno Fietz

8
@ HannoFietz: A frase exata é: "Para simplificar, sugiro começar com 0 e aumentá-lo em 1 cada vez que você precisar." Então, parece que ele usa 0Lapenas inicialmente.
OR Mapper

7
@ORMapper: Você está sugerindo que Jon Skeet precisa voltar e atualizar o código que ele escreveu? Até ao ponto de incompatibilidade estrutural. Suspiro! Heresia!
Thilo

Respostas:


90

Até onde eu sei, isso seria apenas para compatibilidade com versões anteriores. Isso só seria útil se você deixasse de usar um serialVersionUID antes e, em seguida, fizesse uma alteração que você sabe que deveria ser compatível, mas que causa interrupção na serialização.

Consulte a especificação de serialização do Java para obter mais detalhes.


69

O objetivo da versão UID da serialização é acompanhar as diferentes versões de uma classe para executar a serialização válida dos objetos.

A idéia é gerar um ID exclusivo para uma determinada versão de uma classe, que é alterada quando há novos detalhes adicionados à classe, como um novo campo, que afetaria a estrutura do objeto serializado.

Sempre usando o mesmo ID, como 1L significa que, no futuro, se a definição da classe for alterada e causar alterações na estrutura do objeto serializado, haverá uma boa chance de surgirem problemas ao tentar desserializar um objeto.

Se o ID for omitido, o Java calculará o ID para você com base nos campos do objeto, mas acredito que seja um processo caro, portanto, fornecer um manualmente melhorará o desempenho.

Aqui estão alguns links para artigos que discutem serialização e controle de versão de classes:


50
A idéia por trás do uso de 1L é para incrementá-lo toda vez que você altera as propriedades ou métodos da classe.
10609 Powerlord

39
Não há impacto no desempenho do tempo de execução ao permitir que o serialversionUID seja gerado automaticamente - ele é gerado em tempo de compilação por javac ... se você descompilar o bytecode da classe, verá a variável estaticamente no bytecode.
Jared

11
Mais uma observação - ao gerenciar o número explicitamente, você decide quando considera as versões de uma classe "compatíveis", em vez de exigir que a definição da classe seja exatamente a mesma.
Scott Stanchfield

23
@ Jared De acordo com o item 75 do Java efetivo de Josh Bloch: 2ª edição: "declare um UID versão explícita em série em todas as classes serializáveis ​​que você escreve .... Se nenhuma UID versão serial for fornecida, é necessário um cálculo caro para gerar um em tempo de execução . "
Colin K

12
@coobird Essa parece ser a principal razão pela qual o serialVersionUID padrão não é recomendado 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. O comentário acima foi retirado da Especificação de serialização de objetos Java versão 6.0
user624558

20

O principal motivo para o gerado seria torná-lo compatível com uma versão existente da classe que já possui cópias persistentes.


1
OK, mas o mesmo será Se eu tiver sempre 1L. Tudo será compatível, mesmo que eu faça alguma alteração.
grep

@grep tente renomear um campo e veja o que acontece.
Trejkaz

1
@grep o ponto é que, se você tiver uma classe que omitiu o serialVersionUID antes, ela teria obtido o gerado automaticamente. Portanto, agora, você deseja começar a defini-lo explicitamente, defini-lo como 1L o tornaria incompatível com a classe existente, enquanto o uso do valor gerado o mantém compatível.
Marc82ch

15

O padrão "long" de serialVersionUIDé o valor padrão, conforme definido pela Java Serialization Specification , calculado a partir do comportamento de serialização padrão.

Portanto, se você adicionar o número da versão padrão, sua classe será (des) serializada mais rapidamente, desde que nada tenha sido alterado estruturalmente, mas você deverá tomar cuidado para que, se alterar a classe (adicionar / remover campos), também atualize o número de série.

Se você não precisar ser compatível com os fluxos de bits existentes, basta colocar 1Llá e incrementar a versão conforme necessário quando algo mudar. Ou seja, quando a versão padrão de serialização da classe alterada seria diferente da versão padrão da classe antiga.


11

Você absolutamente deve criar um serialVersionUID toda vez que definir uma classe que implementa java.io.Serializable. Caso contrário, um será criado automaticamente para você, mas isso é ruim. O serialVersionUID gerado automaticamente é baseado nas assinaturas de método da sua classe, portanto, se você mudar sua classe no futuro para adicionar um método (por exemplo), a desserialização das versões "antigas" da classe falhará. Aqui está o que pode acontecer:

  1. Crie a primeira versão da sua classe, sem definir o serialVersionUID.
  2. Serialize uma instância da sua classe para um armazenamento persistente; um serialVersionUID é gerado automaticamente para você.
  3. Modifique sua classe para adicionar um novo método e reimplemente seu aplicativo.
  4. Tente desserializar a instância que foi serializada na etapa 2, mas agora falha (quando deve ser bem-sucedida), porque possui um serialVersionUID gerado automaticamente diferente.

1
De fato, a desserialização de versões antigas da classe realmente deve falhar, porque elas não são mais as mesmas. Você sugere gerar serialVersionUID por conta própria para evitar falhas na (des) serialização quando a assinatura da classe é alterada. Embora sua sugestão seja apropriada, sua explicação sobre seu objetivo é simplesmente errada e enganosa. Seria sensato modificar sua resposta.
mostruash 22/02

6

Se você não especificar um serialVersionUID, o Java cria um em tempo real. O serialVersionUID gerado é esse número. Se você alterar algo em sua classe que realmente não a torne incompatível com as versões serializadas anteriores, mas altere o hash, será necessário usar o serialVersionUID de número muito grande gerado (ou o número "esperado" da mensagem de erro) . Caso contrário, se você estiver acompanhando tudo sozinho, 0, 1, 2 ... é melhor.


Você quis dizer ==> 1. Se desejar que diferentes alterações de classes sejam compatíveis, use a gerada. 2. Se você deseja que versões diferentes de classes sejam incompatíveis, use a padrão e seja prudente no incremento. Eu entendi corretamente?
JavaDeveloper

4

Ao usar serialVersionUID (1L) em vez de gerar serialVersionUID (3567653491060394677L), você está dizendo algo.

Você está dizendo que está 100% confiante de que nenhum sistema que jamais toque nessa classe que tenha uma versão serializada incompatível dessa classe com um número de versão 1.

Se você pode pensar em alguma desculpa para o histórico de versão serializado ser desconhecido, pode ser difícil dizer com confiança. Durante sua vida, uma classe de sucesso será mantida por muitas pessoas, viverá em muitos projetos e residirá em muitos sistemas.

Você pode sofrer por isso. Ou você pode jogar na loteria na esperança de perder. Se você gerar a versão, terá uma pequena chance de as coisas darem errado. Se você assumir "Ei, eu aposto que ninguém usou 1 ainda", suas chances são maiores que pequenas. É precisamente porque todos pensamos que 0 e 1 são legais que você tem maiores chances de atingi-los.

-

Ao gerar serialVersionUID (3567653491060394677L), em vez de usar serialVersionUID (1L), você está dizendo algo.

Você está dizendo que as pessoas podem ter criado ou gerado manualmente outros números de versão ao longo do histórico desta classe e você não se importa porque Longs está enlouquecendo grandes números.

De qualquer maneira, a menos que você conheça perfeitamente o histórico dos números de versão usados ​​ao serializar a classe em todo o universo de onde ela existe ou que sempre existirá, você está arriscando. Se você tiver tempo para garantir 100% de certeza de que 1 é AOK, faça isso. Se isso der muito trabalho, vá em frente e gere o número cegamente. É mais provável que você ganhe na loteria do que errar. Se isso acontecer, me avise e eu comprarei uma cerveja para você.

Com toda essa conversa sobre jogar na loteria, posso ter lhe dado a impressão de que serialVersionUID é gerado aleatoriamente. De fato, desde que o intervalo de números seja distribuído uniformemente por todo valor possível de um Long, isso seria bom. No entanto, é realmente feito desta maneira:

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

A única diferença é que você não precisa de uma fonte aleatória. Você está usando as alterações na própria classe para alterar o resultado. Mas, de acordo com o princípio do buraco de pombo, ainda há uma chance de que possa dar errado e ter uma colisão. É incrivelmente improvável. Boa sorte em tirar uma cerveja de mim.

No entanto, mesmo que a classe viva apenas em um sistema e em uma base de código, pensando que incrementar o número manualmente oferece zero chances de colisões, apenas significa que você não entende humanos. :)


Se um "sistema" toca a classe, ou seja, altera a classe de maneira que a serialização se torne incompatível, a questão é se esse sistema também alterará o serialVersionUID. Não acho que as chances sejam menores de que ele se lembre de alterá-lo quando for longo. Acho que o contrário é verdadeiro, se o número é mais fácil de lembrar, as mudanças são maiores que eu noto que acidentalmente não o mudei.
Reto Gmür

2
Isto é falso! Quando você gera serialVersionUID e declara esse valor em seu código-fonte, em vez de 1L ou nada, você está realmente dizendo: Quero uma colisão possivelmente não detectada no futuro com efeitos indefinidos e não quero que java ou humanos evitem isso . Java é paranóico, mas obediente. Os humanos geralmente não mexem com grandes números. Dessa forma, quando a classe muda, o java ainda pode desserializar versões antigas e incompatíveis. MwoaHaHa ...;)
Superole

1

Bem, serialVersionUID é uma exceção à regra de que "campos estáticos não são serializados". ObjectOutputStream grava sempre que o valor de serialVersionUID no fluxo de saída. O ObjectInputStream o lê novamente e, se o valor lido no fluxo não concordar com o valor serialVersionUID na versão atual da classe, ele lançará o InvalidClassException. Além disso, se não houver serialVersionUID oficialmente declarado na classe para ser serializado, o compilador o adicionará automaticamente com um valor gerado com base nos campos declarados na classe.


0

Porque em muitos casos, o ID padrão não é exclusivo. então criamos um ID para criar um conceito único.


Você pode editar sua resposta para aprofundar mais? Parece um comentário aqui. Obrigado.
Grey

0

Para adicionar à resposta do @David Schmitts, como regra geral, eu sempre usaria o 1L padrão fora da convenção. Eu só tive que voltar e alterar alguns deles algumas vezes, mas sabia disso quando fiz a alteração e atualizei o número padrão por um de cada vez.

Na minha empresa atual, eles exigem o número gerado automaticamente, então eu o uso por convenção, mas prefiro o padrão. Minha opinião é que, se não for uma convenção em que você trabalha, use o padrão, a menos que você pense que estará constantemente alterando a estrutura de suas classes serializadas por algum motivo.


0

O objetivo da versão UID da serialização é acompanhar as diferentes versões de uma classe para executar a serialização válida dos objetos.

A idéia é gerar um ID exclusivo para uma determinada versão de uma classe, que é alterada quando há novos detalhes adicionados à classe, como um novo campo, que afetaria a estrutura do objeto serializado.

Uma explicação simples:

Você está serializando dados?

Serialização é basicamente gravar dados da classe em um arquivo / fluxo / etc. A desserialização está lendo esses dados de volta para uma classe.

Você pretende entrar em produção?

Se você está apenas testando algo com dados falsos / sem importância, não se preocupe com isso (a menos que esteja testando serialização diretamente).

Esta é a primeira versão?

Nesse caso, defina serialVersionUID = 1L.

Esta é a segunda, terceira, etc. versão do prod?

Agora você precisa se preocupar com o serialVersionUID e deve analisá-lo em profundidade.

Basicamente, se você não atualizar a versão corretamente quando atualizar uma classe, precisará escrever / ler, receberá um erro ao tentar ler dados antigos.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.