Qual'è la differenza tra Serializable
e Externalizable
in Java?
Qual'è la differenza tra Serializable
e Externalizable
in Java?
Risposte:
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.Externalizable
stata fornita l' interfaccia, che è simile java.io.Serializable
ma con meccanismi scritti per eseguire le funzioni di marshalling e unmarshalling (è necessario implementare readExternal
e writeExternal
metodi 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 Externalizable
con 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
/ readExternal
metodi per tenerne conto.
In sintesi, Externalizable
è una reliquia di Java 1.1 giorni. Non ce n'è più bisogno.
Externalizable
aiuta molto .
Externalizable
mi 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.
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.
Differenze chiave tra Serializable
eExternalizable
Serializable
è l'interfaccia marker senza alcun metodo. Externalizable
l'interfaccia contiene due metodi: writeExternal()
e readExternal()
.Serializable
interfaccia. Verrà avviato il processo di serializzazione definito dal programmatore per le classi che implementano l' Externalizable
interfaccia.Externalizable
interfaccia. Puoi supportare diverse versioni del tuo oggetto. Se implementate Externalizable
, è vostra responsabilità serializzare la super
classeSerializable
usa la riflessione per costruire un oggetto e non richiede alcun costruttore arg. Ma Externalizable
richiede un costruttore pubblico senza argomenti.Fare riferimento al blog di Hitesh Garg
per maggiori dettagli.
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.
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.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 {};
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
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;
}
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 ObjectOutput
essere 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
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.
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.
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.
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.
Fondamentalmente, Serializable
è un'interfaccia marker che implica che una classe è sicura per la serializzazione e la JVM determina come è serializzata. Externalizable
contiene 2 metodi readExternal
e writeExternal
. Externalizable
consente all'implementatore di decidere come serializzare un oggetto, dove come Serializable
serializza gli oggetti nel modo predefinito.
Alcune differenze:
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.
Nella serializzazione se l'utente desidera saltare determinate proprietà da serializzare, deve contrassegnarle come transitorie, viceversa non è necessario per l'esternalizzazione.
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.