O que é um serialVersionUID e por que devo usá-lo?

3000

O Eclipse emite avisos quando serialVersionUIDfalta um.

A classe serializável Foo não declara um campo estático final serialVersionUID do tipo long

O que é serialVersionUIDe por que é importante? Por favor, mostre um exemplo em que a falta serialVersionUIDcausará um problema.

ashokgelal
fonte
1
Encontre uma boa prática sobre serialversionUID; dzone.com/articles/what-is-serialversionuid
ceyun (

Respostas:

2290

Os documentos para java.io.Serializableprovavelmente são uma explicação tão boa quanto você obterá:

O tempo de execução de serialização associa a cada classe serializável um número de versão, chamado a serialVersionUID, que é usado durante a desserialização para verificar se o remetente e o destinatário de um objeto serializado carregaram classes para esse objeto que são compatíveis com a serialização. Se o receptor carregou uma classe para o objeto que é diferente serialVersionUIDda classe do remetente correspondente, a desserialização resultará em um InvalidClassException. Uma classe serializável pode declarar-se serialVersionUIDexplicitamente declarando um campo nomeado serialVersionUIDque deve ser estático, final e do tipo long:

ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;

Se uma classe serializável não declarar explicitamente a serialVersionUID, o tempo de execução da serialização calculará um serialVersionUIDvalor padrão para essa classe com base em vários aspectos da classe, conforme descrito na Especificação de serialização de objetos Java (TM). No entanto, é altamente recomendável que todas as classes serializáveis ​​declarem explicitamente serialVersionUIDvalores, pois o serialVersionUIDcálculo padrão é altamente sensível aos detalhes da classe que podem variar dependendo das implementações do compilador e, portanto, podem resultar em inesperado InvalidClassExceptionsdurante a desserialização. Portanto, para garantir um serialVersionUIDvalor consistente em diferentes implementações do compilador java, uma classe serializável deve declarar um serialVersionUIDvalor explícito . Também é altamente recomendável que explícitosserialVersionUIDdeclarações usam o modificador privado sempre que possível, pois essas declarações se aplicam apenas aos serialVersionUIDcampos de classe imediatamente declarantes não são úteis como membros herdados.

Jon Skeet
fonte
326
Então, o que você está dizendo basicamente é que, se um usuário não entendeu todo o material acima, ele não deve se preocupar em serializar? Eu acredito que você respondeu o "como?" ao invés de explicar o "por quê?". Eu, por exemplo, não entendo por que me preocupo com o SerializableVersionUID.
Ziggy
366
O porquê está no segundo parágrafo: se você não especificar explicitamente serialVersionUID, um valor será gerado automaticamente - mas isso é frágil porque depende da implementação do compilador.
Jon Skeet
14
E por que o Eclipse diz que eu preciso de "private static final long serialVersionUID = 1L;" quando eu estendo a classe Exception?
31414 John JohnMerlino
21
@ JohnMerlino: Bem, eu não esperaria dizer que você precisa de um - mas pode estar sugerindo um para ajudá-lo a serializar as exceções corretamente. Se você não deseja serializá-los, realmente não precisa da constante.
Jon Skeet
16
@JohnMerlino, para responder à parte por que você pergunta: Exception implementa Serializable and eclipse adverte que você não definiu um serialVersionUID, o que seria uma boa idéia (se você não quiser serializar a classe) para evitar os problemas que JonSkeet postou esboços.
zpon
475

Se você está serializando apenas porque precisa serializar por causa da implementação (quem se importa se você serializar um HTTPSession, por exemplo ... se ele estiver armazenado ou não, você provavelmente não se importa com de-serializingum objeto de formulário), poderá Ignore isso.

Se você estiver realmente usando serialização, isso só importa se você planeja armazenar e recuperar objetos usando a serialização diretamente. O serialVersionUIDrepresenta a versão da sua classe e você deve incrementá-la se a versão atual da sua classe não for compatível com a versão anterior.

Na maioria das vezes, você provavelmente não usará serialização diretamente. Se for esse o caso, gere um padrão SerialVersionUIDclicando na opção de correção rápida e não se preocupe.

MetroidFan2002
fonte
78
Eu diria que, se você não estiver usando serialização para armazenamento permanente, use @SuppressWarnings em vez de adicionar um valor. Ela atrapalha menos a classe e preserva a capacidade do mecanismo serialVersionUID para protegê-lo de alterações incompatíveis.
Tom Anderson
25
Não vejo como a adição de uma linha (anotação @SuppressWarnings) em oposição a outra linha (ID serializável) "desorganiza menos a classe". E se você não está usando serialização para armazenamento permanente, por que você não usa apenas "1"? Você não se importaria com o ID gerado automaticamente nesse caso.
precisa saber é o seguinte
71
@ MetroidFan2002: Eu acho que o ponto de @ TomAnderson de serialVersionUIDprotegê-lo contra alterações incompatíveis é válido. Usar os @SuppressWarningsdocumentos com a intenção melhor se você não quiser usar a classe para armazenamento permanente.
AbdullahC
11
"Você deve incrementá-lo se a versão atual da sua classe não for compatível com a versão anterior:" Você deve primeiro explorar o amplo suporte à serialização de objetos da serialização, (a) para garantir que a classe agora seja realmente incompatível com serialização, qual pela especificação é bastante difícil de alcançar; (b) para tentar um esquema como métodos personalizados de read / writeObject (), métodos readResolve / writeReplace (), declarações serializableFields, etc., para garantir que o fluxo permaneça compatível. Mudar o real serialVersionUIDé um último recurso, um conselho de desespero.
Marquês de Lorne
4
O incremento do @EJP de serialVersionUID aparece em cena quando o autor inicial da classe foi introduzido explicitamente. esta é a melhor resposta que eu vi na serialização.
overexchange
307

Não posso deixar passar essa oportunidade de publicar o livro de Josh Bloch, Effective Java (2nd Edition). O capítulo 11 é um recurso indispensável na serialização Java.

Por Josh, o UID gerado automaticamente é gerado com base em um nome de classe, interfaces implementadas e todos os membros públicos e protegidos. Alterar qualquer um desses itens de qualquer maneira alterará o serialVersionUID. Portanto, você não precisa mexer com eles apenas se tiver certeza de que nunca mais de uma versão da classe será serializada (entre processos ou recuperada do armazenamento posteriormente).

Se você ignorá-los por enquanto e descobrir mais tarde que precisa alterar a classe de alguma forma, mas manter a compatibilidade com a versão antiga da classe, poderá usar o serialver da ferramenta JDK para gerar o serialVersionUIDna antiga classe, e definir explicitamente que na nova classe. (Dependendo das alterações, você também pode implementar a serialização personalizada, adicionando métodos writeObjecte readObject- consulte Serializablejavadoc ou o capítulo 11. acima mencionado).

Scott Bale
fonte
33
Então, alguém pode se preocupar com SerializableVersionUID se estiver preocupado com a compatibilidade com versões antigas de uma classe?
Ziggy
10
Sim, caso a versão mais recente altere qualquer membro público para protegido, o SerializableVersionUID padrão será diferente e gerará um InvalidClassExceptions.
21412 Chandler Shivdasani
3
nome da classe, interfaces implementadas, todos os métodos públicos e protegidos, TODAS as variáveis ​​de instância.
Achow
31
Vale ressaltar que Joshua Bloch aconselha que, para todas as classes serializáveis, vale a pena especificar a versão serial uid. Citação do capítulo 11: Independentemente de qual formato serializado você escolher, declare um UID explícito da versão serial em cada classe serializável que você escrever. Isso elimina o UID da versão serial como uma fonte potencial de incompatibilidade (Item 74). Há também um pequeno benefício de desempenho. Se nenhuma UID da versão serial for fornecida, será necessário um cálculo caro para gerar uma em tempo de execução.
Ashutosh Jindal
136

Você pode dizer ao Eclipse para ignorar estes avisos serialVersionUID:

Janela> Preferências> Java> Compilador> Erros / Avisos> Problemas Potenciais de Programação

Caso você não saiba, existem muitos outros avisos que você pode ativar nesta seção (ou até mesmo alguns relatados como erros), muitos são muito úteis:

  • Problemas potenciais de programação: Possível atribuição booleana acidental
  • Problemas potenciais de programação: acesso nulo ao ponteiro
  • Código desnecessário: a variável local nunca é lida
  • Código desnecessário: verificação nula redundante
  • Código desnecessário: conversão desnecessária ou "instanceof"

e muitos mais.

matt b
fonte
21
voto positivo, mas apenas porque o pôster original não parece estar serializando nada. Se o pôster dissesse "estou serializando essa coisa e ...", você seria votado para baixo: P
John Gardner
3
Verdade - eu estava assumindo a pergunta simplesmente não queria ser avisado
matt b
14
@ Gardner -> concordou! Mas o questionador também quer saber por que ele pode não querer ser avisado.
Ziggy
8
O questionador obviamente se preocupa com o porquê de haver um UID. Então, basta dizer a ele para ignorar o aviso deve ser votado abaixo.
cinqS
111

serialVersionUIDfacilita o controle de versão de dados serializados. Seu valor é armazenado com os dados ao serializar. Ao desserializar, a mesma versão é verificada para ver como os dados serializados correspondem ao código atual.

Se você deseja versionar seus dados, normalmente começa com um serialVersionUID de 0 e aumenta com todas as alterações estruturais em sua classe que alteram os dados serializados (adicionando ou removendo campos não transitórios).

O mecanismo interno de desserialização ( in.defaultReadObject()) se recusará a desserializar das versões antigas dos dados. Mas se você quiser, pode definir sua própria função readObject (), que pode ler dados antigos. Esse código personalizado pode então verificar oserialVersionUID para saber em qual versão os dados estão e decidir como desserializá-los. Essa técnica de versão é útil se você armazenar dados serializados que sobrevivem a várias versões do seu código.

Mas armazenar dados serializados por um período de tempo tão longo não é muito comum. É muito mais comum usar o mecanismo de serialização para gravar temporariamente dados em, por exemplo, um cache ou enviá-los pela rede para outro programa com a mesma versão das partes relevantes da base de código.

Nesse caso, você não está interessado em manter a compatibilidade com versões anteriores. Você está preocupado apenas em garantir que as bases de código que estão se comunicando realmente tenham as mesmas versões de classes relevantes. Para facilitar essa verificação, você deve manter oserialVersionUID procedimento anterior e não se esqueça de atualizá-la ao fazer alterações em suas classes.

Se você esquecer de atualizar o campo, poderá acabar com duas versões diferentes de uma classe com estrutura diferente, mas com a mesma serialVersionUID. Se isso acontecer, o mecanismo padrão ( in.defaultReadObject()) não detectará nenhuma diferença e tentará desserializar dados incompatíveis. Agora você pode acabar com um erro de tempo de execução enigmático ou falha silenciosa (campos nulos). Esses tipos de erros podem ser difíceis de encontrar.

Portanto, para ajudar esse caso de uso, a plataforma Java oferece a opção de não definir o serialVersionUIDmanual manualmente. Em vez disso, um hash da estrutura da classe será gerado em tempo de compilação e usado como id. Esse mecanismo garantirá que você nunca tenha estruturas de classe diferentes com o mesmo ID e, portanto, não obterá essas falhas de serialização de tempo de execução difíceis de rastrear mencionadas acima.

Mas há uma desvantagem na estratégia de identificação gerada automaticamente. Ou seja, os IDs gerados para a mesma classe podem diferir entre os compiladores (conforme mencionado por Jon Skeet acima). Portanto, se você comunicar dados serializados entre códigos compilados com diferentes compiladores, é recomendável manter os IDs manualmente de qualquer maneira.

E se você é compatível com os seus dados, como no primeiro caso de uso mencionado, provavelmente também deseja manter o ID por conta própria. Isso para obter identificações legíveis e ter maior controle sobre quando e como elas mudam.

Alexander Torstling
fonte
4
Adicionar ou remover campos não transitórios não torna a classe serializada incompatível. Portanto, não há razão para "aumentar" essas mudanças.
Marquês de Lorne
4
@EJP: Hein? A adição de dados definitivamente altera os dados de serialização no meu mundo.
Alexander Torstling
3
@AlexanderTorstling Leia o que eu escrevi. Eu não disse que não 'altera os dados de serialização'. Eu disse que 'não torna a classe serializada incompatível'. Não é a mesma coisa. Você precisa ler o capítulo Versionamento da Especificação de serialização de objetos.
Marquês de Lorne
3
@EJP: Sei que adicionar um campo não transitório não significa necessariamente que você torne a serialização da classe incompatível, mas é uma mudança estrutural que altera os dados serializados e você geralmente deseja aumentar a versão ao fazê-lo, a menos que você lidar com compatibilidade com versões anteriores, que também expliquei mais adiante neste post. Qual é o seu ponto exatamente?
Alexander Torstling
4
Meu argumento continua sendo exatamente o que eu disse. Adicionar ou remover campos não transitórios não torna a classe Serialization-incompatible. Portanto, você não precisa aumentar o serialVersionUID toda vez que o fizer.
Marquês de Lorne
72

O que é um serialVersionUID e por que devo usá-lo?

SerialVersionUIDé um identificador exclusivo para cada classe, JVMusa-o para comparar as versões da classe, garantindo que a mesma classe foi usada durante a serialização seja carregada durante a desserialização.

A especificação de um fornece mais controle, embora a JVM gere um, se você não especificar. O valor gerado pode diferir entre diferentes compiladores. Além disso, às vezes você deseja, por algum motivo, proibir a desserialização de objetos serializados antigos [ backward incompatibility] e, nesse caso, você apenas precisa alterar o serialVersionUID.

Os javadocs paraSerializable dizer :

o cálculo serialVersionUID padrão é altamente sensível aos detalhes da classe que podem variar dependendo das implementações do compilador e, portanto, podem resultar em InvalidClassExceptions inesperados durante a desserialização.

Portanto, você deve declarar serialVersionUID, pois isso nos dá mais controle .

Este artigo tem alguns bons pontos sobre o tópico.

Thalaivar
fonte
3
@Vinothbabu, mas serialVersionUID é estático, portanto, variáveis ​​estáticas não podem ser serializadas. então como é que JVM irá verificar a versão, sem saber qual é a versão do objeto desserialização
Kranthi Sama
3
A única coisa que não é mencionada nesta resposta é que você pode causar consequências indesejadas, incluindo cegamente, serialVersionUIDsem saber o porquê. O comentário de Tom Anderson sobre a resposta de MetroidFan2002 trata disso: "Eu diria que, se você não estiver usando serialização para armazenamento permanente, use @SuppressWarnings em vez de adicionar um valor. Isso atrapalha menos a classe e preserva a capacidade do mecanismo serialVersionUID para protegê-lo de alterações incompatíveis. "
Kirby
7
serialVersionUIDnão é um 'identificador exclusivo para cada classe'. O nome completo da classe é esse. É um indicador de versão .
Marquês de Lorne
58

A pergunta original pediu 'por que é importante' e 'exemplo', onde isso Serial Version IDseria útil. Bem, eu encontrei um.

Digamos que você crie uma Carclasse, instancie-a e grave-a em um fluxo de objetos. O objeto de carro nivelado fica no sistema de arquivos por algum tempo. Enquanto isso, se a Carclasse for modificada adicionando um novo campo. Mais tarde, quando você tenta ler (ou seja, desserializar) o Carobjeto achatado , obtém o java.io.InvalidClassException- porque todas as classes serializáveis ​​recebem automaticamente um identificador exclusivo. Essa exceção é lançada quando o identificador da classe não é igual ao identificador do objeto achatado. Se você realmente pensa sobre isso, a exceção é lançada devido à adição do novo campo. Você pode evitar que essa exceção seja lançada controlando você mesmo a versão declarando um serialVersionUID explícito. Há também um pequeno benefício de desempenho ao declarar explicitamente suaserialVersionUID(porque não precisa ser calculado). Portanto, é uma boa prática adicionar seu próprio serialVersionUID às classes Serializable assim que você as criar, conforme mostrado abaixo:

public class Car {
    static final long serialVersionUID = 1L; //assign a long value
}
Rupesh
fonte
E deve-se atribuir um número longo aleatório, não 1L, a cada UID.
abbas
4
@abbas 'Deve-se fazer isso por quê? Por favor, explique que diferença isso faz.
Marquês de Lorne
Como o nome diz, representa a versão da classe que foi usada para serializar o objeto. Se você atribuir o mesmo número todas as vezes, não conseguirá encontrar a classe certa para desserializá-la. Portanto, sempre que você fizer uma alteração na classe, é melhor alterar a versão também.
abbas
2
@abbas, essa intenção não entra em conflito com o aumento de números naturais 1e assim por diante.
Vadzim
2
@ BillK, eu pensei que a verificação de serialização está vinculada ao par de nome de classe e serialVersionUID. Portanto, esquemas de numeração diferentes de classes e bibliotecas diferentes não podem interferir de forma alguma. Ou você implicou bibliotecas geradoras de código?
Vadzim 26/01
44

Primeiro, preciso explicar o que é serialização.

A serialização permite converter um objeto em um fluxo, para enviar esse objeto pela rede OU Salvar em arquivo OU salvar em DB para uso de cartas.

Existem algumas regras para serialização .

  • Um objeto é serializável apenas se sua classe ou superclasse implementa a interface Serializable

  • Um objeto é serializável (ele mesmo implementa a interface Serializable), mesmo que sua superclasse não seja. No entanto, a primeira superclasse na hierarquia da classe serializável, que não implementa a interface serializável, DEVE ter um construtor sem argumento. Se isso for violado, readObject () produzirá uma java.io.InvalidClassException em tempo de execução

  • Todos os tipos primitivos são serializáveis.

  • Os campos transitórios (com modificador transitório) NÃO são serializados (ou seja, não são salvos ou restaurados). Uma classe que implementa Serializable deve marcar campos transitórios de classes que não suportam serialização (por exemplo, um fluxo de arquivos).

  • Os campos estáticos (com modificador estático) não são serializados.

Quando Objecté serializado, o Java Runtime associa o número da versão serial, aka serialVersionID.

Onde precisamos de serialVersionID: Durante a desserialização para verificar se o remetente e o destinatário são compatíveis com a serialização. Se o receptor carregou a classe com uma diferenteserialVersionID, a desserialização terminará comInvalidClassCastException.
Uma classe serializável pode declarar sua própriaserialVersionUIDexplicitamente declarando um campo nomeadoserialVersionUIDque deve ser estático, final e de tipo longo.

Vamos tentar isso com um exemplo.

import java.io.Serializable;    
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
private String empname;
private byte empage;

public String getEmpName() {
    return name;
}
public void setEmpName(String empname) {
    this.empname = empname;
}
public byte getEmpAge() {
    return empage;
}
public void setEmpAge(byte empage) {
    this.empage = empage;
}

public String whoIsThis() {
    StringBuffer employee = new StringBuffer();
    employee.append(getEmpName()).append(" is ).append(getEmpAge()).append("
years old  "));
    return employee.toString();
}
}

Criar objeto de serialização

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class Writer {
public static void main(String[] args) throws IOException {
    Employee employee = new Employee();
    employee.setEmpName("Jagdish");
    employee.setEmpAge((byte) 30);

    FileOutputStream fout = new 
FileOutputStream("/users/Jagdish.vala/employee.obj");
    ObjectOutputStream oos = new ObjectOutputStream(fout);
    oos.writeObject(employee);
    oos.close();
    System.out.println("Process complete");
}
}

Desserializar o objeto

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class Reader {
public static void main(String[] args) throws ClassNotFoundException, 
IOException {
    Employee employee = new Employee();
    FileInputStream fin = new 
    FileInputStream("/users/Jagdish.vala/employee.obj");
    ObjectInputStream ois = new ObjectInputStream(fin);
    employee = (Employee) ois.readObject();
    ois.close();
    System.out.println(employee.whoIsThis());
 }
}    

NOTA: Agora altere o serialVersionUID da classe Employee e salve:

private static final long serialVersionUID = 4L;

E execute a classe Reader. Não para executar a classe Writer e você receberá a exceção.

Exception in thread "main" java.io.InvalidClassException: 
com.jagdish.vala.java.serialVersion.Employee; local class incompatible: 
stream classdesc serialVersionUID = 1, local class serialVersionUID = 4
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:616)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1623)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1518)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1774)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1351)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:371)
at com.krishantha.sample.java.serialVersion.Reader.main(Reader.java:14)
JegsVala
fonte
Corrija-me se eu estiver errado - a classe local é a que você está atualmente usando / no caminho de classe e o stream é o usado por outra parte (por exemplo, um servidor que está lhe retornando uma resposta e serializou o resposta). Você pode encontrar essa situação ao se comunicar com um servidor que atualizou suas bibliotecas de terceiros, mas você (o cliente) não fez isso.
Victor
37

