¿Por qué generar un serialVersionUID largo en lugar de un simple 1L?


210

Cuando la clase implementa Serializable en Eclipse, tengo dos opciones: agregar predeterminado serialVersionUID(1L)o generado serialVersionUID(3567653491060394677L). Creo que el primero es más genial, pero muchas veces vi gente usando la segunda opción. ¿Hay alguna razón para generar long serialVersionUID?


49
¿Cómo es ese duplicado exacto? No pregunto por qué generarlo en absoluto, sino por qué generar un serialVersionUID largo.
IAdapter

13
Cuando Jon Skeet usa serialVersionUID, usa 0L: stackoverflow.com/questions/605828/… ;)
Hanno Fietz

8
@HannoFietz: La oración exacta es: "Por simplicidad, sugiero comenzar con 0 y aumentarlo en 1 cada vez que lo necesite". Entonces, parece que 0Lsolo lo usa inicialmente.
O Mapper

77
@ORMapper: ¿Estás insinuando que Jon Skeet alguna vez necesita regresar y actualizar el código que ha escrito? Incluso hasta el punto de incompatibilidad estructural. ¡Jadear! ¡Herejía!
Thilo

Respuestas:



69

El propósito del UID de la versión de serialización es hacer un seguimiento de las diferentes versiones de una clase para realizar una serialización válida de objetos.

La idea es generar una ID que sea única para una determinada versión de una clase, que luego se cambia cuando se agregan nuevos detalles a la clase, como un nuevo campo, que afectaría la estructura del objeto serializado.

Siempre usando la misma ID, como por ejemplo 1L, en el futuro, si se cambia la definición de clase que causa cambios en la estructura del objeto serializado, habrá una buena posibilidad de que surjan problemas al intentar deserializar un objeto.

Si se omite la ID, Java realmente calculará la ID para usted en función de los campos del objeto, pero creo que es un proceso costoso, por lo que proporcionar uno manualmente mejorará el rendimiento.

Aquí hay un par de enlaces a artículos que analizan la serialización y el control de versiones de las clases:


50
La idea detrás del uso de 1L es que la incremente cada vez que cambie las propiedades o métodos de la clase.
Powerlord

39
No hay impacto en el rendimiento del tiempo de ejecución al permitir que el serialversionUID se genere automáticamente: se genera en tiempo de compilación mediante javac ... si descompila el código de bytes de la clase, en realidad verá la variable estáticamente en el código de bytes.
Jared

11
Una nota más: al administrar el número explícitamente, puede decidir cuándo considera que las versiones de una clase son "compatibles", en lugar de requerir que la definición de la clase sea exactamente la misma.
Scott Stanchfield

23
@ Jared De acuerdo con el Artículo 75 en Java efectivo de Josh Bloch: 2da edición: "declare un UID de versión serial explícito en cada clase serializable que escriba ... Si no se proporciona un UID de versión serial, se requiere un cálculo costoso para generar uno en tiempo de ejecución ".
Colin K

12
@coobird Esta parece ser la razón principal por la que no se recomienda el serialVersionUID predeterminado. Note - It is strongly recommended that all serializable classes explicitly declare serialVersionUID values, since the default serialVersionUID computation is highly sensitive to class details that may vary depending on compiler implementations, and can thus result in unexpected serialVersionUID conflicts during deserialization, causing deserialization to fail. El comentario anterior se tomó de Java Object Serialization Specification versión 6.0
user624558

20

La razón principal para la generada sería hacerla compatible con una versión existente de la clase que ya tiene copias persistentes.


1
OK pero lo mismo será si siempre tengo 1L. Todo será compatible incluso si realizo algún cambio.
grep

@grep intente renombrar un campo y luego vea qué sucede.
Trejkaz

1
@grep el punto es que si tiene una clase que omitió el serialVersionUID antes, habría obtenido el generado automáticamente. Entonces, ahora desea comenzar a configurarlo explícitamente, establecerlo en 1L lo haría incompatible con la clase existente, mientras que el uso del valor generado lo mantiene compatible.
marc82ch

15

