Entenda a variável serialVersionUID e sua importância na arquitetura Java

Postado por em   ●   50 comentários

Apesar de tão presente no dia a dia dos desenvolvedores Java, muitos ainda tem dúvidas sobre o atributo serialVersionUID:

  • Quando usar?
  • De onde o Eclipse tira o valor dessa propriedade?
  • Por que é um número tão grande? Não poderia ser 1?
  • Se alterar, vai dar problemas?
  • Ele pode ser removido?

Nesse artigo vou responder a essas dúvidas e, por tabela, algumas outras também.

Acho que essas respostas vão até diminuir um pouco da estranheza que essa propriedade passa. Eu, pelo menos, achava esquisito ela ali, meio que solta no código.

Esse estranhamento é porque mesmo a linguagem Java tendo tantos recursos de organização (interfaces, enums, anotações, etc.), ainda assim, ela parece perdida no meio da classe.

Aqui você vai conhecer o propósito de vida da propriedade serialVersionUID.

Você sabe o que significa serializar um objeto?

Antes de entender o que é o serialVersionUID, é importante conversarmos sobre o que é serialização.

Serializar um objeto, dentro da plataforma Java, significa converter o estado atual dele em um formato padrão e depois disponibilizá-lo em um stream de bytes que poderá ser escrito em disco ou transmitido.

Repare que a palavra serializar, dentro do Java, é um pouco mais do que “entregar em partes” – que seria a definição da mesma. É preciso que essas partes tenham uma estrutura padronizada para que seja possível a desserialização.

O legal de notar é que, entre o momento em que ele foi colocado no stream de bytes até o momento de ser desserializado, você pode encarar o “objeto” como se ele fosse um arquivo qualquer como, por exemplo, uma imagem ou um PDF, pois, ele nada mais será que um amontoado de bytes. Portanto, como mencionei, você pode persistir em disco ou transmiti-lo pela rede.

Cenários comuns para o uso do mecanismo de serialização, dentro do ecossistema Java, são as invocações de métodos remotos (RPC) e também na replicação de sessões dos servidores web ou de aplicação.

Provavelmente, você já sabe isso, mas quero lembrar que: para um objeto tornar candidato a ser serializado, ele e toda a sua hierarquia de propriedades devem implementar a interface java.io.Serializable. Lembrando que muitas classes no Java já fazem isso, inclusive, as classes wrappers juntamente com os seus primitivos – esses não implementam, pois, não são classes, mas podem ser serializados.

Entendido o porquê de se serializar um objeto, agora podemos passar para uma outra pergunta:

O que é o serialVersionUID?

Esse é um atributo utilizado para controlar explicitamente a compatibilidade entre o .class usado para serializar e o .class que será utilizado na desserialização.

O controle é necessário porque um .class pode ter sofrido alterações e ainda assim se manter compatível com sua versão anterior.

O ideal, claro, é utilizar as mesmas versões de .class, mas nem sempre isso será possível.

Aí que entra o serialVersionUID.

Ele é o recurso que usamos para dizer ao Java que um objeto serializado é compatível ou não com o .class utilizado para desserializar.

Inclusive, sempre que temos uma classe estendendo a interface java.io.Serializable esse atributo é criado, mesmo que a implementação venha por alguma superclasse.

Caso ele não esteja declarado por você (explicitamente) o Java irá declará-lo no momento da compilação.

Adiantando… Deixar isso para o Java fazer implicitamente não é uma boa ideia. Mais para frente no artigo veremos melhor sobre.

Antes, vamos observar quais as regras que o Java utiliza para gerar o valor do atributo automaticamente.

Como a geração implícita é feita

Como disse, caso você não informe o atributo serialVersionUID, o Java o fará por você na hora em que for compilar e para gerar o valor de serialVersionUID são levados em consideração alguns aspectos de estrutura da classe:

  • o nome da classe e seus modificadores;
  • o nomes das interfaces que a classe implementa;
  • os atributos e seus modificadores;
  • e algumas outras coisas como você pode conferir na documentação do processo de serialização.

Esses elementos são organizados, então é aplicado um algoritmo de hash que depois vira o valor do atributo serialVersionUID.

Qualquer alteração nesses elementos acarretará em um valor diferente.

Uma coisa legal é que dá para saber qual o valor que o Java atribuiu implicitamente.

Para isso podemos usar a ferramenta serialver que vem com o JDK. Ainda nesse artigo vamos aprender como utilizá-la. Tem até um exemplo real de como ela pode ser útil para quem desenvolve em Java.

O número gerado pelo Eclipse

Você já deve ter reparado que o Eclipse gera um aviso quando temos uma classe que implementa java.io.Serializable, não é mesmo?

Clicando em cima desse aviso, ele nos dá 3 opções:

1. adicionar um serial version ID padrão;
2. gerar um serial version ID;
3. adicionar a anotação @SuppressWarnings(“serial”);