Se você nunca precisará serializar seus objetos para bytes de matriz e enviá-los / armazená-los, não precisará se preocupar com isso. Se o fizer, deverá considerar seu serialVersionUID, pois o desserializador do objeto o corresponderá à versão do objeto que o seu carregador de classes possui. Leia mais sobre isso na Java Language Specification.

eishay
fonte
10
Se você não deseja serializar os objetos, por que eles são serializáveis?
Erickson
7
@erickson - a classe pai pode ser serializável, por exemplo, ArrayList, mas você deseja que seu próprio objeto (por exemplo, uma lista de matrizes modificada) o use como base, mas nunca serializará a coleção que você criar.
MetroidFan2002 30/03/09
5
Não é mencionado em nenhum lugar da Java Language Specification. É mencionado na especificação de versão do objeto.
Marquês de Lorne
4
Aqui está o link para o Java 8 Object Versioning Specification .
Basil Bourque
34

Se você receber esse aviso em uma classe, nunca pensa em serializar e que não se declarou implements Serializable , geralmente é porque herdou de uma superclasse, que implementa Serializable. Frequentemente, seria melhor delegar a esse objeto em vez de usar herança.

Então, ao invés de

public class MyExample extends ArrayList<String> {

    public MyExample() {
        super();
    }
    ...
}