El valor predeterminado "largo" de serialVersionUIDes el valor predeterminado definido por la Especificación de serialización de Java , calculado a partir del comportamiento de serialización predeterminado.

Entonces, si agrega el número de versión predeterminado, su clase se (des) serializará más rápido siempre que nada haya cambiado estructuralmente, pero tendrá que tener cuidado de que si cambia la clase (agregar / eliminar campos) también actualice el número de serie.

Si no tiene que ser compatible con las secuencias de bits existentes, simplemente puede colocar 1Lallí e incrementar la versión según sea necesario cuando algo cambie. Es decir, cuando la versión de serialización predeterminada de la clase modificada sería diferente de la versión predeterminada de la clase anterior.


11

Absolutamente debe crear un serialVersionUID cada vez que defina una clase que implemente java.io.Serializable. Si no lo hace, se creará uno para usted automáticamente, pero esto es malo. El serialVersionUID generado automáticamente se basa en las firmas de método de su clase, por lo que si cambia su clase en el futuro para agregar un método (por ejemplo), la deserialización de las versiones "antiguas" de la clase fallará. Esto es lo que puede pasar:

  1. Cree la primera versión de su clase, sin definir el serialVersionUID.
  2. Serialice una instancia de su clase en una tienda persistente; un serialVersionUID se genera automáticamente para usted.
  3. Modifique su clase para agregar un nuevo método y vuelva a implementar su aplicación.
  4. Intente deserializar la instancia que se serializó en el paso 2, pero ahora falla (cuando debería tener éxito), porque tiene un ID de serie diferente generado automáticamente.

1
De hecho, deserializar versiones antiguas de la clase debería fallar porque ya no son lo mismo. Sugiere generar serialVersionUID por sí mismo para evitar fallas de (des) serialización cuando cambia la firma de la clase. Aunque su sugerencia es apropiada, su explicación de su propósito es simplemente errónea y engañosa. Sería prudente modificar su respuesta.
mostruash

6

Si no especifica un serialVersionUID, Java crea uno sobre la marcha. El serialVersionUID generado es ese número. Si cambia algo en su clase que realmente no hace que su clase sea incompatible con versiones anteriores serializadas pero cambia el hash, entonces debe usar el número de serie serialVersionUID generado (o el número "esperado" del mensaje de error) . De lo contrario, si realiza un seguimiento de todo usted mismo, 0, 1, 2 ... es mejor.


Querías decir ==> 1. Si quieres que diferentes cambios de clases sean compatibles, usa el generado. 2. Si desea que las diferentes versiones de clases sean incompatibles, use una predeterminada y sea prudente en incrementos. ¿Lo entendí correctamente?
JavaDeveloper

4

Cuando usa serialVersionUID (1L) en lugar de generar serialVersionUID (3567653491060394677L) está diciendo algo.

Está diciendo que está 100% seguro de que ningún sistema tocará esta clase que tenga una versión serializada incompatible de esta clase con un número de versión 1.

Si puede pensar en alguna excusa para que su historial de versiones en serie sea desconocido, eso podría ser difícil de decir con confianza. En su vida, muchas personas mantendrán una clase exitosa, vivirán en muchos proyectos y residirán en muchos sistemas.

Puedes agonizar por eso. O puedes jugar a la lotería con la esperanza de perder. Si genera la versión, tiene una pequeña posibilidad de que las cosas salgan mal. Si asumes "Hey, apuesto a que nadie usó 1 todavía", tus probabilidades son mayores que pequeñas. Precisamente porque todos pensamos que 0 y 1 son geniales, tiene mayores probabilidades de alcanzarlos.

-

Cuando genera serialVersionUID (3567653491060394677L) en lugar de usar serialVersionUID (1L), está diciendo algo.

Está diciendo que las personas pueden haber creado o generado manualmente otros números de versión a lo largo del historial de esta clase y no le importa porque Longs está volviendo grandes números.

