¿Qué es un serialVersionUID y por qué debería usarlo?

2999

Eclipse emite advertencias cuando serialVersionUIDfalta un.

La clase serializable Foo no declara un campo estático final serialVersionUID de tipo largo

¿Qué es serialVersionUIDy por qué es importante? Muestre un ejemplo en el que faltar serialVersionUIDprovocará un problema.

ashokgelal
fuente
1
Encuentre una buena práctica sobre serialversionUID; dzone.com/articles/what-is-serialversionuid
ceyun

Respuestas:

2290

Los documentos para java.io.Serializableson probablemente una explicación tan buena como la que obtendrá:

El tiempo de ejecución de serialización asocia con cada clase serializable un número de versión, llamado a serialVersionUID, que se utiliza durante la deserialización para verificar que el emisor y el receptor de un objeto serializado hayan cargado clases para ese objeto que son compatibles con respecto a la serialización. Si el receptor ha cargado una clase para el objeto que tiene una clase diferente serialVersionUIDa la de la clase del remitente correspondiente, la deserialización dará como resultado un InvalidClassException. Una clase serializable puede declarar su propia serialVersionUIDexplícitamente declarando un campo llamado serialVersionUIDque debe ser estático, final y de tipo long:

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

Si una clase serializable no declara explícitamente a serialVersionUID, entonces el tiempo de ejecución de serialización calculará un serialVersionUIDvalor predeterminado para esa clase basado en varios aspectos de la clase, como se describe en la Especificación de serialización de objetos Java (TM). Sin embargo, se recomienda encarecidamente que todas las clases serializables declaren serialVersionUIDvalores explícitamente , ya que el serialVersionUIDcálculo predeterminado es muy sensible a los detalles de la clase que pueden variar según las implementaciones del compilador y, por lo tanto, pueden resultar inesperados InvalidClassExceptionsdurante la deserialización. Por lo tanto, para garantizar un serialVersionUIDvalor consistente en diferentes implementaciones del compilador de Java, una clase serializable debe declarar un serialVersionUIDvalor explícito . También se recomienda encarecidamente que explícitoserialVersionUIDlas declaraciones usan el modificador privado siempre que sea posible, ya que tales declaraciones se aplican solo a los serialVersionUIDcampos de clase que declaran inmediatamente no son útiles como miembros heredados.

Jon Skeet
fuente
326
Entonces, lo que está diciendo esencialmente es que si un usuario no entendió todo el material anterior, ¿dicho usuario no debería preocuparse por la serialización? Creo que respondiste el "¿cómo?" en lugar de explicar el "¿por qué?". Por mi parte, no entiendo por qué me molesto con SerializableVersionUID.
Ziggy
366
El motivo está en el segundo párrafo: si no especifica explícitamente serialVersionUID, se genera un valor automáticamente, pero eso es frágil porque depende de la implementación del compilador.
Jon Skeet el
14
¿Y por qué Eclipse dice que necesito "private static final long serialVersionUID = 1L;" cuando extiendo la clase de excepción?
JohnMerlino
21
@JohnMerlino: Bueno, no esperaría que dijera que necesita uno, pero puede estar sugiriendo uno para ayudarlo a serializar las excepciones correctamente. Si no vas a serializarlos, realmente no necesitas la constante.
Jon Skeet
16
@JohnMerlino, para responder a la pregunta de por qué: Exception implementa Serializable y eclipse advierte que no ha configurado un serialVersionUID, lo que sería una buena idea (si no desea serializar la clase) para evitar los problemas que la publicación de JonSkeet contornos
zpon
475

Si está serializando solo porque tiene que serializar por el bien de la implementación (a quién le importa si serializa para un HTTPSession, por ejemplo ... si está almacenado o no, probablemente no le interese de-serializingun objeto de formulario), entonces puede ignora esto.

Si realmente está utilizando la serialización, solo importa si planea almacenar y recuperar objetos utilizando la serialización directamente. El serialVersionUIDrepresenta la versión de la clase, y que debería incrementarse si la versión actual de su clase no es compatible con su versión anterior.

La mayoría de las veces, probablemente no utilizará la serialización directamente. Si este es el caso, genere un valor predeterminado SerialVersionUIDhaciendo clic en la opción de solución rápida y no se preocupe.

MetroidFan2002
fuente
78
Diría que si no está utilizando la serialización para el almacenamiento permanente, debe usar @SuppressWarnings en lugar de agregar un valor. Abarrota a la clase menos, y preserva la capacidad del mecanismo serialVersionUID para protegerlo de cambios incompatibles.
Tom Anderson el
25
No veo cómo agregar una línea (anotación @SuppressWarnings) en lugar de otra línea (identificación serializable) "satura la clase menos". Y si no está utilizando la serialización para almacenamiento permanente, ¿por qué no usaría simplemente "1"? De todos modos, no le importaría la identificación autogenerada en ese caso.
MetroidFan2002
71
@ MetroidFan2002: Creo que el punto de @ TomAnderson de serialVersionUIDprotegerlo de cambios incompatibles es válido. El uso de @SuppressWarningsdocumentos es mejor si no desea utilizar la clase para el almacenamiento permanente.
AbdullahC
11
"Debería incrementarlo si la versión actual de su clase no es compatible con versiones anteriores de su clase anterior:" Primero debe explorar el amplio soporte de versiones de objetos de Serialización, (a) para asegurarse de que la clase realmente sea ahora incompatible con la serialización, que según la especificación es bastante difícil de lograr; (b) probar un esquema como los métodos personalizados read / writeObject (), métodos readResolve / writeReplace (), declaraciones serializableFields, etc., para asegurarse de que la secuencia sigue siendo compatible. Cambiar lo real serialVersionUIDes un último recurso, un consejo de desesperación.
Marqués de Lorne
44
El incremento @EJP de serialVersionUID aparece en la imagen cuando el autor inicial de la clase se ha introducido explícitamente. Diría que jvm generó un ID de serie, debería estar bien. Esta es la mejor respuesta que vi en la serialización.
intercambio excesivo el
307