Faz

public class MyExample {
    private List<String> myList;

    public MyExample() {
         this.myList = new ArrayList<String>();
    }
    ...
}

e nos métodos relevantes chamam em myList.foo()vez de this.foo()(ou super.foo()). (Isso não se aplica a todos os casos, mas ainda com bastante frequência.)

Muitas vezes vejo pessoas estendendo o JFrame ou algo assim, quando realmente precisam apenas delegar isso. (Isso também ajuda na conclusão automática em um IDE, já que o JFrame possui centenas de métodos, dos quais você não precisa quando deseja chamar seus personalizados em sua classe.)

Um caso em que o aviso (ou o serialVersionUID) é inevitável é quando você estende de AbstractAction, normalmente em uma classe anônima, adicionando apenas o método actionPerformed. Eu acho que não deveria haver um aviso nesse caso (já que você normalmente não pode serializar e desserializar essas classes anônimas de maneira confiável em versões diferentes da sua classe), mas não tenho certeza de como o compilador poderia reconhecer isso.

Paŭlo Ebermann
fonte
4
Acho que você está certo de que a composição em detrimento da herança faz mais sentido, principalmente quando você está discutindo classes como ArrayList. No entanto, muitas estruturas exigem que as pessoas se estendam a partir de superclasses abstratas que são serializáveis ​​(como a classe ActionForm do Struts 1.2 ou a ExtensionFunctionDefinition do Saxon), nesse caso, essa solução é inviável. Eu acho que você está certo, seria bom se o aviso foram ignorados em certos casos (como se estivesse estendendo-se desde um resumo classe serializável)
piepera
2
Certamente, se você adicionar uma classe como membro, em vez de herdá-la, teria que escrever um método de wrapper para TODOS os métodos da classe de membro que desejava usar, o que tornaria inviável em um grande número de situações. a menos que o java tenha uma função semelhante à do perl __AUTOLOAD, que eu não conheço.
M_M
4
@M_M: Quando você delega muitos métodos ao seu objeto empacotado, é claro que não seria apropriado usar a delegação. Mas suponho que este caso seja um sinal de um erro de design - os usuários da sua classe (por exemplo, "MainGui") não precisam chamar muitos métodos do objeto agrupado (por exemplo, JFrame).
Paŭlo Ebermann 20/08/2012
1
O que eu não gosto na delegação é a necessidade de manter uma referência ao delegado. E toda referência significa mais memória. Corrija-me se eu estiver errado. Se eu precisar de um CustomizedArrayList de 100 objetos, isso não importará muito, mas se eu precisar de centenas de CustomizdeArrayList de alguns objetos, o uso da memória aumentará significativamente.
WVrock #
2
Throwable é serializável, e somente Throwable é jogável, portanto, não é possível definir uma exceção que não seja serializável. Delegação não é possível.
Phil
22