De cualquier manera, a menos que conozca perfectamente el historial de números de versión utilizados al serializar la clase en todo el universo de dónde ha existido o existirá alguna vez, se está arriesgando. Si tienes tiempo para asegurarte al 100% de que 1 es AOK, hazlo. Si eso es demasiado trabajo, adelante y genera el número a ciegas. Es más probable que ganes la lotería que que salga mal. Si es así, avísame y te compraré una cerveza.

Con toda esta charla sobre jugar a la lotería, puedo haberte dado la impresión de que serialVersionUID se genera aleatoriamente. De hecho, siempre y cuando el rango de números se distribuya uniformemente sobre cada valor posible de un Long, estaría bien. Sin embargo, en realidad se hace de esta manera:

http://docs.oracle.com/javase/6/docs/platform/serialization/spec/class.html#4100

La única diferencia que obtienes con eso es que no necesitas una fuente aleatoria. Estás usando los cambios en la clase misma para cambiar el resultado. Pero de acuerdo con el principio del casillero, todavía existe la posibilidad de que pueda salir mal y tener una colisión. Es increíblemente improbable. Buena suerte sacando una cerveza de mí.

Sin embargo, incluso si la clase solo vivirá en un sistema y una base de código, pensar que incrementar el número a mano te da cero posibilidades de colisiones solo significa que no entiendes a los humanos. :)


Si un "sistema" toca la clase, es decir, cambia la clase de manera que la serialización se vuelve incompatible, entonces la pregunta es si ese sistema también cambiará el serialVersionUID. No creo que las probabilidades sean menores, ya que recordará cambiarlo cuando sea largo. Creo que más bien lo contrario es cierto, si el número es más fácil de recordar, los cambios son más altos y noto que accidentalmente no lo cambié.
Reto Gmür

2
Esto es falso! Cuando genera serialVersionUID, y declara ese valor en su código fuente, en lugar de 1L o nada, realmente está diciendo: Quiero una colisión posiblemente no detectada en el futuro con efectos indefinidos, y no quiero que Java o ningún humano evite esto . Java es paranoico, pero obediente. Los humanos generalmente no se meten con grandes números. De esta manera, cuando la clase cambia, Java aún puede des serializar versiones antiguas incompatibles de la misma. MwoaHaHa ...;)
Superole

1

Bueno, serialVersionUID es una excepción a la regla de que "los campos estáticos no se serializan". ObjectOutputStream escribe cada vez que el valor de serialVersionUID en la secuencia de salida. ObjectInputStream lo lee de nuevo y si el valor leído de la secuencia no está de acuerdo con el valor serialVersionUID en la versión actual de la clase, arroja la InvalidClassException. Además, si no hay un serialVersionUID declarado oficialmente en la clase para ser serializado, el compilador lo agrega automáticamente con un valor generado basado en los campos declarados en la clase.


0

Porque en muchos casos la identificación predeterminada no es única. entonces creamos id para hacer un concepto único.


¿Puedes editar tu respuesta para desarrollarla más? Esto parece un comentario aquí. Gracias.
Gray

0

Para agregar a la respuesta de @David Schmitts, como regla general, siempre usaría el 1L predeterminado fuera de convención. Solo tuve que regresar y cambiar algunos de ellos algunas veces, pero lo sabía cuando hice el cambio y actualicé el número predeterminado por uno cada vez.

En mi compañía actual requieren el número generado automáticamente, así que lo uso para convenciones, pero prefiero el predeterminado. Mi opinión es que, si no es una convención donde trabajas, usa el predeterminado, a menos que pienses que cambiarás constantemente la estructura de tus clases serializadas por alguna razón.


0

El propósito del UID de la versión de serialización es hacer un seguimiento de las diferentes versiones de una clase para realizar una serialización válida de objetos.

La idea es generar una ID que sea única para una determinada versión de una clase, que luego se cambia cuando se agregan nuevos detalles a la clase, como un nuevo campo, que afectaría la estructura del objeto serializado.

Una explicación simple:

¿Estás serializando datos?

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

¿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).

¿Es esta la primera versión?

Si es así, establezca serialVersionUID = 1L.

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

Ahora debe preocuparse por serialVersionUID, y 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.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.