Na verdade, são mais opções. Veja:

Opções do Eclipse quanto ao serialVersionUID

… mas, as que nos interessam são as 3 que comentei.

Na primeira opção, o Eclipse simplesmente atribui o valor 1L.

A segunda vou falar por último… Na terceira você diz ao Eclipse que assume o risco de não ter um serialVersionUID.

Quanto a segunda…

Muitos já devem ter utilizado ela e se perguntado:

De onde o Eclipse tira esse número doido?

Eu já. :)

Acontece que o número gerado pelo Eclipse não é aleatório.

Como existe um padrão sobre quais elementos da classe relevar e sobre o algoritmo utilizado para gerar o hash, então o Eclipse tira proveito disso para gerar esse número pra você, caso queira.

Ou seja, o número doido que o Eclipse gera tem uma lógica.

Utilizando a ferramenta serialver do Java

A ferramenta serialver que vem no JDK nos ajuda a descobrir o valor de serialVersionUID, independente dele ter sido gerado implicitamente ou explicitamente.

Veremos adiante que isso pode evitar algumas dores de cabeça.

Primeiro, vamos ver como utilizá-la.

Observe a classe Clube.java (clube de futebol), como abaixo:

Para descobrir o serialVersionUID dessa classe podemos utilizar:

Talvez você esteja se perguntando:

Por que eu precisaria saber qual o serialVersionUID que o Java atribuiu para minha classe?

Isso é útil sim. Tem até um caso que aconteceu dentro do próprio Java que ainda quero mostrar pra você.

Contextualizando melhor a necessidade dessa ferramenta…

Imagine o Pedro. No sistema que ele está desenvolvendo para controlar o campeonato de futebol da cidade dele, existe a classe:

Repare que ele NÃO declarou o serialVersionUID.

Tempos depois, ele precisou adicionar um atributo na classe Clube que já possuía objetos serializados e persistidos em disco.

Veja como a classe ficou:

Ao tentar desserializar os objetos, que foram serializados com a versão antiga, ele recebe a exceção java.io.InvalidClassException.

Na verdade, esse é até o comportamento correto. Afinal de contas, o Pedro alterou a classe.

Mas, pela regra de negócio, pode acontecer da alteração feita na classe não ter impacto negativo no sistema.

Por exemplo, se você simplesmente coloca um atributo novo na classe, então não precisa ignorar os objetos serializados com a versão antiga. É possível reaproveitá-los.

Como o Pedro é um garoto esperto, ele utilizou a ferramenta serialver para descobrir qual o serialVersionUID da primeira versão da classe que foi computado pela JVM implicitamente.

Foi simplesmente rodar o comando:

… apontando para o .class da versão antiga.

Descoberto o valor de serialVersionUID na versão antiga da classe, então, basta declará-lo explicitamente na nova versão para conseguir desserializar os objetos mais antigos:

Sobre o caso que aconteceu dentro do Java… Foi na evolução do Java 1.3 para o 1.4.

Dentre as alterações, existiu uma pequena na classe java.text.AttributedCharacterIterator.Attribute que acabou impactando na geração automática do atributo serialVersionUID e isso gerou um bug.

Ou seja, um objeto dessa classe serializado com a versão Java 1.3 não poderia ser desserializado na versão 1.4 e vice-versa.

A solução para a classe java.text.AttributedCharacterIterator.Attribute foi a mesma adotada pelo Pedro.

Eles descobriram o valor de serialVersionUID na versão do Java 1.3 e declararam explicitamente na versão Java 1.4.

Omitir ou informar

Dentro da própria especificação existe uma nota recomendando que os desenvolvedores declarem a propriedade explicitamente.

Essa recomendação pode evitar problemas inesperados de desserialização.

Como cheguei a mencionar, quando incluímos um atributo em uma classe, não precisamos descartar os objetos desserializados com a versão antiga.

Por essas e outras que informar o serialVersionUID explicitamente pode evitar problemas em tempo de execução.

Quando alterá-lo?

Você pode e até deve alterá-lo quando a versão antiga não faz mais sentido ou quando não dá para manter a compatibilidade.

É possível colocar um novo número escolhido arbitrariamente ou deixar o Eclipse gerar ele de novo pra você.

Serializando e desserializando

Para percebermos isso na prática, veremos um pequeno exemplo de como serializar e desserializar um objeto.

Vamos trabalhar com a seguinte classe:

Para serializar utilizaremos uma classe como abaixo:

… e outra desserializadora:

Testando com o mesmo serialVersionUID

Com o mesmo número o resultado seria:

Testando com números diferentes

Já com serialVersionUID diferentes o resultado seria a exceção:

Testando com o mesmo número e com tipo diferente

Nesse último teste vou alterar voltar o serialVersionUID para 1L e alterar o tipo do atributo nome para int.