Para entender o significado do campo serialVersionUID, é preciso entender como a serialização / desserialização funciona.

Quando um objeto de classe Serializable é serializado, o Java Runtime associa um número de versão serial (chamado como serialVersionUID) a esse objeto serializado. No momento em que você desserializa esse objeto serializado, o Java Runtime corresponde ao serialVersionUID do objeto serializado com o serialVersionUID da classe. Se os dois forem iguais, somente ele prosseguirá com o processo adicional de desserialização, lançando InvalidClassException.

Portanto, concluímos que para tornar o processo de serialização / desserialização bem-sucedido, o serialVersionUID do objeto serializado deve ser equivalente ao serialVersionUID da classe. Caso o programador especifique explicitamente o valor serialVersionUID no programa, o mesmo valor será associado ao objeto serializado e à classe, independentemente da plataforma de serialização e desserialização (por exemplo, a serialização pode ser feita em plataformas como janelas usando sun ou A MS JVM e a desserialização podem estar em uma plataforma diferente do Linux usando o Zing JVM).

Mas, se o serialVersionUID não for especificado pelo programador, enquanto estiver executando Serialization \ DeSerialization de qualquer objeto, o Java Runtime utilizará seu próprio algoritmo para calculá-lo. Esse algoritmo de cálculo serialVersionUID varia de um JRE para outro. Também é possível que o ambiente em que o objeto seja serializado esteja usando um JRE (ex: SUN JVM) e o ambiente em que a desserialização ocorre esteja usando o Linux Jvm (zing). Nesses casos, serialVersionUID associado ao objeto serializado será diferente do serialVersionUID da classe calculada no ambiente de desserialização. Por sua vez, a desserialização não será bem-sucedida. Portanto, para evitar tais situações / problemas, o programador deve sempre especificar serialVersionUID da classe Serializable.

