Qual'è la differenza tra Serializable ed Externalizable in Java?


Risposte:


267

Per aggiungere alle altre risposte, implementando java.io.Serializable, ottieni la capacità di serializzazione "automatica" per gli oggetti della tua classe. Non è necessario implementare altre logiche, funzionerà e basta. Il runtime Java utilizzerà la riflessione per capire come eseguire il marshalling e il ripristino dei tuoi oggetti.

Nella versione precedente di Java, la riflessione era molto lenta e quindi serializzare grafici di oggetti di grandi dimensioni (ad esempio nelle applicazioni RMI client-server) rappresentava un problema di prestazioni. Per gestire questa situazione, è java.io.Externalizablestata fornita l' interfaccia, che è simile java.io.Serializablema con meccanismi scritti per eseguire le funzioni di marshalling e unmarshalling (è necessario implementare readExternale writeExternalmetodi sulla propria classe). Questo ti dà i mezzi per aggirare il collo di bottiglia delle prestazioni di riflessione.

Nelle recenti versioni di Java (1.3 in poi, certamente) le prestazioni della riflessione sono notevolmente migliori rispetto a prima, e quindi questo è molto meno un problema. Ho il sospetto che ti sarebbe difficile trovare un vantaggio significativo Externalizablecon una JVM moderna.

Inoltre, il meccanismo di serializzazione Java incorporato non è l'unico, è possibile ottenere sostituzioni di terze parti, come la serializzazione JBoss, che è notevolmente più veloce ed è una sostituzione drop-in per impostazione predefinita.

Un grande svantaggio di Externalizableè che devi mantenere questa logica tu stesso - se aggiungi, rimuovi o cambi un campo nella tua classe, devi cambiare i tuoi writeExternal/ readExternalmetodi per tenerne conto.

In sintesi, Externalizableè una reliquia di Java 1.1 giorni. Non ce n'è più bisogno.


61
Non secondo questi parametri di riferimento: [ code.google.com/p/thrift-protobuf-compare/wiki/Benchmarking] , la serializzazione manuale (usando esternalizzabile) è molto, molto più veloce dell'uso della serializzazione predefinita di java. Se la velocità conta per il tuo lavoro, scrivi sicuramente il tuo serializzatore.
volni,

6
aggiornamento al nuovo link github.com/eishay/jvm-serializers/wiki suggerito da @Jack
noquery

3
Il "java-manuale" in github.com/eishay/jvm-serializers/wiki non non usa Externalizable, che implicherebbe utilizzando ObjectOutputStream. Vedere github.com/eishay/jvm-serializers/wiki/ToolBehavior per un collegamento al codice. Al contrario, è un codice scritto a mano che utilizza DataOutputStream, quindi non soffre delle cose che rallentano ObjectOutputStream (come tenere traccia delle istanze degli oggetti e supportare i cicli degli oggetti).
Esko Luontola,

7
Dover mantenere la logica da solo è solo un aspetto negativo se la classe non cambia mai e non devi mai leggere versioni persistenti di vecchi dati. Se vuoi che la libertà cambi la tua classe senza dover scrivere codice infernale per deserializzare le sue vecchie versioni, Externalizableaiuta molto .
Tim Boudreau,

2
Ho appena dovuto scrivere una raccolta personalizzata e devo dire che Externalizablemi si adatta molto meglio, poiché non voglio produrre array con spazi vuoti o oggetti segnaposto, inoltre con l'interfaccia esplicita puoi gestire l'ereditarietà, il che significa che il mio sub sincronizzato -class può facilmente aggiungere il blocco attorno alla chiamata a writeExternal(). Quindi sì, Externalizable è ancora molto rilevante, sicuramente per oggetti grandi o complessi.
Haravikk,

37

La serializzazione fornisce funzionalità predefinite per archiviare e successivamente ricreare l'oggetto. Utilizza un formato dettagliato per definire l'intero grafico degli oggetti da archiviare, ad esempio supponiamo di avere un Elenco collegato e di scrivere codice come di seguito, quindi la serializzazione predefinita rileverà tutti gli oggetti collegati e serializzerà. Nella serializzazione predefinita l'oggetto viene costruito interamente dai suoi bit memorizzati, senza chiamate del costruttore.

  ObjectOutputStream oos = new ObjectOutputStream(
                new FileOutputStream("/Users/Desktop/files/temp.txt"));
        oos.writeObject(linkedListHead); //writing head of linked list
        oos.close();

