2009-05-21 20 views
59

In java, se una classe implementa Serializable ma è astratta, dovrebbe avere un serialVersionUID lungo dichiarato o le sottoclassi richiedono solo questo?Se una classe astratta ha un serialVersionUID

In questo caso è infatti intenzione che tutte le sottoclassi gestiscano la serializzazione poiché lo scopo del tipo deve essere utilizzato nelle chiamate RMI.

+3

Continuo a scrivere una risposta, realizzando che non lo so, anche se ho la sensazione. +1 per una domanda a cui non posso rispondere. –

risposta

40

Il serialVersionUID viene fornito per determinare la compatibilità tra un oggetto deseralizzato e la versione corrente della classe. Come tale, non è realmente necessario nella prima versione di una classe, o in questo caso, in una classe base astratta. Non avrai mai un'istanza di quella classe astratta per serializzare/deserializzare, quindi non ha bisogno di un serialVersionUID.

(Naturalmente, lo fa generare un avviso del compilatore, che si vuole sbarazzarsi di, giusto?)

Si scopre commento di James è corretta. Il serialVersionUID di una classe di base astratta fa viene propagato in sottoclassi. Alla luce di questo, è do necessario serialVersionUID nella classe base.

Il codice di prova:

import java.io.Serializable; 

public abstract class Base implements Serializable { 

    private int x = 0; 
    private int y = 0; 

    private static final long serialVersionUID = 1L; 

    public String toString() 
    { 
     return "Base X: " + x + ", Base Y: " + y; 
    } 
} 



import java.io.FileInputStream; 
import java.io.FileOutputStream; 
import java.io.ObjectInputStream; 
import java.io.ObjectOutputStream; 

public class Sub extends Base { 

    private int z = 0; 

    private static final long serialVersionUID = 1000L; 

    public String toString() 
    { 
     return super.toString() + ", Sub Z: " + z; 
    } 

    public static void main(String[] args) 
    { 
     Sub s1 = new Sub(); 
     System.out.println(s1.toString()); 

     // Serialize the object and save it to a file 
     try { 
      FileOutputStream fout = new FileOutputStream("object.dat"); 
      ObjectOutputStream oos = new ObjectOutputStream(fout); 
      oos.writeObject(s1); 
      oos.close(); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 

     Sub s2 = null; 
     // Load the file and deserialize the object 
     try { 
      FileInputStream fin = new FileInputStream("object.dat"); 
      ObjectInputStream ois = new ObjectInputStream(fin); 
      s2 = (Sub) ois.readObject(); 
      ois.close(); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 

     System.out.println(s2.toString()); 
    } 
} 

Eseguire il principale secondario una volta per farlo creare e salvare un oggetto. Quindi cambia il serialVersionUID nella classe Base, commenta le linee in main che salvano l'oggetto (quindi non lo salva di nuovo, vuoi solo caricare quello vecchio) ed eseguilo di nuovo. Questo si tradurrà in un'eccezione

java.io.InvalidClassException: Base; local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2 
+0

Risposta buona ... @SuppressWarnings ("seriale") sopprimerà il messaggio di avviso –

+1

@Ryan: Grazie, in genere trattiamo gli avvertimenti come errori e li gestisco direttamente. –

+1

... ma capisco che non tutti sono dogmatici come me, quindi il tuo commento è apprezzato. –

6

Sì, in generale, per la stessa ragione che qualsiasi altra classe ha bisogno di un ID seriale - per evitare di generarne uno per esso. Fondamentalmente ogni classe (non l'interfaccia) che implementa serializzabile dovrebbe definire l'id della versione seriale o si rischiano errori di serializzazione quando la stessa compilazione .class non si trova nelle JVM del server e del client.

Ci sono altre opzioni se stai cercando di fare qualcosa di stravagante. Non sono sicuro di cosa intendi con "è l'intenzione delle sottoclassi ...". Stai per scrivere metodi di serializzazione personalizzati (ad esempio writeObject, readObject)? Se è così ci sono altre opzioni per affrontare una super classe.

vedi: http://java.sun.com/javase/6/docs/api/java/io/Serializable.html

HTH Tom

2

In realtà, sottolineando di collegamento di Tom se mancante serialVersionID è in realtà calcolato serializzazione runtime cioè non durante la compilazione

Se una classe serializzabile non lo fa dichiarare esplicitamente un serialVersionUID , quindi il runtime di serializzazione calcolerà un valore serialVersionUID predefinito per tale cla ss in base a vari aspetti della classe ...

Ciò rende le cose ancora più complicate con versioni diverse di JRE.

0

Concettualmente, i dati serializzati simile a questa:

subClassData(className + version + fieldNames + fieldValues) 
parentClassData(className + version + fieldNames + fieldValues) 
... (up to the first parent, that implements Serializable) 

Quindi, quando si deserializzare, non corrispondente nella versione in una delle classi della gerarchia provoca la deserializzazione al sicuro. Niente viene memorizzato per le interfacce, quindi non è necessario specificarne la versione.

Risposta: sì, è necessario fornire serialVersionUID anche nella classe base astratta. Anche se non ha campi (className + version vengono memorizzati anche se non ci sono campi).

noti inoltre quanto segue:

  1. Se la classe non ha un campo, che si trova nei dati serializzati (un campo rimosso), viene ignorato.
  2. Se la classe ha un campo, che non è presente nei dati serializzati (un nuovo campo), è impostato su 0/falso/null (non sul valore predefinito come ci si aspetterebbe).
  3. se un campo cambia datatype, il valore deserializzato deve essere assegnabile al nuovo tipo. Per esempio. se si dispone di un campo Object con il valore String, la modifica del tipo di campo su String avrà esito positivo, ma non verrà modificata in Integer. Tuttavia, il cambio di campo da int a long non funziona, anche se è possibile assegnare il valore alla variabile long.
  4. Se la sottoclasse non estende più la classe genitore, che si estende nei dati serializzati, viene ignorata (come nel caso 1).
  5. Se una sottoclasse ora estende una classe, che non si trova nei dati serializzati, i campi della classe padre vengono ripristinati con valore 0/falso/null (come nel caso 2).

In parole semplici: è possibile riordinare i campi, aggiungere e rimuovere loro, anche cambiare la gerarchia delle classi. Non dovresti rinominare campi o classi (non fallirà, ma i valori non saranno deserializzati). Non è possibile modificare il tipo di campi con il tipo primitivo e modificare i campi del tipo di riferimento a condizione che il nuovo tipo sia assegnabile da tutti i valori.

Nota: se la classe base non implementa Serializable e fa solo la sottoclasse, i campi della classe base si comporterebbero come transient.

Problemi correlati