Caelum | Ensino e Inovação - Cursos de Java, Scrum, Ruby on Rails


Entendendo o serialVersionUID

Por Paulo Silveira em 01/04/08

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!

serialVersionUID

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 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!

  • Share/Bookmark

20 Comments »

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

    Comment by Antonio Lazaro — April 1, 2008 @ 1:22 pm

  2. [...] http://blog.caelum.com.br/2008/04/01/entendendo-o-serialversionuid/ [...]

    Pingback by serialVersionUID - Para que ele server realmente? « Paulo Jr - JDevelopment — April 2, 2008 @ 12:33 pm

  3. Muito bom. Vai pro favoritos =D

    Comment by Zinho (Rafael) — April 2, 2008 @ 12:44 pm

  4. vlw mesmo!! aprendi mais uma..

    jopss

    Comment by Joao Paulo — April 11, 2008 @ 11:54 am

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

    Comment by Daniel — April 22, 2008 @ 6:59 pm

  6. 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.

    Comment by Paulo Silveira — April 23, 2008 @ 1:16 pm

  7. Direto e claro, muito bom mesmo!

    Comment by Rafael Hulk — May 4, 2008 @ 7:13 pm

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

    Comment by Juarez — May 8, 2008 @ 11:29 am

  9. 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.

    Comment by Fernando — May 29, 2008 @ 11:37 am

  10. 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?

    Comment by Marco Antonio — June 25, 2008 @ 2:35 am

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

    Comment by Weberson — June 9, 2009 @ 11:24 am

  12. excelente explicação, muito obrigado!

    Comment by Daniel Monti — June 29, 2009 @ 6:24 am

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

    Parabéns

    Comment by rafael — July 7, 2009 @ 5:12 am

  14. [...] http://blog.caelum.com.br/2008/04/01/entendendo-o-serialversionuid/ Java, [...]

    Pingback by Entendendo o serialVersionUID @ Professor Samuel — September 4, 2009 @ 5:40 pm

  15. Muito bom

    Obrigado

    Comment by Manuel — September 29, 2009 @ 8:46 am

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

    Comment by Claucia — January 21, 2010 @ 2:57 pm

  17. Demorei pra encontrar uma boa resposta :)

    Comment by Felipe Alexandre Rodrigues — January 22, 2010 @ 11:40 am

  18. finalmente uma boa explicação …

    valeu pelo esforço…

    Comment by vitor zachi — March 27, 2010 @ 9:30 pm

  19. Direto ao ponto :) Ótimo artigo!

    Comment by Rodrigo Ramalho — May 16, 2010 @ 8:57 pm

  20. [...] serem implementados. Como "boa prática de programação" podemos definir o atributo final static serialVersionUID que é o indentificador de versão de serialização de uma classe que implementa a interface [...]

    Pingback by Serialização :: Usando ObjectOutputStream e ObjectInputStream | ChristianBorges.com — July 29, 2010 @ 5:12 pm

RSS feed for comments on this post. TrackBack URL

Leave a comment




Caelum | Ensino e Inovação
São Paulo: Rua Vergueiro, 3185, cj. 87, próximo ao Metrô Vila Mariana   |   Tel. (11) 5571-2751
Rio de Janeiro: Rua Senador Dantas, 80, cj. 307/308 - Centro   |   Tel. (21) 2220-4156 ou 2297-0033
Brasília: SCS Qd. 8 Bl. B-50, Sala 521 - Ed. Venâncio 2000   |   Tel. (61) 3039-4222