Ma se si desidera la serializzazione limitata o non si desidera serializzare una parte del proprio oggetto, utilizzare Externalizable. L'interfaccia Externalizable estende l'interfaccia Serializable e aggiunge due metodi, writeExternal () e readExternal (). Questi vengono chiamati automaticamente durante la serializzazione o la deserializzazione. Mentre lavoriamo con Externalizable dovremmo ricordare che il costruttore predefinito dovrebbe essere pubblico, altrimenti il ​​codice genererà un'eccezione. Si prega di seguire il seguente codice:

public class MyExternalizable implements Externalizable
{

private String userName;
private String passWord;
private Integer roll;

public MyExternalizable()
{

}

public MyExternalizable(String userName, String passWord, Integer roll)
{
    this.userName = userName;
    this.passWord = passWord;
    this.roll = roll;
}

@Override
public void writeExternal(ObjectOutput oo) throws IOException 
{
    oo.writeObject(userName);
    oo.writeObject(roll);
}

@Override
public void readExternal(ObjectInput oi) throws IOException, ClassNotFoundException 
{
    userName = (String)oi.readObject();
    roll = (Integer)oi.readObject();
}

public String toString()
{
    StringBuilder b = new StringBuilder();
    b.append("userName: ");
    b.append(userName);
    b.append("  passWord: ");
    b.append(passWord);
    b.append("  roll: ");
    b.append(roll);

    return b.toString();
}
public static void main(String[] args)
{
    try
    {
        MyExternalizable m  = new MyExternalizable("nikki", "student001", 20);
        System.out.println(m.toString());
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("/Users/Desktop/files/temp1.txt"));
        oos.writeObject(m);
        oos.close();

        System.out.println("***********************************************************************");
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("/Users/Desktop/files/temp1.txt"));
        MyExternalizable mm = (MyExternalizable)ois.readObject();
        mm.toString();
        System.out.println(mm.toString());
    } 
    catch (ClassNotFoundException ex) 
    {
        Logger.getLogger(MyExternalizable.class.getName()).log(Level.SEVERE, null, ex);
    }
    catch(IOException ex)
    {
        Logger.getLogger(MyExternalizable.class.getName()).log(Level.SEVERE, null, ex);
    }
}
}

Qui se commentate il costruttore predefinito, il codice genererà un'eccezione al di sotto:

 java.io.InvalidClassException: javaserialization.MyExternalizable;     
 javaserialization.MyExternalizable; no valid constructor.

Possiamo osservare che poiché la password è un'informazione sensibile, quindi non la sto serializzando nel metodo writeExternal (ObjectOutput oo) e non sto impostando il valore della stessa in readExternal (ObjectInput oi). Questa è la flessibilità fornita da Externalizable.

L'output del codice sopra è come sotto:

userName: nikki  passWord: student001  roll: 20
***********************************************************************
userName: nikki  passWord: null  roll: 20

Possiamo osservare come non stiamo impostando il valore di passWord, quindi è nullo.

Lo stesso si può ottenere anche dichiarando il campo password come transitorio.

private transient String passWord;

Spero che sia d'aiuto. Mi scuso se ho fatto degli errori. Grazie.


22

Differenze chiave tra SerializableeExternalizable

  1. Interfaccia marker : Serializableè l'interfaccia marker senza alcun metodo. Externalizablel'interfaccia contiene due metodi: writeExternal()e readExternal().
  2. Processo di serializzazione : verrà avviato il processo di serializzazione predefinito per le classi che implementano l' Serializableinterfaccia. Verrà avviato il processo di serializzazione definito dal programmatore per le classi che implementano l' Externalizableinterfaccia.
  3. Manutenzione : modifiche incompatibili potrebbero interrompere la serializzazione.
  4. Compatibilità e controllo all'indietro : se devi supportare più versioni, puoi avere il pieno controllo con l' Externalizableinterfaccia. Puoi supportare diverse versioni del tuo oggetto. Se implementate Externalizable, è vostra responsabilità serializzare la superclasse
  5. costruttore No-arg pubblico : Serializableusa la riflessione per costruire un oggetto e non richiede alcun costruttore arg. Ma Externalizablerichiede un costruttore pubblico senza argomenti.

Fare riferimento al blog di Hitesh Gargper maggiori dettagli.


1
(3) non è corretto. Esiste un vasto repertorio di modifiche che è possibile apportare a una classe senza interrompere la deserializzazione di oggetti esistenti e l'aggiunta di membri serializzabili è sicuramente uno di questi. Eliminarli è un altro. Vedere il capitolo Controllo delle versioni degli oggetti nella Specifica di serializzazione degli oggetti Java.
Marchese di Lorne,

1
Frase riformulata.
Ravindra babu,

