Domanda

In Java, se una classe implementa Serializable, ma è astratto, dovrebbe avere una lunga serialVersionUID dichiarato, o fare le sottoclassi solo richiede che?

In questo caso è infatti l'intenzione che tutte le sottoclassi trattano serializzazione come lo scopo del tipo deve essere utilizzato in RMI chiamate.

È stato utile?

Soluzione

Il serialVersionUID viene fornito per determinare la compatibilità tra un oggetto deseralized e la versione corrente della classe. In quanto tale, non è davvero necessario nella prima versione di una classe, o in questo caso, in una classe base astratta. Non avrete mai un'istanza di tale 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 base astratta non ottenere propagato alle sottoclassi. Alla luce di ciò, è do bisogno del 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 modificare il serialVersionUID nella classe Base, commentare le righe in principale che salvano l'oggetto (in modo che non salva ancora una volta, si vuole solo caricare il vecchio), ed eseguirlo nuovamente. Questo si tradurrà in un'eccezione

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

Altri suggerimenti

Sì, in generale, per la stessa ragione che qualsiasi altra classe ha bisogno di un ID seriale - per evitare di generarne uno per esso. In pratica ogni classe (non Interface) che implementa serializable dovrebbe definire la versione di serie id o si rischia di errori di de-serializzazione quando lo stesso .class compilazione non è nelle server e client JVM.

Ci sono altre opzioni, se si sta cercando di fare qualcosa di fantasia. Non sono sicuro di cosa si intende per "è intenzione delle classi secondarie ...". Hai intenzione di scrivere i metodi di serializzazione su misura (ad es. WriteObject, readObject)? Se è così ci sono altre opzioni per trattare con una classe di super-.

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

HTH Tom

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

  

Se una classe serializzabile non dichiara esplicitamente un   serialVersionUID, quindi il runtime serializzazione calcolerà un   il valore di default serialVersionUID per quella classe sulla base di vari aspetti   della classe ...

Questo rende le cose ancora più complicate avere diverse versioni di JRE.

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 a fallire. Nulla viene memorizzato per le interfacce, quindi non c'è bisogno di specificare la versione per loro.

Risposta: Sì, si ha bisogno di fornire serialVersionUID nella classe astratta di base pure. Anche se non ha i campi (className + version vengono memorizzate anche se non ci sono campi).

Si 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 / false / null (non al valore di default come ci si aspetterebbe).
  3. se un campo cambia tipo di dati, il valore deserializzati deve essere trasmesso al nuovo tipo. Per esempio. se si ha un campo Object con valore String, cambiando il tipo di campo String avrà successo, ma cambiando a Integer non lo faranno. Tuttavia, cambiando campo da int a long non funziona, anche se è possibile assegnare il valore int a long variabile.
  4. Se sottoclasse non estende la classe genitore, che si estende nei dati serializzati, viene ignorato (come nel caso 1).
  5. Se una sottoclasse estende ora una classe, che non si trova nei dati serializzati, campi della classe genitore vengono ripristinati con 0 / false / valore null (come nel caso 2).

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

. Nota: se la classe base non implementa Serializable e solo la sottoclasse non, quindi i campi dalla classe di base si comporterebbe come transient

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top