Nitesh Soni
fonte
2
O algoritmo não varia, mas é ligeiramente subespecificado.
Marquês de Lorne
... o algoritmo não varia, mas é ligeiramente sub-especificado ... ou seja, qualquer JVM pode variar ..... @ user207421
AnthonyJClink
18

Não se preocupe, o cálculo padrão é realmente bom e suficiente para 99,9999% dos casos. E se você tiver problemas, poderá - como já foi dito - introduzir UIDs conforme a necessidade surgir (o que é altamente improvável)

Grand Johnson
fonte
2
Lixo. É adequado no caso em que a classe não mudou. Você tem zero evidência para apoiar '99 .9999% '.
Marquês de Lorne,
18

Como um exemplo em que o serialVersionUID ausente pode causar um problema:

Estou trabalhando neste aplicativo Java EE que é composto por um módulo da Web que usa um EJBmódulo. O módulo da Web chama o EJBmódulo remotamente e passa um POJOque implementaSerializable como argumento.

este POJO's classe foi empacotada dentro do jar EJB e dentro de seu próprio jar no WEB-INF / lib do módulo da web. Na verdade, eles são da mesma classe, mas quando empacoto o módulo EJB, descompacto o frasco deste POJO para empacotá-lo junto com o módulo EJB.

A chamada para o EJBestava falhando com a exceção abaixo porque eu não havia declarado serialVersionUID:

Caused by: java.io.IOException: Mismatched serialization UIDs : Source
 (Rep.
 IDRMI:com.hordine.pedra.softbudget.domain.Budget:5CF7CE11E6810A36:04A3FEBED5DA4588)
 = 04A3FEBED5DA4588 whereas Target (Rep. ID RMI:com.hordine.pedra.softbudget.domain.Budget:7AF5ED7A7CFDFF31:6227F23FA74A9A52)
 = 6227F23FA74A9A52
Henrique Ordine
fonte
16

Eu geralmente uso serialVersionUIDem um contexto: quando eu sei, ele sairá do contexto da Java VM.

Eu saberia disso quando usar ObjectInputStreame ObjectOutputStreampara o meu aplicativo ou se conheço uma biblioteca / estrutura que o utilizarei. O serialVersionID garante que diferentes Java VMs de versões diferentes ou fornecedores interoperem corretamente ou se forem armazenados e recuperados fora da VM, por exemploHttpSession os dados da sessão poderão permanecer mesmo durante uma reinicialização e atualização do servidor de aplicativos.

Para todos os outros casos, eu uso

@SuppressWarnings("serial")

já que na maioria das vezes o padrão serialVersionUIDé suficiente. Isto inclui Exception, HttpServlet.

Archimedes Trajano
fonte
Ele não inclui HttpServlet em contêineres onde eles podem ser trocados, ou Exceção no RMI, por exemplo.
Marquês de Lorne
14

Os dados do campo representam algumas informações armazenadas na classe. A classe implementa a Serializableinterface, então o eclipse se ofereceu automaticamente para declarar o serialVersionUIDcampo. Vamos começar com o valor 1 definido lá.

Se você não deseja que esse aviso aconteça, use o seguinte:

@SuppressWarnings("serial")
Mukti
fonte
11

Seria bom se o CheckStyle pudesse verificar se o serialVersionUID em uma classe que implementa o Serializable possui um bom valor, ou seja, se ele corresponde ao que o gerador de ID da versão serial produziria. Se você tem um projeto com muitos DTOs serializáveis, por exemplo, lembrar-se de excluir o serialVersionUID existente e regenerar é uma dor, e atualmente a única maneira (que eu conheço) de verificar isso é a regeneração de cada classe e a comparação com o antigo. Isso é muito, muito doloroso.


fonte
8
Se você definir o serialVersionUID sempre para o mesmo valor que o gerador produziria, você realmente não precisará dele. Afinal, sua razão de ser é permanecer o mesmo após as alterações, quando a classe ainda é compatível.
Paŭlo Ebermann
4
O motivo é que diferentes compiladores criam o mesmo valor para a mesma classe. Conforme explicado nos javadocs (também respondido acima), a versão gerada é quebradiça e pode variar mesmo quando a classe é desserializável corretamente. Contanto que você execute esse teste no mesmo compilador a cada vez, ele deve ser seguro. Deus o ajude se você atualizar o jdk e uma nova regra sair, mesmo que o código não tenha sido alterado.
Andrew Backer
2
Não é necessário combinar o serialverque produziria. -1
Marquês de Lorne
Em geral, não é necessário. O caso do @ AndrewBacker exigiria dois compiladores diferentes no mesmo arquivo .java, com as duas versões dos arquivos .class se comunicando - na maioria das vezes você apenas cria a classe e a distribui. Se for esse o caso, não ter um SUID funcionaria bem.
Bill K
1
As pessoas que realmente usam a serialização para fins de armazenamento / recuperação geralmente definem o valor serialVersionUID1. Se uma versão mais recente da classe for incompatível, mas ainda precisar ser capaz de lidar com os dados antigos, você aumenta o número da versão e adiciona código especial ao lidar com formatos mais antigos. Eu choro toda vez que vejo um número serialVersionUIDmaior que 1 dígito, seja por ser um número aleatório (inútil) ou porque a classe aparentemente precisa lidar com mais de 10 versões diferentes.
102618 John11518
11

SerialVersionUID é usado para controle de versão do objeto. você também pode especificar serialVersionUID no seu arquivo de classe. A conseqüência de não especificar serialVersionUID é que, quando você adiciona ou modifica qualquer campo na classe, a classe já serializada não poderá se recuperar porque serialVersionUID gerado para a nova classe e para o antigo objeto serializado será diferente. O processo de serialização do Java depende do serialVersionUID correto para recuperar o estado do objeto serializado e lança java.io.InvalidClassException no caso de incompatibilidade serialVersionUID

Leia mais: http://javarevisited.blogspot.com/2011/04/top-10-java-serialization-interview.html#ixzz3VQxnpOPZ

Neethu
fonte
9

Por que usar a classe SerialVersionUIDinterna Serializableem Java?

Durante serialization, o Java Runtime cria um número de versão para uma classe, para que possa desserializá-la posteriormente. Esse número da versão é conhecido como SerialVersionUIDem Java.

