Entendendo o serialVersionUID

O serialVersionUID é uma dúvida constante entre muitos desenvolvedores. Afinal, quando e para que exatamente usá-lo? Devo gerar um número aleatório bem grande, ou um número qualquer? Essas perguntas são comuns, e ao desenvolvedor experiente é necessário conhecer a fundo esse detalhe do processo de serialização do Java.

Quando um objeto é serializado no Java, essa sequência de bytes, além de conter seus atributos de instância não transientes, carrega consigo um número que indentifica a “versão” da classe que foi usada durante o processo. Esse é o chamado serialVersionUID, ou seja, o indentificador de versão de serialização de uma classe. Esse número é utilizado para saber se o objeto que estamos recuperando é de uma versão “compatível” com a versão da classe que foi utilizada quando serializamos o objeto: em outras palavras, os arquivos .class não precisam ser necessariamente os mesmos para que o processo de serialização ocorra com sucesso.

Por exemplo, considere a seguinte classe Usuario:

package br.com.caelum;

public class Usuario implements Serializable {
  private String login;
}

Essa classe possui o serialVersionUID igual a 2806421523585360625L. Esse número não é aleatório! Ele é um hash (SHA) calculado em cima dos nomes dos seus atributos, e assinaturas dos métodos em uma ordem bem definida pela especificação do processo de serialização. E como eu sei esse número? O JDK vem com a ferramenta serialver, que implementa esse mesmo hash:

serialver br.com.caelum.Usuario

Se o serialVersionUID utilizado durante a serialização não bater exatamente com o serialVersionUID da classe que está sendo usada para recuperar essa informação, uma exception é lançada: java.io.InvalidClassException.

Por exemplo, se adicionarmos um novo atributo na nossa classe Usuario:

public class Usuario implements Serializable {
  private String login;
  private String senha;
}

Agora teremos o serialVersionUID valendo 416295346730660862L. Caso você serialize um Usuario com a primeira classe aqui definida, e tentar recuperar essa informação usando essa nova versão de classe, receberemos a conhecida java.io.InvalidClassException. Esse é o comportamente que em muitos casos queremos, mas algumas vezes fazemos pequenas modificações na classe as quais percebemos que não impactarão no processo de serialização, e precisamos manter compatibilidade com a versão antiga daquela classe. Para isso, basta definirmos explicitamente qual é o nosso serialVersionUID, e no caso de querer manter compatibilidade com a classe Usuario anterior, vamos utilizar o valor de serialVersionUID que seria gerado pela JVM: 2806421523585360625L. O código ficaria:

public class Usuario implements Serializable {
  private static final long serialVersionUID = 2806421523585360625L;
  private String login;
  private String senha;
}

Às vezes recebemos um warning do Eclipse, e ele pede para que seja definido o serialVersionUID da classe em questão. Isso ocorre porque você implementa Serializable ou uma de suas mães a implementa. O Eclipse então te abre três opções: utilizar o @SurpressWarnings para você assumir o risco, usar um valor default, ou usar o valor gerado. O gerador de UIDs do Eclipse é exatamente o mesmo gerador utilizado pelo Java SE para criar os UIDs padrão! Reforçando, esse número não é um número aleatório!

Quando alguém esquece de manter o mesmo serialVersionUID para duas versões compatíveis de uma classe, podemos ter problemas em usar diferentes versões do software que são teoricamente compatíveis. Isso muitas vezes acontece em servidores de aplicação, e se seu cliente esta desatualizado em relação a versão dos jars necessários pelo servidor, podemos ter alguns InvalidClassExceptions que poderiam ser facilmente evitados se o serialVersionUID tivesse sido corretamente aplicado. Claro que algumas outras vezes as versões realmente não são compatíveis e a exception procede.

Esse grave problema pode acontecer mesmo usando classes do Java SE entre diferentes versões, como é o caso da classe java.text.AttributedCharacterIterator.Attribute (utilizada pela java.awt.Font). Do Java 1.3 para o Java 1.4 essa classe foi levemente alterada, e o serialVersionUID gerado pelo algoritmo da JVM desta classe mudou de -1514471214376796190L para -9142742483513960612L. Quando alguém serializava uma java.awt.Font em uma versão não podia desserializa-la em outra, sendo que as versões tecnicamente são compatíveis: a não definição explícita do serialVersionUID gerou um bug no Java SE. Como isto foi resolvido? Definiram o serialVersionUID como -1514471214376796190L, que é o valor que seria gerado pela JVM na versão anterior da classe.