No puedo dejar pasar esta oportunidad de conectar el libro de Josh Bloch Effective Java (2nd Edition). El Capítulo 11 es un recurso indispensable en la serialización de Java.

Según Josh, el UID generado automáticamente se genera en función de un nombre de clase, interfaces implementadas y todos los miembros públicos y protegidos. Cambiar cualquiera de estos de alguna manera cambiará el serialVersionUID. Por lo tanto, no necesita meterse con ellos solo si está seguro de que no se serializará más de una versión de la clase (ya sea a través de procesos o recuperada del almacenamiento en un momento posterior).

Si los ignora por ahora, y encontrar más adelante que es necesario cambiar la clase de alguna manera, pero mantener la compatibilidad w / versión antigua de la clase, puede utilizar la herramienta JDK serialver para generar el serialVersionUIDde la vieja clase, y establece explícitamente que en la nueva clase. (Dependiendo de sus cambios, es posible que también necesite implementar una serialización personalizada agregando writeObjecty readObjectmétodos; consulte Serializablejavadoc o el capítulo 11 mencionado anteriormente).

Scott Bale
fuente
33
Entonces, ¿podría preocuparse con SerializableVersionUID si le preocupara la compatibilidad con las versiones anteriores de una clase?
Ziggy
10
Sí, en caso de que la versión más nueva cambie algún miembro público a protegido, el SerializableVersionUID predeterminado será diferente y generará una InvalidClassExceptions.
Chander Shivdasani
3
Nombre de clase, interfaces implementadas, todos los métodos públicos y protegidos, TODAS las variables de instancia.
Achow
31
Vale la pena señalar que Joshua Bloch advierte que por cada clase serializable vale la pena especificar la versión en serie uid. Cita del capítulo 11: Independientemente de la forma serializada que elija, declare un UID de versión serial explícita en cada clase serializable que escriba. Esto elimina el UID de la versión en serie como una fuente potencial de incompatibilidad (Elemento 74). También hay un pequeño beneficio de rendimiento. Si no se proporciona un UID de versión en serie, se requiere un cálculo costoso para generar uno en tiempo de ejecución.
Ashutosh Jindal
136

Puede decirle a Eclipse que ignore estas advertencias de serialVersionUID:

Ventana> Preferencias> Java> Compilador> Errores / advertencias> Posibles problemas de programación

En caso de que no lo supiera, hay muchas otras advertencias que puede habilitar en esta sección (o incluso tener algunas informadas como errores), muchas son muy útiles:

  • Posibles problemas de programación: posible asignación booleana accidental
  • Posibles problemas de programación: acceso de puntero nulo
  • Código innecesario: la variable local nunca se lee
  • Código innecesario: verificación nula redundante
  • Código innecesario: conversión innecesaria o 'instancia de'

y muchos más.

mate b
fuente
21
voto positivo, pero solo porque el póster original no parece estar serializando nada. Si el póster dijera "Estoy serializando esto y ...", entonces tendría un voto negativo: P
John Gardner
3
Es cierto: suponía que el interlocutor simplemente no quería ser advertido
matt b
14
@Gardner -> de acuerdo! Pero el interlocutor también quiere saber por qué no quiere que le avisen.
Ziggy
8
El interlocutor obviamente se preocupa por qué debería haber un UID. Entonces, simplemente decirle que ignore la advertencia debería ser rechazado.
cinqS
111

serialVersionUIDfacilita el versionado de datos serializados. Su valor se almacena con los datos cuando se serializa. Al deserializar, se verifica la misma versión para ver cómo los datos serializados coinciden con el código actual.

Si desea versionar sus datos, normalmente comienza con un serialVersionUID0, y lo agrega con cada cambio estructural a su clase que altera los datos serializados (agregando o eliminando campos no transitorios).

El mecanismo de deserialización incorporado ( in.defaultReadObject()) se negará a deserializar las versiones anteriores de los datos. Pero si lo desea, puede definir su propia función readObject () que puede leer datos antiguos. Este código personalizado puede verificar elserialVersionUID para saber en qué versión están los datos y decidir cómo deserializarlos. Esta técnica de control de versiones es útil si almacena datos serializados que sobreviven a varias versiones de su código.

Pero almacenar datos serializados durante un período de tiempo tan largo no es muy común. Es mucho más común usar el mecanismo de serialización para escribir temporalmente datos en, por ejemplo, un caché o enviarlos a través de la red a otro programa con la misma versión de las partes relevantes de la base de código.