Veja o resultado:

Atributos transientes

Quando você não desejar que um atributo seja serializado juntamente com o resto do objeto, você adiciona o modificador transient:

Repare o atributo hino na classe acima… No momento da serialização do objeto, será ignorado.

Quem usa JPA já deve conhecer esse conceito através da anotação javax.persistence.Transient. Ela tem um objetivo parecido com o modificador transient.

Customizando a serialização

Algumas vezes podemos querer algum tipo de customização no momento de serializar e/ou desserializar nossos objetos.

Podemos incluir esse comportamento extra quando implementamos esses dois métodos em nossa classe:

Veja como eles ficariam dentro da nossa classe Clube:

Nesse caso o único comportamento adicionado foi a serialização adicional do atributo hino.

Mas, writeObject e readObject são métodos comuns e você pode adicionar o comportamento que desejar dentro deles.

Você pode testar essa versão com customização da mesma forma que fizemos da primeira vez. Inclusive com as mesmas classes serializadora e desserializadora.

Conclusão: Serialização de objetos Java e a variável serialVersionUID

Apesar de não ser um recurso que precisamos programar com frequência, é importante sabermos como funciona.

Vimos para que serve o serialVersionUID, quando atribuir e alterar os valores dele e espero que tenha sido útil pra você.

Vai ser bacana ler um comentário seu aí embaixo com sua opinião, crítica ou elogio.

Tenho certeza que as informações sobre serialização de objetos são importantes para quem deseja carreira como desenvolvedor Java.

Caso esteja comprometido a seguir em frente, então quero recomendar o nosso curso de Java e Orientação A Objetos.

Por ora, é possível experimentá-lo sem compromisso! Clique aqui e saiba mais.

Um abraço e até uma próxima!

PS: você pode baixar o código do exemplo em nosso GitHub: http://github.com/algaworks/artigo-java-serialversionuid

 

É graduado em Sistemas de Informação, trabalha como instrutor na AlgaWorks e está no mercado de programação Java há mais de 8 anos, principalmente no desenvolvimento de sistemas corporativos.