Como então devemos proceder para escolher um serialVersionUID apropriado? É muito simples: se essa classe está nascendo neste momento, você pode se dar ao luxo de utilizar um serialVersionUID, como por exemplo:

public class Usuario implements Serializable {
  private static final long serialVersionUID = 1L;
  private String login;
}

Porém se você está definindo o serialVersionUID de uma classe já em produção, e sabe que a mudança que está fazendo é compatível com a versão anterior, você deve utilizar o serialVersionUID que seria gerado pela JVM na primeira versão, como foi o caso aqui quando adicionamos o atributo senha na classe Usuario, e também foi o caso da correção do bug da classe java.text.AttributedCharacterIterator.Attribute. Quando você fizer uma alteração onde percebe que o cliente precisará de atualização das classes envolvidas, basta definir um serialVersionUID diferente dos anteriormente utilizados.

Para completar, implementar uma interface que não define métodos (Serializable) e ser forçado a escrever um atributo sem um contrato mais burocrático é um tanto estranho em uma linguagem como o Java. Sem dúvida, se esse mecanismo todo tivesse sido inventado já com a existência de anotações, Serializable seria uma anotação e version um atributo dela, talvez obrigatório, criando algo como @Serializable(version=12345L). Boas serializações e invocações remotas!

47 Comentários

  1. Antonio Lazaro 01/04/2008 at 13:22 #

    Finalmente uma explicação clara e objetiva sobre a finalidade desse atributo!
    Valeu Paulo

  2. Zinho (Rafael) 02/04/2008 at 12:44 #

    Muito bom. Vai pro favoritos =D

  3. Joao Paulo 11/04/2008 at 11:54 #

    vlw mesmo!! aprendi mais uma..

    jopss

  4. Daniel 22/04/2008 at 18:59 #

    Poderia esclarecer o que acontece quando o serialVersionUID não é definido?

  5. Paulo Silveira 23/04/2008 at 13:16 #

    Olá Daniel. O quarto parágrafo fala disso. Se você não tem um, a API gera um para você baseado nos seus membros. Você pode ver isso pela ferramenta serialver que vem como o JDK.

  6. Rafael Hulk 04/05/2008 at 19:13 #

    Direto e claro, muito bom mesmo!

  7. Juarez 08/05/2008 at 11:29 #

    Muito bom o conteúdo. Minhas dúvidas estão sanadas.

  8. Fernando 29/05/2008 at 11:37 #

    Para ficar mais claro deveria ter escrito assim: “…e no caso de querer manter compatibilidade com a classe Usuario anterior, vamos utilizar o valor de serialVersionUID” que serialver geraria PARA ESSA VERSÃO ANTERIOR da classe.

  9. Marco Antonio 25/06/2008 at 02:35 #

    Compilando uma classe que implementa Serializable diretamente com javac, eu não recebo nenhum warning sobre isso. Isso significa que este aviso ou comportamento é uma particularidade do Eclipse e não um padrão do java?

  10. Weberson 09/06/2009 at 11:24 #

    Perfeito, havia procurado em varios lugares e apenas aqui consegui entender por completo este assunto

  11. Daniel Monti 29/06/2009 at 06:24 #

    excelente explicação, muito obrigado!

  12. rafael 07/07/2009 at 05:12 #

    Precisamos mais artigos práticos e objetivos como esse na nossa área

    Parabéns

  13. Manuel 29/09/2009 at 08:46 #

    Muito bom

    Obrigado

  14. Claucia 21/01/2010 at 14:57 #

    Perfeita a explicação, parabéns.
    Com está explicação agora sei resolver os problemas das minhas classes serializable

  15. Felipe Alexandre Rodrigues 22/01/2010 at 11:40 #

    Demorei pra encontrar uma boa resposta 🙂

  16. vitor zachi 27/03/2010 at 21:30 #

    finalmente uma boa explicação …

    valeu pelo esforço…

  17. Rodrigo Ramalho 16/05/2010 at 20:57 #

    Direto ao ponto 🙂 Ótimo artigo!

  18. Fernando 10/10/2010 at 09:44 #

    Eu acho esse lance de colocar o serialVersionUID extremamente tosco. Isso não é parte do meu domínio e não deveria estar na minha classe. Se fosse uma annotation seria “menos pior”. Aliás, tenho a impressão de que “fazer parte do domínio” não era algo muito pensado nos primórdios do java. E muitas características ruins permanecem até hoje por causa disso.

  19. Levy 02/01/2011 at 14:22 #

    Agora eu entendi, só tem profissional porreta nessa caelum.

  20. Victor 26/01/2011 at 03:07 #

    Parabéns! Há um tempo atras perguntei para um professor o porque do serialVersionUID pedido pelo eclipse e ele disse que ia pesquisar mas nunca me respondeu. Hoje bateu novamente a duvida e o pessoal da Caelum (Paulo Silveira) deu uma ótima resposta!

    Obrigado por ajudar a incrementar meu conhecimento!

  21. Renato 10/03/2011 at 17:08 #

    Muito bom o artigo, meus parabéns.

    Só esqueceu de colocar o tipo primitivo (long) de serialVersionUID no último exemplo de código, mas dá para entender perfeitamente.

  22. Paulo Silveira 11/03/2011 at 18:52 #

    Oi Renato! obrigado, editei e alterei o long faltante.

  23. Gustavo 18/05/2011 at 10:01 #

    Isso que é exemplo de alguém que sabe o que está dizendo, e provavelmente deve fazer valer seu certificado Java.

    Excelente resposta, muito clara, e explica com clareza a utilização e necessidade da especificação do número de versão de serialização.

  24. Yelken Heckman 08/11/2011 at 17:07 #

    Parabéns pela explicação, esclareceu bastante algumas dúvidas.

  25. chambas 18/11/2011 at 18:32 #

    Tenho de admitir que esta explicação está completa.

    Agora sim fiquei esclarecido… e sem dúvidas.

    Parabéns!

  26. MarceloLynx 14/08/2012 at 16:44 #

    Ótimo post, agora já sei como utilizar o serializable 🙂

  27. Vanderlei 10/09/2012 at 17:14 #

    Excelente! Obrigado Paulo.

  28. Helder Silva 20/12/2012 at 15:58 #

    muito bom mesmo, tirei minhas dúvidas!

  29. Alexandre Soares da Silva 12/03/2013 at 17:17 #

    Muito obrigado pelo post.

  30. samir 11/04/2013 at 15:21 #

    Valeu!
    Eliminou todas as minha dúvidas.

  31. Frederico 22/05/2013 at 22:17 #

    Muito fera esse Paulo Silveira 🙂

  32. Guilherme Freita 13/09/2013 at 08:24 #

    Parabéns pelo post!

  33. David Machado 09/12/2013 at 10:54 #

    Finalmente eu entendi pra que serve esta porcaria! 😀
    Normalmente eu digitava qualquer coisa no teclado numérico quando este atributo era exigido. Bom, vou continuar fazendo isso, mas agora sei o que estou fazendo, kkkkk.

  34. Saymon Dom Pires 15/08/2014 at 14:08 #

    éta menino hora mar que menino deu aula melhor que meu prof. 4 aulas e nada.

  35. Guilherme Rodrigues 18/09/2014 at 17:42 #

    ótima explicação.

  36. Frederico Genovez 23/10/2014 at 16:08 #

    Parabéns!! Ótima explicação.

  37. Renan 15/01/2015 at 19:24 #

    Como e onde você aprende essas coisas ?

  38. Yasmin 21/05/2015 at 16:24 #

    Ótima explicação!

  39. Daniel 14/07/2015 at 17:45 #

    Muito obrigado. Excelente explicação!

  40. JOSÉ PAULO JORGE DOS SANTOS 17/10/2015 at 19:56 #

    Parabens pela explicação e a atitude.

  41. Herquiloide Hele 14/09/2016 at 17:54 #

    Gostei da explicação, foi optima!

  42. João Rafael Campos da Silva 01/08/2017 at 15:50 #

    Muito boa a explicação

  43. Joao Cunha 15/08/2017 at 17:44 #

    e como devo abordar a questão de compatibilidade, quando eu serializei utilizando uma versão, mas preciso desserializar utilizando outra?

Deixe uma resposta