En este caso, no está interesado en mantener la compatibilidad con versiones anteriores. Solo le interesa asegurarse de que las bases de código que se comunican tienen las mismas versiones de clases relevantes. Para facilitar tal verificación, debe mantener el serialVersionUIDmismo como antes y no olvidar actualizarlo cuando realice cambios en sus clases.

Si olvida actualizar el campo, puede terminar con dos versiones diferentes de una clase con una estructura diferente pero con la misma serialVersionUID. Si esto sucede, el mecanismo predeterminado ( in.defaultReadObject()) no detectará ninguna diferencia e intentará deserializar los datos incompatibles. Ahora puede terminar con un error de tiempo de ejecución críptico o una falla silenciosa (campos nulos). Este tipo de errores puede ser difícil de encontrar.

Entonces, para ayudar a este caso de uso, la plataforma Java le ofrece la opción de no configurarlo serialVersionUIDmanualmente. En cambio, se generará un hash de la estructura de clases en tiempo de compilación y se usará como id. Este mecanismo se asegurará de que nunca tenga estructuras de clase diferentes con la misma identificación, por lo que no obtendrá estos fallos de serialización de tiempo de ejecución difíciles de rastrear mencionados anteriormente.

Pero hay una parte trasera de la estrategia de identificación generada automáticamente. A saber, que los identificadores generados para la misma clase pueden diferir entre los compiladores (como se mencionó anteriormente por Jon Skeet). Entonces, si comunica datos serializados entre el código compilado con diferentes compiladores, se recomienda mantener los identificadores manualmente de todos modos.

Y si usted es retrocompatible con sus datos, como en el primer caso de uso mencionado, probablemente también desee mantener la identificación usted mismo. Esto para obtener identificadores legibles y tener un mayor control sobre cuándo y cómo cambian.

Alexander Torstling
fuente
44
Agregar o eliminar campos no transitorios no hace que la clase de serialización sea incompatible. Por lo tanto, no hay ninguna razón para "golpearlo" en tales cambios.
Marqués de Lorne
44
@EJP: ¿Eh? Agregar datos definitivamente cambia los datos de serialización en mi mundo.
Alexander Torstling
3
@AlexanderTorstling Lee lo que escribí. No dije que no 'cambia los datos de serialización'. Dije que 'no hace que la clase de serialización sea incompatible'. No es lo mismo. Debe leer el capítulo Control de versiones de la Especificación de serialización de objetos.
Marqués de Lorne
3
@EJP: Me doy cuenta de que agregar un campo no transitorio no necesariamente significa que haga que la clase sea incompatible con la serialización, pero es un cambio estructural que altera los datos serializados y, por lo general, desea cambiar la versión al hacerlo a menos que usted manejar la compatibilidad con versiones anteriores, que también expliqué más adelante en la publicación. ¿Cuál es su punto exactamente?
Alexander Torstling
44
Mi punto sigue siendo exactamente lo que dije. Agregar o eliminar campos no transitorios no hace que la clase Serialización sea incompatible. Por lo tanto, no necesita topar el serialVersionUID cada vez que lo haga.
Marqués de Lorne
72

¿Qué es un serialVersionUID y por qué debería usarlo?

SerialVersionUIDes un identificador único para cada clase, lo JVMutiliza para comparar las versiones de la clase, asegurando que la misma clase se utilizó durante la serialización se carga durante la deserialización.

Especificar uno da más control, aunque JVM genera uno si no lo especifica. El valor generado puede diferir entre diferentes compiladores. Además, a veces solo desea por alguna razón prohibir la deserialización de objetos serializados antiguos [ backward incompatibility], y en este caso solo tiene que cambiar el serialVersionUID.

Los javadocs paraSerializable decir :

el cálculo predeterminado de serialVersionUID es muy sensible a los detalles de la clase que pueden variar según las implementaciones del compilador y, por lo tanto, pueden generar InvalidClassExceptions inesperados durante la deserialización.

Por lo tanto, debe declarar serialVersionUID porque nos da más control .

Este artículo tiene algunos buenos puntos sobre el tema.

Thalaivar
fuente
3
@Vinothbabu pero serialVersionUID es estático, por lo que las variables estáticas no se pueden serializar. entonces, ¿cómo es que jvm verificará la versión, sin saber cuál es la versión del objeto deserializador
Kranthi Sama
3
Lo único que no se menciona en esta respuesta es que puede causar consecuencias no deseadas al incluir a ciegas serialVersionUIDsin saber por qué. El comentario de Tom Anderson sobre la respuesta de MetroidFan2002 aborda esto: "Yo diría que si no está utilizando la serialización para el almacenamiento permanente, debe usar @SuppressWarnings en lugar de agregar un valor. Abarrota la clase menos y preserva la capacidad de Mecanismo serialVersionUID para protegerlo de cambios incompatibles ".
Kirby
77
serialVersionUIDno es un "identificador único para cada clase". El nombre de clase completo es ese. Es un indicador de versión .
Marqués de Lorne
58

La pregunta original ha preguntado "por qué es importante" y "ejemplo" donde esto Serial Version IDsería útil. Pues he encontrado uno.