Grazie per aver condiviso il collegamento alle specifiche di serializzazione.
JL_SO,

21

La serializzazione utilizza determinati comportamenti predefiniti per archiviare e successivamente ricreare l'oggetto. È possibile specificare in quale ordine o come gestire i riferimenti e le strutture di dati complessi, ma alla fine si tratta di utilizzare il comportamento predefinito per ciascun campo di dati primitivo.

L'esternalizzazione viene utilizzata nei rari casi in cui si desidera veramente archiviare e ricostruire il proprio oggetto in un modo completamente diverso e senza utilizzare i meccanismi di serializzazione predefiniti per i campi di dati. Ad esempio, immagina di avere il tuo schema di codifica e compressione unico.


5
Abbiamo usato Externalizable per grandi raccolte di "ID selezionati" - era esternalizzazione molto più efficiente come più o meno un conteggio e una matrice di ints primitivi, rispetto alla serializzazione predefinita. È un caso d'uso molto semplice, niente di "speciale" o "unico".
Thomas W,

9

La serializzazione degli oggetti utilizza le interfacce serializzabili ed esternalizzabili. Un oggetto Java è solo serializzabile. se una classe o una delle sue superclasse implementa l'interfaccia java.io.Serializable o la sua interfaccia secondaria, java.io.Externalizable. La maggior parte della classe java è serializzabile .

  • NotSerializableException: packageName.ClassName«Per partecipare a un oggetto classe nel processo di serializzazione, la classe deve implementare l'interfaccia serializzabile o esternalizzabile.

inserisci qui la descrizione dell'immagine


Interfaccia serializzabile

La serializzazione degli oggetti produce un flusso con informazioni sulle classi Java per gli oggetti che vengono salvati. Per gli oggetti serializzabili, vengono conservate informazioni sufficienti per ripristinare tali oggetti anche se è presente una versione diversa (ma compatibile) dell'implementazione della classe. L'interfaccia serializzabile è definita per identificare le classi che implementano il protocollo serializzabile:

package java.io;

public interface Serializable {};
  • L'interfaccia di serializzazione non ha metodi o campi e serve solo per identificare la semantica di essere serializzabile. Per serializzare / deserializzare una classe, possiamo usare i metodi writeObject e readObject predefiniti (oppure) possiamo sovrascrivere i metodi writeObject e readObject da una classe.
  • JVM avrà il controllo completo nella serializzazione dell'oggetto. utilizzare una parola chiave temporanea per impedire la serializzazione del membro dati.
  • Qui gli oggetti serializzabili vengono ricostruiti direttamente dallo stream senza essere eseguiti
  • InvalidClassException«Nel processo di deserializzazione, se il valore serialVersionUID della classe locale è diverso dalla classe del mittente corrispondente. quindi il risultato è in conflitto come java.io.InvalidClassException: com.github.objects.User; local class incompatible: stream classdesc serialVersionUID = 5081877, local class serialVersionUID = 50818771
  • I valori dei campi non transitori e non statici della classe vengono serializzati.

Interfaccia esternalizzabile

Per oggetti Externalizable, solo l'identità della classe dell'oggetto viene salvata dal contenitore; la classe deve salvare e ripristinare i contenuti. L'interfaccia Externalizable è definita come segue:

package java.io;

public interface Externalizable extends Serializable
{
    public void writeExternal(ObjectOutput out)
        throws IOException;

    public void readExternal(ObjectInput in)
        throws IOException, java.lang.ClassNotFoundException;
}
  • L'interfaccia Externalizable ha due metodi, un oggetto esternalizzabile deve implementare i metodi writeExternal e readExternal per salvare / ripristinare lo stato di un oggetto.
  • Il programmatore deve occuparsi degli oggetti da serializzare. Come programmatore si occupa della serializzazione Quindi, qui la parola chiave transitoria non limiterà alcun oggetto nel processo di serializzazione.
  • Quando viene ricostruito un oggetto Externalizable, viene creata un'istanza utilizzando il costruttore no-arg pubblico, quindi viene chiamato il metodo readExternal. Gli oggetti serializzabili vengono ripristinati leggendoli da un ObjectInputStream.
  • OptionalDataException«I campi DEVONO ESSERE NELLO STESSO ORDINE E TIPO come li abbiamo scritti. In caso di mancata corrispondenza del tipo dallo stream, viene generato OptionalDataException.

    @Override public void writeExternal(ObjectOutput out) throws IOException {
        out.writeInt( id );
        out.writeUTF( role );
        out.writeObject(address);
    }
    @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        this.id = in.readInt();
        this.address = (Address) in.readObject();
        this.role = in.readUTF();
    }
    
  • I campi di istanza della classe che ha scritto (esposto) per ObjectOutputessere serializzato.