50 comentários sobre “Entenda a variável serialVersionUID e sua importância na arquitetura Java

  1. Eberson -

    Muito bom, até hoje não sabia o porque era criado serialVersionUID, só sabia que tinha que ser criado. Tem muitas coisas para se aprender ainda.

    Obrigado por compartilhar!

    1. Alexandre Afonso Autor do post-

      Por nada Eberson. Bacana que gostou!

  2. Hugo Sebastião -

    Adorei, excelente.

    1. Alexandre Afonso Autor do post-

      Valeu Hugo!

  3. Douglas -

    Ótimo artigo Alexandre! Eu sabia que o serialVersionUID estava ligado à serialização, mas não conhecia os tipos de problemas que podem ocorrer em virtude de sua alteração.

    Obrigado por compartilhar!

    1. Alexandre Afonso Autor do post-

      Legal Douglas. Fico feliz que tenha sido útil. Abraço!

  4. Leandro Costa da Silva -

    Muito bom!

    1. Alexandre Afonso Autor do post-

      Obrigado Leandro!

  5. Belizario monjane -

    Conhecimentos importantes!!

  6. Alexandre Afonso Autor do post-

    Concordo Belizario! Esse tipo de questão ajuda a gente a entender várias questões dentro dos muitos frameworks do Java.

  7. Weliff Lima -

    Parabéns pelo artigo. Já sabia a utilização desse atributo mas é sempre bom relembrar… Não conhecia esse recurso do serialver.

    Abraço.

    1. Alexandre Afonso Autor do post-

      Valeu os parabéns Weliff!

  8. Francisco adoniad -

    Nunca me preocupei muito com esse numero. Muito bom saber que e importante. Obrigado. Otimo artigo.

  9. Alexandre Afonso Autor do post-

    Obrigado pelo comentário positivo Francisco!

  10. Dilnei Cunha -

    Excelente explicação Alexandre, parabéns, foi a melhor explicação que já li sobre o serialVersionUID, muito rica em conteúdo, obrigado por compartilhar.

  11. emailfromjavacode -

    [1] “Serializar um objeto Java é traduzir a estrutura de dados dele para um formato que possa ser manipulado mais facilmente.”

    Na verdade, e mais precisamente, serializar um objeto significa converter o estado atual (em memória) para uma stream de bytes que pode ser revertida em uma copia do objeto original.

    [2] Outro ponto importante é que TODOS os objetos da hierarquia de um objeto devem ser serializáveis para que o processo possa ser feito. Cada serialVersionuid deve ser único e não se repetir em todo o classpath de uma aplicação, caso mais de uma classe candidata seja encontrada a VM lança exception.

    [3] Uma discussão mais aprofundada sobre o assunto pode ser encontrada num ótimo artigo da JavaWorld : http://www.javaworld.com/article/2071731/core-java/ensure-proper-version-control-for-serialized-objects.html

    1. Alexandre Afonso Autor do post-

      emailfromjavacode, valeu por acrescentar! Inclusive, sua definição de serializar me ajudou a melhorar a minha. (Obrigado!)

      Abraço!

  12. Guilherme Alves -

    Bom saber, eu achava que esse número ajudava na operação de equals(), mas pelo visto é útil somente para serialização e desserialização.

  13. Diógenes Dias -

    Muito bom, obrigado Alexandre pelo post, bem rico o conteúdo.
    Já sabia mas ler ou re-ler nunca é demais. Parabéns

  14. FLAVIO ALVES -

    Muito bom post! Ajudou bastante.

  15. Françoes -

    Texto simples e claro. Muito bom.

  16. Marcos André -

    Alexandre, parabéns pelo artigo!
    Com esses detalhes da explicação o artigo repassou muito conteúdo importante.

  17. Wagner -

    Tenho uma dúvida: O SerialVersionUID sua função basicamente é gerar um número aleatório que serve como identificador, assim quando ele for persiste/gravar um arquivo no disco ou enviar na rede o arquivo fica identificado com id que foi gerado. Minha dúvida é: Esse id irá servir no caso de fazer uma alteracao no arquivo já gravado e quando for salvar essas alteracoes, comparar se os dois arquivos sao os mesmos para assim poder substituir?

    1. Alexandre Afonso Autor do post-

      Beleza Wagner? Ele serve para o Java validar se o arquivo que sera desserializado ainda tem estrutura compatível com a classe que está no classpath atual.

      Até!

  18. Wanderson Joner -

    Show agora entendi!

  19. Bruna Rocha -

    Excelente. Quando o professor explicou, pareceu um pouco confuso, mas agora consegui entender perfeitamente!
    Parabéns.

  20. Claudiney Marques -

    Muito bom artigo Alexandre. Parabéns!

  21. AVicente -

    Ola Alexandre!

    Parabens pelo excelente post, exemplo simples e pratico.

  22. Alisson Costa -

    Belo Artigo!

  23. Kleberson Santos -

    Eu já imaginava mesmo sem conhecer a fundo que se tratava de um identificador interno. Ótima explanação!

  24. Ramon -

    Excelente artigo.

  25. Willian Ribeiro -

    Muito bem explicado, valeu por mostrar a importância do SerialVersionUID e as possíveis causas de exceções na alteração da classe.

  26. Fabio -

    Achei interessante o tema. Apesar de sempre usar esse valor gerado, nunca tive a curiosidade de entender o que acontece ao mudar valor, ou no que implicaria colocar o valor default gerado pelo java (1L). Interessante saber. vlw

  27. Janildo -

    Muuuito bom post valeu mesmo, continuem nos surpreendendo com conhecimentos pois nunca é demais!

  28. Marcelo -

    ótimo artigo Alexandre, obrigado por compartilhar esse conhecimento :-)

  29. Valmir -

    Muito bom!

  30. Thiago -

    Parabéns pelo artigo!

  31. Bruno -

    Excelente explicação Alexandre e mais uma vez muito obrigado por compartilhar seu conhecimento.

  32. Divino -

    Muito bom artigo, parabéns.

  33. Francisco Lopes -

    Irmão, muito bom o artigo!
    Parabéns pelo esclarecimento.
    É realmente importante ter a noção do que significa isso.

  34. Yuri -

    Cara, você é fera. Parabéns pelo artigo. Um dia quem sabe chego no nível de conhecimento de vocês da Algaworks.

  35. Igor -

    Muito bom!

  36. Gerson reis -

    Parabéns pelo post, gostei bastante. Esse é um assunto que quase não vamos atras para aprofundar o conhecimento.

  37. Jhonatan -

    Muito bem! Gostei do artigo! Mas vou ter que re-ler com pra entender melhor!

  38. Ivalmir -

    Muito bom, obrigado por compartilhar conhecimento,
    ler este artigo é gratificante.

  39. Manoel Pereira de Souza -

    Parabéns, muito bom, obrigado por compartilhar.

  40. Wagner Araujo -

    Muito bom o post. Só as informações setadas no objeto não são verdadeiras (São Paulo melhor time do mundo) hahahaha….

  41. Cicero Jeferson -

    Parabéns Alexandre. Entendi um pouco sobre o SerialVersionUID ao fazer um trabalho sobre RMI, onde o servidor tinha a lógica de um labirinto e ao ser acessado pelo o cliente o mesmo enviava o labirinto para ser resolvido pelo mesmo, nesse caso só consegui fazer a comunicação funcionar após serializar os objetos.

  42. olavo -

    Muito bom!

  43. Messias -

    Muito bom o artigo.

Deixe um comentário