SerialVersionUIDé usado para versão de dados serializados. Você só pode desserializar uma classe se ela SerialVersionUIDcorresponder à instância serializada. Quando não declaramos SerialVersionUIDem nossa classe, o Java Runtime o gera para nós, mas não é recomendado. Recomenda-se declarar SerialVersionUIDcomoprivate static final long variável para evitar o mecanismo padrão.

Quando você declara uma classe como Serializableimplementando a interface do marcador java.io.Serializable, o Java Runtime persiste a instância dessa classe no disco usando o mecanismo de serialização padrão, desde que você não tenha customizado o processo usandoExternalizable interface.

veja também Por que usar SerialVersionUID dentro da classe Serializable em Java

roottraveller
fonte
8

Se você deseja alterar um grande número de classes que não tinham serialVersionUID definido, mantendo a compatibilidade com as classes antigas, ferramentas como o IntelliJ Idea, o Eclipse fica aquém, pois gera números aleatórios e não funciona em vários arquivos numa única tentativa. Eu venho o seguinte script bash (desculpe pelos usuários do Windows, considere comprar um Mac ou converter para Linux) para corrigir o problema de serialVersionUID com facilidade:

base_dir=$(pwd)                                                                  
src_dir=$base_dir/src/main/java                                                  
ic_api_cp=$base_dir/target/classes                                               

while read f                                                                     
do                                                                               
    clazz=${f//\//.}                                                             
    clazz=${clazz/%.java/}                                                       
    seruidstr=$(serialver -classpath $ic_api_cp $clazz | cut -d ':' -f 2 | sed -e 's/^\s\+//')
    perl -ni.bak -e "print $_; printf qq{%s\n}, q{    private $seruidstr} if /public class/" $src_dir/$f
done

você salva esse script, diga add_serialVersionUID.sh para você ~ / bin. Em seguida, você o executa no diretório raiz do seu projeto Maven ou Gradle, como:

add_serialVersionUID.sh < myJavaToAmend.lst

Este .lst inclui a lista de arquivos java para adicionar o serialVersionUID no seguinte formato:

com/abc/ic/api/model/domain/item/BizOrderTransDO.java
com/abc/ic/api/model/domain/item/CardPassFeature.java
com/abc/ic/api/model/domain/item/CategoryFeature.java
com/abc/ic/api/model/domain/item/GoodsFeature.java
com/abc/ic/api/model/domain/item/ItemFeature.java
com/abc/ic/api/model/domain/item/ItemPicUrls.java
com/abc/ic/api/model/domain/item/ItemSkuDO.java
com/abc/ic/api/model/domain/serve/ServeCategoryFeature.java
com/abc/ic/api/model/domain/serve/ServeFeature.java
com/abc/ic/api/model/param/depot/DepotItemDTO.java
com/abc/ic/api/model/param/depot/DepotItemQueryDTO.java
com/abc/ic/api/model/param/depot/InDepotDTO.java
com/abc/ic/api/model/param/depot/OutDepotDTO.java

Este script usa a ferramenta serialVer JDK sob o capô. Portanto, verifique se o seu $ JAVA_HOME / bin está no PATH.

schnell18
fonte
2
Me dá uma idéia: sempre regenere a versão serial do uid com uma ferramenta como esta, nunca à mão, antes de fazer um lançamento - dessa forma, você evita esquecer de fazer uma alteração em uma classe cuja versão da série uid deveria ter sido alterada devido a um problema real. mudança incompatível. Acompanhar isso manualmente é muito difícil.
Ignazio
6

Esta questão está muito bem documentada em Effective Java por Joshua Bloch. Um livro muito bom e uma leitura obrigatória. Vou descrever alguns dos motivos abaixo:

O tempo de execução da serialização vem com um número chamado versão serial para cada classe serializável. Esse número é chamado serialVersionUID. Agora, há um pouco de matemática por trás desse número e ele sai com base nos campos / métodos definidos na classe. Para a mesma classe, a mesma versão é gerada sempre. Esse número é usado durante a desserialização para verificar se o remetente e o destinatário de um objeto serializado carregaram classes para esse objeto que são compatíveis com a serialização. Se o receptor carregou uma classe para o objeto que possui um serialVersionUID diferente daquele da classe do remetente correspondente, a desserialização resultará em uma InvalidClassException.

Se a classe é serializável, você também pode declarar seu próprio serialVersionUID explicitamente declarando um campo chamado "serialVersionUID" que deve ser estático, final e do tipo long. A maioria dos IDEs, como o Eclipse, ajuda a gerar essa cadeia longa.

Nerd
fonte
6

Cada vez que um objeto é serializado, ele é carimbado com um número de ID da versão da classe do objeto. Esse ID é chamado serialVersionUID e é calculado com base nas informações sobre a estrutura da classe. Suponha que você tenha criado uma classe Employee e ela tenha o ID da versão nº 333 (atribuído pela JVM). Agora, quando você serializar o objeto dessa classe (objeto Suponha que o funcionário), a JVM atribuirá UID a ele como nº 333.

Considere uma situação - no futuro, você precisará editar ou alterar sua classe e, nesse caso, quando a modificar, a JVM atribuirá a ela um novo UID (suponha # 444). Agora, quando você tenta desserializar o objeto empregado, a JVM comparará a ID da versão do objeto serializado (objeto Empregado) (# 333) com a da classe, ou seja, # 444 (Desde que foi alterado). Em comparação, a JVM descobrirá que o UID da versão é diferente e, portanto, a desserialização falhará. Portanto, se serialVersionID para cada classe for definido pelo próprio programador. Será o mesmo, mesmo que a classe seja evoluída no futuro e, portanto, a JVM sempre achará que a classe é compatível com o objeto serializado, mesmo que a classe seja alterada. Para mais informações, consulte o capítulo 14 do HEAD PRIMST JAVA.

Naved Ali
fonte
1
Cada vez que a classe de um objeto é serializada, ela serialVersionUIDé transmitida. Não é enviado com todos os objetos.
Marquês de Lorne
3

Uma explicação simples:

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

  2. 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 a serialização diretamente).

  3. Esta é a primeira versão?

    Se sim, defina serialVersionUID=1L.

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

    Agora você precisa se preocupar serialVersionUIDe deve investigar isso 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.

Gagarwa
fonte
2

'serialVersionUID' é um número de 64 bits usado para identificar exclusivamente uma classe durante o processo de desserialização. Quando você serializa um objeto, serialVersionUID da classe também é gravado no arquivo. Sempre que você desserializar esse objeto, o tempo de execução java extrai esse valor serialVersionUID dos dados serializados e compara o mesmo valor associado à classe. Se ambos não corresponderem, 'java.io.InvalidClassException' será lançado.

Se uma classe serializável não declarar explicitamente um serialVersionUID, o tempo de execução da serialização calculará o valor serialVersionUID para essa classe com base em vários aspectos da classe, como campos, métodos etc. Você pode consultar este link para o aplicativo de demonstração.

Hari Krishna
fonte
1

Para contar a história longa, esse campo é usado para verificar se os dados serializados podem ser desserializados corretamente. A serialização e a desserialização geralmente são feitas por diferentes cópias do programa - por exemplo, o servidor converte objeto em string e o cliente converte a string recebida em objeto. Este campo informa que ambos operam com a mesma idéia sobre o que é esse objeto. Este campo ajuda quando:

  • você tem muitas cópias diferentes do seu programa em locais diferentes (como 1 servidor e 100 clientes). Se você mudar de objeto, alterar o número da versão e esquecer de atualizar um desses clientes, ele saberá que ele não é capaz de desserializar

  • você armazenou seus dados em algum arquivo e, posteriormente, tenta abri-lo com a versão atualizada do seu programa com objeto modificado - você saberá que esse arquivo não é compatível se você mantiver sua versão correta

Quando isso é importante?

O mais óbvio - se você adicionar alguns campos ao seu objeto, as versões mais antigas não poderão usá-los porque eles não têm esses campos em sua estrutura de objetos.

Menos óbvio - Quando você desserializa um objeto, os campos que não estiverem presentes na string serão mantidos como NULL. Se você removeu o campo do seu objeto, as versões mais antigas manterão esse campo como sempre NULL, o que pode levar a um comportamento inadequado se as versões mais antigas dependem dos dados desse campo (de qualquer maneira, você o criou para algo, não apenas por diversão :-))