Esempio « implementa serializzabile

class Role {
    String role;
}
class User extends Role implements Serializable {

    private static final long serialVersionUID = 5081877L;
    Integer id;
    Address address;

    public User() {
        System.out.println("Default Constructor get executed.");
    }
    public User( String role ) {
        this.role = role;
        System.out.println("Parametarised Constructor.");
    }
}

class Address implements Serializable {

    private static final long serialVersionUID = 5081877L;
    String country;
}

Esempio « implementa Externalizable

class User extends Role implements Externalizable {

    Integer id;
    Address address;
    // mandatory public no-arg constructor
    public User() {
        System.out.println("Default Constructor get executed.");
    }
    public User( String role ) {
        this.role = role;
        System.out.println("Parametarised Constructor.");
    }

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeInt( id );
        out.writeUTF( role );
        out.writeObject(address);
    }
    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        this.id = in.readInt();
        this.address = (Address) in.readObject();
        this.role = in.readUTF();
    }
}

Esempio

public class CustomClass_Serialization {
    static String serFilename = "D:/serializable_CustomClass.ser";

    public static void main(String[] args) throws IOException {
        Address add = new Address();
        add.country = "IND";

        User obj = new User("SE");
        obj.id = 7;
        obj.address = add;

        // Serialization
        objects_serialize(obj, serFilename);
        objects_deserialize(obj, serFilename);

        // Externalization
        objects_WriteRead_External(obj, serFilename);
    }