Supongamos que crea una Carclase, la instancia y la escribe en una secuencia de objetos. El objeto aplanado del automóvil permanece en el sistema de archivos durante un tiempo. Mientras tanto, si la Carclase se modifica agregando un nuevo campo. Más adelante, cuando intentas leer (es decir, deserializar) el Carobjeto aplanado , obtienes el java.io.InvalidClassException- porque todas las clases serializables reciben automáticamente un identificador único. Esta excepción se produce cuando el identificador de la clase no es igual al identificador del objeto aplanado. Si realmente lo piensa, la excepción se produce debido a la adición del nuevo campo. Puede evitar esta excepción controlando la versión usted mismo declarando un serialVersionUID explícito. También hay un pequeño beneficio de rendimiento al declarar explícitamente suserialVersionUID (porque no tiene que calcularse). Por lo tanto, es una buena práctica agregar su propio serialVersionUID a sus clases serializables tan pronto como las cree, como se muestra a continuación:

public class Car {
    static final long serialVersionUID = 1L; //assign a long value
}
Rupesh
fuente
Y uno debe asignar un número largo aleatorio, no 1L, a cada UID.
abbas
44
@abbas 'Uno debería' hacer eso ¿por qué? Por favor explique qué diferencia hace.
Marqués de Lorne el
Como su nombre lo indica, representa la versión de la clase que se usó para serializar el objeto. Si le asigna el mismo número cada vez, no podrá encontrar la clase correcta para deserializarlo. Entonces, cada vez que realice un cambio en la clase, también es mejor cambiar la versión.
abbas
2
@abbas, esta intención no choca con el uso de números naturales incrementales, 1etc.
Vadzim
2
@ Bill, pensé que la verificación de serialización está vinculada al par de classname y serialVersionUID. Por lo tanto, diferentes esquemas de numeración de diferentes clases y bibliotecas no pueden interferir de ninguna manera. ¿O implicabas bibliotecas generadoras de código?
Vadzim
44

Primero necesito explicar qué es la serialización.

La serialización permite convertir un objeto en una secuencia, para enviar ese objeto a través de la red O Guardar en archivo O guardar en DB para uso de letras.

Hay algunas reglas para la serialización .

  • Un objeto es serializable solo si su clase o su superclase implementa la interfaz serializable

  • Un objeto es serializable (implementa la interfaz serializable) incluso si su superclase no lo es. Sin embargo, la primera superclase en la jerarquía de la clase serializable, que no implementa la interfaz serializable, DEBE tener un constructor sin argumentos. Si esto se viola, readObject () producirá una excepción java.io.InvalidClassException en tiempo de ejecución

  • Todos los tipos primitivos son serializables.

  • Los campos transitorios (con modificador transitorio) NO se serializan (es decir, no se guardan ni restauran). Una clase que implementa Serializable debe marcar campos transitorios de clases que no admiten la serialización (por ejemplo, una secuencia de archivos).

  • Los campos estáticos (con modificador estático) no se serializan.

Cuando Objectse serializa, Java Runtime asocia el número de versión de serie, también conocido como serialVersionID.

Donde necesitamos serialVersionID: durante la deserialización para verificar que el remitente y el receptor sean compatibles con respecto a la serialización. Si el receptor cargó la clase con una diferenteserialVersionID, la deserialización terminará conInvalidClassCastException.
Una clase serializable puede declarar su propiaserialVersionUIDexplícitamente declarando un campo llamadoserialVersionUIDque debe ser estático, final y de tipo largo.

Probemos esto con un ejemplo.

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();
}
}

Crear objeto serializado

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");
}
}

Deserializar el 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: Ahora cambie el serialVersionUID de la clase Employee y guarde:

private static final long serialVersionUID = 4L;

Y ejecuta la clase Reader. No ejecutar la clase Writer y obtendrá la excepción.

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
fuente
Corríjame si me equivoco: la clase local es la que está teniendo / utilizando actualmente en el classpath y la secuencia uno es la utilizada por otra parte (por ejemplo, un servidor que le está devolviendo una respuesta y ha serializado el respuesta). Puede encontrar esta situación cuando se está comunicando con un servidor que ha actualizado sus bibliotecas de terceros, pero usted (el cliente) no lo hizo.
Victor
37

Si nunca necesitará serializar sus objetos en una matriz de bytes y enviarlos / almacenarlos, entonces no necesita preocuparse por eso. Si lo hace, debe considerar su serialVersionUID ya que el deserializador del objeto lo hará coincidir con la versión del objeto que tiene su cargador de clases. Lea más sobre esto en la Especificación del lenguaje Java.

eishay
fuente
10
Si no va a serializar los objetos, ¿por qué son serializables?
erickson
77
@erickson: la clase padre puede ser serializable, por ejemplo, ArrayList, pero desea que su propio objeto (por ejemplo, una lista de matriz modificada) lo use como base, pero nunca serializará la Colección que cree.
MetroidFan2002
55
No se menciona en ninguna parte de la Especificación del lenguaje Java. Se menciona en la especificación de versiones de objetos.
Marqués de Lorne
44
Aquí está el enlace a la especificación de versiones de objetos Java 8 .
Basil Bourque
34