Menos óbvio - às vezes você muda a ideia que coloca no significado de algum campo. Por exemplo, quando você tem 12 anos, você quer dizer "bicicleta" em "bicicleta", mas quando você tem 18 anos, significa "motocicleta" - se seus amigos o convidarem para "andar de bicicleta pela cidade" e você será o único que veio de bicicleta, você vai entender como é importante manter o mesmo significado nos campos :-)

Stanislav Orlov
fonte
1

Primeiramente, para responder à sua pergunta, quando não declaramos SerialVersionUID em nossa classe, o Java Runtime o gera para nós, mas esse processo é sensível a muitos metadados de classe, incluindo número de campos, tipo de campos, modificador de acesso de campos, interface implementada por classe etc. Portanto, é recomendável declá-lo nós mesmos e o Eclipse está avisando sobre o mesmo.

Serialização: frequentemente trabalhamos com objetos importantes cujo estado (dados nas variáveis ​​do objeto) é tão importante que não podemos arriscar perdê-lo devido a falhas de energia / sistema (ou) falhas de rede no caso de enviar o estado do objeto para outro máquina. A solução para esse problema é denominada "Persistência", que significa simplesmente persistir (reter / salvar) os dados. A serialização é uma das muitas outras maneiras de obter persistência (salvando dados no disco / memória). Ao salvar o estado do objeto, é importante criar uma identidade para o objeto, para poder lê-lo adequadamente (desserialização). Essa identificação exclusiva é ID é SerialVersionUID.

Shanks D Shiva
fonte
0

O que é o SerialVersionUID? Resposta: - Digamos que há duas pessoas, uma da HQ e outra do ODC, ambas realizarão serialização e desserialização, respectivamente. Nesse caso, para autenticar que o destinatário que está no ODC é a pessoa autenticada, a JVM cria um ID exclusivo, conhecido como SerialVersionUID.

Aqui está uma boa explicação com base no cenário,

Por que SerialVersionUID?

Serialização : No momento da serialização, com cada lado do remetente do objeto, a JVM salvará um Identificador Exclusivo. A JVM é responsável por gerar esse ID exclusivo com base no arquivo .class correspondente que está presente no sistema emissor.

Desserialização : No momento da desserialização, a JVM do lado do receptor comparará o ID exclusivo associado ao Objeto com o ID Único da classe local, ou seja, a JVM também criará um ID Exclusivo com base no arquivo .class correspondente que está presente no sistema receptor. Se o ID exclusivo corresponder, somente a desserialização será executada. Caso contrário, obteremos a Runtime Exception dizendo InvalidClassException. Esse identificador exclusivo não passa de SerialVersionUID

forkdbloke
fonte