    public static void objects_serialize( User obj, String serFilename ) throws IOException{
        FileOutputStream fos = new FileOutputStream( new File( serFilename ) );
        ObjectOutputStream objectOut = new ObjectOutputStream( fos );

        // java.io.NotSerializableException: com.github.objects.Address
        objectOut.writeObject( obj );
        objectOut.flush();
        objectOut.close();
        fos.close();

        System.out.println("Data Stored in to a file");
    }
    public static void objects_deserialize( User obj, String serFilename ) throws IOException{
        try {
            FileInputStream fis = new FileInputStream( new File( serFilename ) );
            ObjectInputStream ois = new ObjectInputStream( fis );
            Object readObject;
            readObject = ois.readObject();
            String calssName = readObject.getClass().getName();
            System.out.println("Restoring Class Name : "+ calssName); // InvalidClassException

            User user = (User) readObject;
            System.out.format("Obj[Id:%d, Role:%s] \n", user.id, user.role);

            Address add = (Address) user.address;
            System.out.println("Inner Obj : "+ add.country );
            ois.close();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public static void objects_WriteRead_External( User obj, String serFilename ) throws IOException {
        FileOutputStream fos = new FileOutputStream(new File( serFilename ));
        ObjectOutputStream objectOut = new ObjectOutputStream( fos );

        obj.writeExternal( objectOut );
        objectOut.flush();

        fos.close();

        System.out.println("Data Stored in to a file");

        try {
            // create a new instance and read the assign the contents from stream.
            User user = new User();

            FileInputStream fis = new FileInputStream(new File( serFilename ));
            ObjectInputStream ois = new ObjectInputStream( fis );

            user.readExternal(ois);

            System.out.format("Obj[Id:%d, Role:%s] \n", user.id, user.role);

            Address add = (Address) user.address;
            System.out.println("Inner Obj : "+ add.country );
            ois.close();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

@vedere


7

L'interfaccia Externalizable non è stata effettivamente fornita per ottimizzare le prestazioni del processo di serializzazione! ma per fornire mezzi per implementare la tua elaborazione personalizzata e offrire il controllo completo sul formato e sui contenuti del flusso per un oggetto e i suoi super tipi!

Ne sono un esempio l'implementazione del remoting AMF (ActionScript Message Format) per trasferire oggetti di script di azioni native sulla rete.


7

https://docs.oracle.com/javase/8/docs/platform/serialization/spec/serialTOC.html

La serializzazione predefinita è in qualche modo dettagliata e presuppone lo scenario di utilizzo più ampio possibile dell'oggetto serializzato, e di conseguenza il formato predefinito (serializzabile) annota il flusso risultante con informazioni sulla classe dell'oggetto serializzato.

L'esternalizzazione fornisce al produttore del flusso di oggetti il ​​controllo completo sui metadati precisi della classe (se presenti) oltre l'identificazione minima richiesta della classe (ad esempio il suo nome). Ciò è chiaramente desiderabile in determinate situazioni, come ambienti chiusi, in cui il produttore del flusso di oggetti e il suo consumatore (che reifica l'oggetto dal flusso) sono abbinati e metadati aggiuntivi sulla classe non servono a nulla e degradano le prestazioni.

Inoltre (come sottolinea Uri) l'esternalizzazione fornisce anche il controllo completo sulla codifica dei dati nel flusso corrispondente ai tipi Java. Per un esempio (inventivo), potresti voler registrare il vero booleano come 'Y' e il falso come 'N'. L'esternalizzazione ti consente di farlo.


2

Quando si considerano le opzioni per migliorare le prestazioni, non dimenticare la serializzazione personalizzata. Puoi lasciare che Java faccia ciò che fa bene, o almeno abbastanza bene, gratuitamente e fornire supporto personalizzato per ciò che fa male. Questo di solito è molto meno codice del pieno supporto esternalizzabile.


2

Esistono così tante differenze tra Serializable ed Externalizable, ma quando confrontiamo la differenza tra Serializable personalizzato (override writeObject () e readObject ()) ed Externalizable, allora troviamo che l'implementazione personalizzata è strettamente legata alla classe ObjectOutputStream dove, come nel caso Externalizable, noi stessi fornire un'implementazione di ObjectOutput che può essere la classe ObjectOutputStream o potrebbe essere un po 'come org.apache.mina.filter.codec.serialization.ObjectSerializationOutputStream

In caso di interfaccia esternalizzabile

@Override
public void writeExternal(ObjectOutput out) throws IOException {
    out.writeUTF(key);
    out.writeUTF(value);
    out.writeObject(emp);
}

@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
    this.key = in.readUTF();
    this.value = in.readUTF();
    this.emp = (Employee) in.readObject();
}





**In case of Serializable interface**


        /* 
             We can comment below two method and use default serialization process as well
             Sequence of class attributes in read and write methods MUST BE same.
        // below will not work it will not work . 
        // Exception = java.io.StreamCorruptedException: invalid type code: 00\
              private void writeObject(java.io.ObjectOutput stream) 
              */
            private void writeObject(java.io.ObjectOutputStream Outstream)
                    throws IOException {

                System.out.println("from writeObject()");
                /*     We can define custom validation or business rules inside read/write methods.
 This way our validation methods will be automatically 
    called by JVM, immediately after default serialization 
    and deserialization process 
    happens.
                 checkTestInfo();
                */

                stream.writeUTF(name);
                stream.writeInt(age);
                stream.writeObject(salary);
                stream.writeObject(address);
            }

            private void readObject(java.io.ObjectInputStream Instream)
                    throws IOException, ClassNotFoundException {
                System.out.println("from readObject()");
                name = (String) stream.readUTF();
                age = stream.readInt();
                salary = (BigDecimal) stream.readObject();
                address = (Address) stream.readObject();
                // validateTestInfo();
            }

Ho aggiunto un codice di esempio per spiegare meglio. per favore controlla dentro / fuori la cassa dell'oggetto di Externalizable. Questi non sono legati direttamente a nessuna implementazione.
Dove come Outstream / Instream sono strettamente legati alle classi. Possiamo estendere ObjectOutputStream / ObjectInputStream ma sarà un po 'difficile da usare.


1
Potresti elaborarlo di più? Mentre lo leggo, non capisco cosa stai cercando di dire. Inoltre, se potessi formattare il testo con alcuni paragrafi ed esempi, questa potrebbe essere un'ottima risposta.
Shirkam,

0

Fondamentalmente, Serializableè un'interfaccia marker che implica che una classe è sicura per la serializzazione e la JVM determina come è serializzata. Externalizablecontiene 2 metodi readExternale writeExternal. Externalizableconsente all'implementatore di decidere come serializzare un oggetto, dove come Serializableserializza gli oggetti nel modo predefinito.


0

Alcune differenze:

  1. Per la serializzazione non è necessario il costruttore predefinito di quella classe perché Object perché JVM costruisce lo stesso con l'aiuto dell'API Reflection. Nel caso di un contraente di esternalizzazione senza arg è necessario, poiché il controllo è in mano a programmar e successivamente assegna i dati deserializzati all'oggetto tramite setter.

  2. Nella serializzazione se l'utente desidera saltare determinate proprietà da serializzare, deve contrassegnarle come transitorie, viceversa non è necessario per l'esternalizzazione.

  3. Quando è previsto il supporto di compatibilità con le versioni precedenti per qualsiasi classe, si consiglia di utilizzare Externalizable. La serializzazione supporta il persistere defaultObject e se la struttura dell'oggetto è rotta, causerà problemi durante la deserializzazione.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.