2010-05-25 7 views
40

Ho una classe che definisce un tipo di valore immutabile che ora ho bisogno di serializzare. L'immutabilità deriva dai campi finali impostati nel costruttore. Ho provato a serializzare, e funziona (sorprendentemente?) - ma non ho idea di come.In che modo la serializzazione java deserializza i campi finali quando non è specificato alcun costruttore predefinito?

Ecco un esempio della classe

public class MyValueType implements Serializable 
{ 
    private final int value; 

    private transient int derivedValue; 

    public MyValueType(int value) 
    { 
     this.value = value; 
     this.derivedValue = derivedValue(value); 
    } 

    // getters etc... 
} 

Dato che la classe non ha un costruttore senza arg, come può essere istanziato e il set campo finale?

(Una parentesi -. Ho notato questa classe soprattutto perché idea non stava generando un'ispezione "no serialVersionUID" avvertimento per questa classe, ma gli avvisi generati con successo per altre classi che ho appena fatto serializzabile)

+0

Domanda bonus: come è serializzata una istanza di una classe * anonima *? – ewernli

+0

Un'ipotesi: è serializzabile solo se uno dei suoi supertipi è serializzabile e la classe che lo contiene è serializzabile? Ciò sembra avere senso, ma nessuno può indovinare cosa c'è nel JLS. – mdma

+0

Non è "nessuno indovina cosa c'è nella JLS". È lì per essere letto da chiunque. – EJP

risposta

34

La deserializzazione è implementata dalla JVM su un livello inferiore ai costrutti del linguaggio di base. Nello specifico, non chiama nessun costruttore.

+2

In realtà chiama il costruttore senza parametri. Solo le classi con tale costruttore sono serializzabili. – Oak

+11

@Oak. Non vero. È possibile serializzare una classe senza un costruttore predefinito. –

+2

@Alexander: il mio male. L'ho confuso con il fatto che le superclassi non serializzabili per i tipi serializzabili devono avere un simile costruttore. – Oak

14

Dato che la classe non ha un costruttore no arg, come può essere istanziata e il campo finale impostato?

Si verifica una brutta magia nera. C'è una backdoor nella JVM che consente di creare un oggetto senza invocare alcun costruttore. I campi del nuovo oggetto vengono inizialmente inizializzati con i loro valori predefiniti (false, 0, null, ecc.) E quindi il codice di deserializzazione degli oggetti popola i campi con i valori del flusso di oggetti.

(Ora che Java è open source, è possibile leggere il codice che fa questo ... e piangete!)

+4

In effetti, non hai nemmeno bisogno di brutte magie nere, di riflessioni (ad esempio una brutta magia grigia), può essere usato per impostare i campi finali. Funziona finché i valori vengono impostati prima che i campi vengano letti la prima volta. – gustafc

+4

@gustafc - ma la magia nera è necessaria per creare l'oggetto senza eseguire alcun costrutto. –

11

Sia Michael e Stephen ti ha dato una risposta eccellente, voglio solo mettere in guardia voi circa transient campi .

Se il valore predefinito (null per i riferimenti, 0 per le primitive) non è accettabile per loro dopo la deserializzazione, quindi è necessario fornire la versione di readObject e inizializzarla lì.

private void readObject (
      final ObjectInputStream s 
     ) throws 
      ClassNotFoundException, 
      IOException 
    { 
     s.defaultReadObject(); 

     // derivedValue is still 0 
     this.derivedValue = derivedValue(value); 
    } 
+0

Grande commento, grazie per l'esempio. –

Problemi correlati