Si recibe esta advertencia en una clase que nunca piensa en serializar, y que no se declaró implements Serializable, a menudo es porque la heredó de una superclase, que implementa Serializable. A menudo, entonces sería mejor delegar a dicho objeto en lugar de usar la herencia.

Entonces, en lugar de

public class MyExample extends ArrayList<String> {

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

hacer

public class MyExample {
    private List<String> myList;

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

y en los métodos relevantes llame en myList.foo()lugar de this.foo()(o super.foo()). (Esto no encaja en todos los casos, pero aún con bastante frecuencia).

A menudo veo personas que extienden JFrame o algo así, cuando realmente solo necesitan delegar en esto. (Esto también ayuda a completar automáticamente en un IDE, ya que JFrame tiene cientos de métodos, que no necesita cuando desea llamar a sus personalizados en su clase).

Un caso en el que la advertencia (o el serialVersionUID) es inevitable es cuando se extiende desde AbstractAction, normalmente en una clase anónima, solo agregando el método actionPerformed. Creo que no debería haber una advertencia en este caso (ya que normalmente no se puede serializar y deserializar de manera confiable tales clases anónimas de todas formas en diferentes versiones de su clase), pero no estoy seguro de cómo el compilador podría reconocer esto.

Paŭlo Ebermann
fuente
44
Creo que tienes razón en que la composición sobre la herencia tiene más sentido, particularmente cuando estás discutiendo clases como ArrayList. Sin embargo, muchos marcos requieren que las personas se extiendan desde las superclases abstractas que son serializables (como la clase ActionForm de Struts 1.2 o la ExtensionFunctionDefinition de Saxon) en cuyo caso esta solución no es factible. Creo que tiene razón, sería bueno si la advertencia se ignorara en ciertos casos (como si se extendiera desde una clase serializable abstracta)
Piepera
2
Seguramente si agrega una clase como miembro, en lugar de heredar de ella, tendría que escribir un método de envoltura para CADA método de la clase de miembro que desea utilizar, lo que lo haría inviable en una gran cantidad de situaciones. a menos que Java tenga una función similar a la de Perl __AUTOLOAD, de la que no sé nada.
M_M
44
@M_M: Cuando delegaría muchos métodos a su objeto envuelto, por supuesto, no sería apropiado usar la delegación. Pero supongo que este caso es un signo de un error de diseño: los usuarios de su clase (por ejemplo, "MainGui") no deberían necesitar llamar a muchos métodos del objeto envuelto (por ejemplo, JFrame).
Paŭlo Ebermann
1
Lo que no me gusta de la delegación es la necesidad de tener una referencia al delegado. Y cada referencia significa más memoria. Corrígeme si estoy equivocado. Si necesito una CustomizedArrayList de 100 objetos, esto no importaría mucho, pero si necesito cientos de CustomizdeArrayList de algunos objetos, el uso de memoria aumenta significativamente.
WVrock
2
Throwable es serializable, y solo Throwable es arrojable, por lo que no es posible definir una excepción que no sea serializable. La delegación no es posible.
Phil
22

Para comprender la importancia del campo serialVersionUID, uno debe entender cómo funciona la serialización / deserialización.

Cuando un objeto de clase serializable se serializa, Java Runtime asocia un número de versión serial (llamado serialVersionUID) con este objeto serializado. En el momento en que deserializa este objeto serializado, Java Runtime coincide con el serialVersionUID del objeto serializado con el serialVersionUID de la clase. Si ambos son iguales, entonces solo continúa con el proceso posterior de deserialización; de lo contrario, se produce InvalidClassException.

Por lo tanto, concluimos que para que el proceso de serialización / deserialización sea exitoso, el serialVersionUID del objeto serializado debe ser equivalente al serialVersionUID de la clase. En caso de que el programador especifique explícitamente el valor serialVersionUID en el programa, el mismo valor se asociará con el objeto serializado y la clase, independientemente de la plataforma de serialización y deserialización (por ejemplo, la serialización se puede hacer en una plataforma como Windows usando Sun o MS JVM y Deserialization pueden estar en diferentes plataformas Linux usando Zing JVM).

Pero en caso de que el programador no especifique serialVersionUID, al realizar Serialization \ DeSerialization de cualquier objeto, el tiempo de ejecución de Java usa su propio algoritmo para calcularlo. Este algoritmo de cálculo serialVersionUID varía de un JRE a otro. También es posible que el entorno en el que se serializa el objeto esté utilizando un JRE (por ejemplo: SUN JVM) y el entorno donde se produce la deserialización está utilizando Linux Jvm (zing). En tales casos, serialVersionUID asociado con el objeto serializado será diferente al serialVersionUID de la clase calculada en el entorno de deserialización. A su vez, la deserialización no tendrá éxito. Entonces, para evitar tales situaciones / problemas, el programador siempre debe especificar serialVersionUID de la clase Serializable.

Nitesh Soni
fuente
2
El algoritmo no varía, pero está ligeramente subespecificado.
Marqués de Lorne
... el algoritmo no varía, pero está ligeramente subespecificado ... lo que significa que cualquier jvm puede variar ..... @ user207421
AnthonyJClink
18

No se moleste, el cálculo predeterminado es realmente bueno y suficiente para el 99,9999% de los casos. Y si tiene problemas, puede, como ya se ha dicho, introducir los UID cuando sea necesario (lo cual es muy poco probable)

Grand Johnson
fuente
2
Basura. Es adecuado en el caso donde la clase no ha cambiado. Tienes cero pruebas para apoyar '99 .9999% '.
Marqués de Lorne
18

En cuanto a un ejemplo donde el serialVersionUID faltante puede causar un problema:

Estoy trabajando en esta aplicación Java EE que se compone de un módulo web que usa un EJBmódulo. El módulo web llama al EJBmódulo de forma remota y pasa uno POJOque implementaSerializable como argumento.

Esta POJO's clase fue empaquetada dentro del jar EJB y dentro de su propio jar en el WEB-INF / lib del módulo web. En realidad son de la misma clase, pero cuando empaquete el módulo EJB, desempaquete el jar de este POJO para empacarlo junto con el módulo EJB.

La llamada al EJBestaba fallando con la excepción a continuación porque no había declarado su 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
fuente
16

Generalmente lo uso serialVersionUIDen un contexto: cuando sé que abandonará el contexto de la máquina virtual Java.

Lo sabría cuando lo use ObjectInputStreamy ObjectOutputStreampara mi aplicación o si sé que una biblioteca / marco que uso lo usaré. SerialVersionID garantiza que diferentes máquinas virtuales Java de diferentes versiones o proveedores interactúen correctamente o, por ejemplo, si se almacenan y recuperan fuera de la máquina virtualHttpSession los datos de la sesión pueden permanecer incluso durante un reinicio y actualización del servidor de aplicaciones.

Para todos los demás casos, uso

@SuppressWarnings("serial")

ya que la mayoría de las veces el valor predeterminado serialVersionUIDes suficiente. Esto incluye Exception, HttpServlet.

Arquímedes Trajano
fuente
No incluye HttpServlet en contenedores donde se pueden intercambiar, o Excepción en RMI, por ejemplo.
Marqués de Lorne
14

Los datos de campo representan cierta información almacenada en la clase. La clase implementa la Serializableinterfaz, por lo que eclipse se ofrece automáticamente para declarar el serialVersionUIDcampo. Comencemos con el valor 1 establecido allí.

Si no desea que llegue esa advertencia, use esto:

@SuppressWarnings("serial")
Mukti
fuente
11

Sería bueno si CheckStyle pudiera verificar que el serialVersionUID en una clase que implementa Serializable tenga un buen valor, es decir, que coincida con lo que produciría el generador de id de versión en serie. Si tiene un proyecto con muchos DTO serializables, por ejemplo, recordar eliminar el serialVersionUID existente y regenerarlo es una molestia, y actualmente la única forma (que sé) de verificar esto es regenerar para cada clase y compararlo con el viejo. Esto es muy muy doloroso.


fuente
8
Si configura el serialVersionUID siempre en el mismo valor que produciría el generador, realmente no lo necesita en absoluto. Después de todo, su razón de ser es permanecer igual después de los cambios, cuando la clase aún es compatible.
Paŭlo Ebermann
44
La razón es para que diferentes compiladores obtengan el mismo valor para la misma clase. Como se explica en los javadocs (también respondidos anteriormente), la versión generada es frágil y puede variar incluso cuando la clase es deserializable correctamente. Siempre que ejecute esta prueba en el mismo compilador cada vez, debería ser segura. que Dios te ayude si actualizas el jdk y sale una nueva regla, aunque tu código no haya cambiado.
Andrew Backer
2
No es necesario que coincida con lo serialverque produciría. -1
Marqués de Lorne
En general no se requiere en absoluto. El caso de @ AndrewBacker requeriría dos compiladores diferentes en el mismo archivo .java con ambas versiones de los archivos .class comunicándose entre sí, la mayoría de las veces solo crea la clase y la distribuye. Si ese es el caso, entonces no tener un SUID funcionaría bien.
Bill K
1
Las personas que realmente usan la serialización para fines de almacenamiento / recuperación generalmente establecerán el serialVersionUID1. Si una versión más nueva de la clase es incompatible, pero aún requiere poder manejar los datos antiguos, incrementa el número de versión y agrega un código especial a lidiar con formatos más antiguos. Lloro cada vez que veo un serialVersionUIDdígito mayor de 1 dígito, ya sea porque era un número aleatorio (inútil) o porque la clase aparentemente necesita lidiar con más de 10 versiones diferentes.
john16384
11

SerialVersionUID se utiliza para el control de versión del objeto. También puede especificar serialVersionUID en su archivo de clase. La consecuencia de no especificar serialVersionUID es que cuando agrega o modifica cualquier campo en la clase, la clase ya serializada no podrá recuperarse porque serialVersionUID generado para la nueva clase y para el antiguo objeto serializado será diferente. El proceso de serialización de Java se basa en serialVersionUID correcto para recuperar el estado del objeto serializado y arroja java.io.InvalidClassException en caso de incompatibilidad serialVersionUID

Leer más: http://javarevisited.blogspot.com/2011/04/top-10-java-serialization-interview.html#ixzz3VQxnpOPZ

Neethu
fuente
9

¿Por qué usar SerialVersionUIDdentroSerializable class en Java?

Durante el serializationtiempo de ejecución de Java, se crea un número de versión para una clase, de modo que pueda deserializarlo más tarde. Este número de versión se conoce como SerialVersionUIDen Java.

SerialVersionUIDse utiliza para versionar datos serializados. Solo puede des serializar una clase si SerialVersionUIDcoincide con la instancia serializada. Cuando no declaramos SerialVersionUIDen nuestra clase, el tiempo de ejecución de Java lo genera para nosotros, pero no se recomienda. Se recomienda declarar SerialVersionUIDcomoprivate static final long variable para evitar el mecanismo predeterminado.

Cuando declara una clase como Serializableimplementando la interfaz de marcador java.io.Serializable, el tiempo de ejecución de Java persiste la instancia de esa clase en el disco utilizando el mecanismo de serialización predeterminado, siempre que no haya personalizado el proceso utilizandoExternalizable interfaz.

vea también ¿Por qué usar SerialVersionUID dentro de la clase Serializable en Java?

roottraveller
fuente
8

Si desea modificar una gran cantidad de clases que no tenían un serialVersionUID establecido en primer lugar mientras mantiene la compatibilidad con las clases antiguas, las herramientas como IntelliJ Idea, Eclipse se quedan cortas, ya que generan números aleatorios y no funcionan en un montón de archivos en una ida. Presento el siguiente script bash (lo siento por los usuarios de Windows, considere comprar una Mac o convertir a Linux) para solucionar el problema de serialVersionUID con facilidad:

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

guarde este script, diga add_serialVersionUID.sh a usted ~ / bin. Luego lo ejecuta en el directorio raíz de su proyecto Maven o Gradle como:

add_serialVersionUID.sh < myJavaToAmend.lst

Este .lst incluye la lista de archivos java para agregar el serialVersionUID en el siguiente 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 utiliza la herramienta JDK serialVer debajo del capó. Así que asegúrese de que su $ JAVA_HOME / bin esté en la RUTA.

schnell18
fuente
2
Me da una idea: siempre regenere el uid de la versión en serie con una herramienta como esta, nunca a mano, antes de realizar un lanzamiento; de esa forma, evita olvidarse de hacer un cambio en una clase cuya versión en serie uid debería haber cambiado debido a un problema real. cambio incompatible Hacer un seguimiento de eso manualmente es muy difícil.
Ignazio
6

Esta pregunta está muy bien documentada en Effective Java por Joshua Bloch. Un muy buen libro y una lectura obligada. Voy a describir algunas de las razones a continuación:

El tiempo de ejecución de la serialización viene con un número llamado versión en serie para cada clase serializable. Este número se llama serialVersionUID. Ahora hay algo de Matemáticas detrás de este número y sale según los campos / métodos que se definen en la clase. Para la misma clase, se genera la misma versión cada vez. Este número se utiliza durante la deserialización para verificar que el emisor y el receptor de un objeto serializado hayan cargado clases para ese objeto que sean compatibles con respecto a la serialización. Si el receptor ha cargado una clase para el objeto que tiene un serialVersionUID diferente al de la clase del remitente correspondiente, la deserialización dará como resultado una InvalidClassException.

Si la clase es serializable, también puede declarar explícitamente su propio serialVersionUID declarando un campo llamado "serialVersionUID" que debe ser estático, final y de tipo largo. La mayoría de los IDE como Eclipse lo ayudan a generar esa cadena larga.

Friki
fuente
6

Cada vez que se serializa un objeto, se marca con un número de ID de versión para la clase del objeto. Este ID se llama serialVersionUID y se calcula en función de la información sobre la estructura de la clase. Suponga que creó una clase Employee y tiene el ID de versión # 333 (asignado por JVM). Ahora, cuando serialice el objeto de esa clase (Suponga que el objeto Employee), JVM le asignará UID como # 333.

Considere una situación: en el futuro deberá editar o cambiar su clase y, en ese caso, cuando la modifique, JVM le asignará un nuevo UID (Suponga que # 444). Ahora, cuando intente deserializar el objeto de empleado, JVM comparará el ID de versión del objeto serializado (objeto de empleado) (# 333) con el de la clase, es decir, # 444 (desde que se modificó). En comparación, JVM encontrará que ambos UID de versión son diferentes y, por lo tanto, la deserialización fallará. Por lo tanto, si serialVersionID para cada clase está definido por el propio programador. Será lo mismo incluso si la clase se desarrolla en el futuro y, por lo tanto, JVM siempre encontrará que la clase es compatible con el objeto serializado aunque la clase haya cambiado. Para obtener más información, puede consultar el capítulo 14 de HEAD FIRST JAVA.

Ali naved
fuente
1
Cada vez que se serializa la clase de un objeto, serialVersionUIDse transmite. No se envía con cada objeto.
Marqués de Lorne
3

Una explicación simple:

  1. ¿Estás serializando datos?

    La serialización es básicamente escribir datos de clase en un archivo / flujo / etc. La deserialización es leer esos datos nuevamente en una clase.

  2. ¿Pretendes entrar en producción?

    Si solo está probando algo con datos falsos o sin importancia, no se preocupe por ello (a menos que esté probando la serialización directamente).

  3. ¿Es esta la primera versión?

    Si es así, listo serialVersionUID=1L.

  4. ¿Es esta la segunda, tercera, etc. versión prod?

    Ahora debe preocuparse serialVersionUIDy debe analizarlo en profundidad.

Básicamente, si no actualiza la versión correctamente cuando actualiza una clase que necesita escribir / leer, recibirá un error cuando intente leer datos antiguos.

Gagarwa
fuente
2

'serialVersionUID' es un número de 64 bits utilizado para identificar de forma exclusiva una clase durante el proceso de deserialización. Cuando serializa un objeto, serialVersionUID de la clase también se escribe en el archivo. Siempre que deserialice este objeto, el tiempo de ejecución de Java extrae este valor serialVersionUID de los datos serializados y compara el mismo valor asociado con la clase. Si ambos no coinciden, se lanzará 'java.io.InvalidClassException'.

Si una clase serializable no declara explícitamente un serialVersionUID, el tiempo de ejecución de serialización calculará el valor serialVersionUID para esa clase en función de varios aspectos de la clase, como campos, métodos, etc., puede consultar este enlace para la aplicación de demostración.

Hari Krishna
fuente
1

Para contar la larga historia, este campo se utiliza para verificar si los datos serializados se pueden deserializar correctamente. La serialización y la deserialización a menudo se realizan mediante diferentes copias del programa, por ejemplo, el servidor convierte el objeto en cadena y el cliente convierte la cadena recibida en objeto. Este campo indica que ambos operan con la misma idea sobre qué es este objeto. Este campo ayuda cuando:

  • tiene muchas copias diferentes de su programa en diferentes lugares (como 1 servidor y 100 clientes). Si cambia su objeto, modifica su número de versión y olvida actualizar uno de estos clientes, sabrá que no es capaz de deserializar

  • ha almacenado sus datos en algún archivo y luego intenta abrirlo con una versión actualizada de su programa con un objeto modificado; sabrá que este archivo no es compatible si mantiene su versión correcta

Cuando es importante

Lo más obvio: si agrega algunos campos a su objeto, las versiones anteriores no podrán usarlos porque no tienen estos campos en su estructura de objetos.

Menos obvio: cuando deserializa el objeto, los campos que no están presentes en la cadena se mantendrán como NULL. Si ha eliminado el campo de su objeto, las versiones anteriores mantendrán este campo como siempre NULL, lo que puede conducir a un mal comportamiento si las versiones anteriores dependen de datos en este campo (de todos modos, lo ha creado para algo, no solo por diversión :-))

Menos obvio: a veces cambias la idea que pones en el significado de algún campo. Por ejemplo, cuando tienes 12 años te refieres a "bicicleta" debajo de "bicicleta", pero cuando tienes 18 te refieres a "motocicleta" - si tus amigos te invitan a "andar en bicicleta por la ciudad" y serás el único que llegó en bicicleta, comprenderá lo importante que es mantener el mismo significado en todos los campos :-)

Stanislav Orlov
fuente
1

En primer lugar para responder a su pregunta, cuando no declaramos SerialVersionUID en nuestra clase, el tiempo de ejecución de Java lo genera para nosotros, pero ese proceso es sensible a muchos metadatos de clase, incluidos el número de campos, el tipo de campos, el modificador de acceso de los campos, la interfaz implementada por clase, etc. Por lo tanto, se recomienda declararlo nosotros mismos y Eclipse le advierte sobre lo mismo.

Serialización: a menudo trabajamos con objetos importantes cuyo estado (datos en las variables del objeto) es tan importante que no podemos arriesgarnos a perderlo debido a fallas de energía / sistema (o) fallas de red en caso de enviar el estado del objeto a otro máquina. La solución para este problema se llama "Persistencia", que simplemente significa persistir (mantener / guardar) los datos. La serialización es una de las muchas otras formas de lograr la persistencia (guardando datos en el disco / memoria). Al guardar el estado del objeto, es importante crear una identidad para el objeto, para poder leerlo correctamente (deserialización). Esta identificación única es ID es SerialVersionUID.

Shanks D Shiva
fuente
0

¿Qué es SerialVersionUID? Respuesta: Digamos que hay dos personas, una de HQ y otra de ODC, ambas realizarán serialización y deserialización respectivamente. En este caso, para autenticar que el receptor que está en ODC es la persona autenticada, JVM crea una ID única que se conoce como SerialVersionUID.

Aquí hay una buena explicación basada en el escenario,

¿Por qué SerialVersionUID?

Serialización : en el momento de la serialización, con cada objeto remitente, JVM guardará un identificador único. JVM es responsable de generar esa ID única basada en el archivo .class correspondiente que está presente en el sistema emisor.

Deserialización : en el momento de la deserialización, JVM del lado del receptor comparará la ID única asociada con el Objeto con la ID única de clase local, es decir, JVM también creará una ID única basada en el archivo .class correspondiente que está presente en el sistema receptor. Si ambas ID únicas coinciden, solo se realizará la deserialización. De lo contrario, obtendremos Runtime Exception diciendo InvalidClassException. Este identificador único no es más que SerialVersionUID

tenedor
fuente