Was ist der Unterschied zwischen Serializable und Externalisierbar in Java?

Was ist der Unterschied zwischen Serializable und Externalizable in Java?

Um zu den anderen Antworten java.io.Serializable , erhalten Sie durch die Implementierung von java.io.Serializable “automatische” Serialisierungsfunktionen für Objekte Ihrer class. Keine Notwendigkeit, irgendeine andere Logik zu implementieren, es wird einfach funktionieren. Die Java-Laufzeitumgebung verwendet Reflektion, um herauszufinden, wie Sie Ihre Objekte marshalieren und entpacken.

In einer früheren Version von Java war die Reflektion sehr langsam und so war das Serialisieren großer Objektdiagramme (z. B. in Client-Server-RMI-Anwendungen) ein kleines performancesproblem. Um dieser Situation zu java.io.Externalizable , wurde die java.io.Externalizable Schnittstelle bereitgestellt, die wie java.io.Serializable aber mit speziell geschriebenen Mechanismen zum Ausführen der Marshalling- und Unmarshalling-functionen (Sie müssen die readExternal und writeExternal für Ihre class implementieren). Dies gibt Ihnen die Möglichkeit, den Engpass der reflectionsperformance zu umgehen.

In neueren Versionen von Java (ab 1.3) ist die performance der reflection deutlich besser als früher, und das ist viel weniger ein Problem. Ich vermute, Sie würden sich schwer tun, von Externalizable mit einer modernen JVM einen bedeutungsvollen Nutzen zu erzielen.

Der integrierte Java-Serialisierungsmechanismus ist nicht der einzige, Sie können Ersatz von Drittanbietern wie JBoss Serialization erhalten, die erheblich schneller ist und einen Drop-In-Ersatz für den Standard darstellt.

Ein großer Nachteil von Externalizable ist, dass Sie diese Logik selbst pflegen müssen – wenn Sie ein Feld in Ihrer class hinzufügen, entfernen oder ändern, müssen Sie Ihre writeExternal / readExternal Methoden readExternal , um sie zu berücksichtigen.

Zusammenfassend ist Externalizable ein Relikt der Java 1.1 Tage. Es gibt wirklich keinen Grund mehr dafür.

Die Serialisierung bietet Standardfunktionen zum Speichern und späteren Neuerstellen des Objekts. Es verwendet das ausführliche Format, um den gesamten Graph der zu speichernden Objekte zu definieren. Nehmen wir beispielsweise an, dass Sie eine LinkedList haben und wie unten beschrieben, dann findet die Standardserialisierung alle Objekte, die verlinkt sind und serialisieren. Bei der Standardserialisierung wird das Objekt vollständig aus seinen gespeicherten Bits ohne Konstruktoraufrufe konstruiert.

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

Wenn Sie jedoch eine eingeschränkte Serialisierung wünschen oder nicht möchten, dass ein Teil Ihres Objekts serialisiert wird, verwenden Sie Externalizable. Die Externalizable-Schnittstelle erweitert die Serializable-Schnittstelle und fügt zwei Methoden hinzu, writeExternal () und readExternal (). Diese werden automatisch während der Serialisierung oder Deserialisierung aufgerufen. Bei der Arbeit mit Externalizable sollten wir daran denken, dass der Standardkonstruktor öffentlich sein sollte, sonst wird der Code eine Ausnahme auslösen. Bitte folgen Sie dem untenstehenden Code:

 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); } } } 

Wenn Sie hier den Standardkonstruktor kommentieren, wird der Code unterhalb der Ausnahme übergeben:

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

Wir können beobachten, dass es sich bei dem Passwort um sensible Informationen handelt, so dass ich es nicht in der writeExternal (ObjectOutput oo) -Methode serialisiere und den Wert desselben nicht in readExternal (ObjectInput oi) setze. Das ist die Flexibilität, die von Externalizable bereitgestellt wird.

Die Ausgabe des obigen Codes ist wie folgt:

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

Wir können beobachten, wie wir den Wert von passWord nicht auf null setzen.

Das Gleiche kann auch erreicht werden, indem das Kennwortfeld als vorübergehend deklariert wird.

 private transient String passWord; 

Ich hoffe es hilft. Ich entschuldige mich, wenn ich Fehler gemacht habe. Vielen Dank.

Der Vollständigkeit halber schließt das transient Schlüsselwort auch die Lücke zwischen den beiden.

Wenn Sie nur einen Teil Ihres Objekts serialisieren möchten, setzen Sie bestimmte Felder als transient , markieren Sie sie als nicht persistent und implementieren Sie Serializable .

Die Serialisierung verwendet bestimmte Standardverhalten, um das Objekt zu speichern und später neu zu erstellen. Sie können angeben, in welcher Reihenfolge oder wie mit Verweisen und komplexen Datenstrukturen umgegangen wird, aber letztendlich kommt es darauf an, das Standardverhalten für jedes primitive Datenfeld zu verwenden.

Die Externalisierung wird in den seltenen Fällen verwendet, in denen Sie Ihr Objekt wirklich vollständig speichern und neu erstellen möchten, ohne die Standard-Serialisierungsmechanismen für Datenfelder zu verwenden. Stellen Sie sich zum Beispiel vor, Sie hätten Ihr eigenes Kodierungs- und Komprimierungsschema.

Hauptunterschiede zwischen Serializable und Externalizable

  1. Marker-Schnittstelle : Serializable ist Marker-Schnittstelle ohne Methoden. Externalizable Schnittstelle enthält zwei Methoden: writeExternal() und readExternal() .
  2. Serialisierungsprozess : Für classn, die eine Serializable Schnittstelle implementieren, wird der Standard-Serialisierungsprozess gestartet. Programmierdefinierter Serialisierungsprozess wird für classn eingeführt, die die Externalizable Schnittstelle implementieren.
  3. Wartung : Inkompatible Änderungen können die Serialisierung unterbrechen.
  4. Rückwärtskompatibilität und Kontrolle : Wenn Sie mehrere Versionen unterstützen müssen, haben Sie die volle Kontrolle über die Externalizable Schnittstelle. Sie können verschiedene Versionen Ihres Objekts unterstützen. Wenn Sie Externalizable implementieren, liegt es in Ihrer Verantwortung, die Superklasse zu serialisieren
  5. Öffentlicher No-arg-Konstruktor : Serializable verwendet Reflektion zum Konstruieren von Objekten und benötigt keinen Arg-Konstruktor. Aber Externalizable erfordert öffentlichen No-Arg-Konstruktor.

Weitere Informationen finden Sie im Blog von Hitesh Garg .

Die Externalizable-Schnittstelle wurde nicht zur Verfügung gestellt, um die performance des Serialisierungsprozesses zu optimieren! aber um Mittel zur Verfügung zu stellen, Ihre eigene kundenspezifische Verarbeitung zu implementieren und komplette Kontrolle über das Format und den Inhalt des Stromes für ein Objekt und seine Supertypen zu bieten!

Beispiele hierfür sind die Implementierung von AMF (ActionScript Message Format) Remoting, um native ActionScript-Objekte über das Netzwerk zu übertragen.

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

Die Standard-Serialisierung ist etwas ausführlich und geht von dem weitest möglichen Verwendungsszenario des serialisierten Objekts aus, und entsprechend kennzeichnet das Standardformat (Serializable) den resultierenden Datenstrom mit Informationen über die class des serialisierten Objekts.

Die Externalisierung gibt dem Produzenten des Objektstroms vollständige Kontrolle über die genauen classnmetadaten (falls vorhanden) über die minimal erforderliche Identifikation der class hinaus (z. B. ihr Name). Dies ist in bestimmten Situationen, wie beispielsweise geschlossenen Umgebungen, bei denen der Erzeuger des Objektstroms und sein Verbraucher (der das Objekt aus dem Stream vereinheitlicht) zusammenpassen, eindeutig erwünscht, und zusätzliche Metadaten über die class dienen keinem Zweck und verschlechtern die performance.

Zusätzlich (wie Uri hervorhebt) ermöglicht die Externalisierung auch eine vollständige Kontrolle über die Kodierung der Daten in dem Stream, die den Java-Typen entsprechen. Für ein künstliches Beispiel möchten Sie vielleicht Boolean True als ‘Y’ und False als ‘N’ aufzeichnen. Die Externalisierung ermöglicht dies.

Die Objektserialisierung verwendet die Schnittstellen Serializable und Externalizable. Ein Java-Objekt ist nur serialisierbar. wenn eine class oder eine ihrer Oberklassen entweder die java.io.Serializable-Schnittstelle oder ihre Subschnittstelle java.io.Externalizable implementiert. Die meisten Java-classn sind serialisierbar .

  • NotSerializableException : packageName.ClassName «Um an einem packageName.ClassName im Serialisierungsprozess teilzunehmen, muss die class entweder Serializable- oder Externalisable-Schnittstelle implementieren.

Bildbeschreibung hier eingeben


Serialisierbare Schnittstelle

Object Serialization erzeugt einen Stream mit Informationen über die Java-classn für die Objekte, die gespeichert werden. Bei serialisierbaren Objekten werden ausreichende Informationen zur Wiederherstellung dieser Objekte beibehalten, selbst wenn eine andere (aber kompatible) Version der Implementierung der class vorhanden ist. Die Serializable-Schnittstelle ist definiert, um classn zu identifizieren, die das serialisierbare Protokoll implementieren:

 package java.io; public interface Serializable {}; 
  • Die Serialisierungsschnittstelle hat keine Methoden oder Felder und dient nur dazu, die Semantik der Serialisierung zu identifizieren. Zum Serialisieren / Deserialisieren einer class können wir entweder die Methoden writeObject und readObject (oder) verwenden, wobei wir die Methoden writeObject und readObject aus einer class überschreiben können.
  • JVM hat die vollständige Kontrolle über die Serialisierung des Objekts. Verwenden Sie das vorübergehende Schlüsselwort , um zu verhindern, dass das Datenelement serialisiert wird.
  • Hier werden serialisierbare Objekte direkt aus dem Stream rekonstruiert, ohne ausgeführt zu werden
  • InvalidClassException «Im Deserialisierungsprozess, wenn der Wert der lokalen class serialVersionUID sich von der class des entsprechenden Senders unterscheidet. Das Ergebnis steht in Konflikt als java.io.InvalidClassException: com.github.objects.User; local class incompatible: stream classdesc serialVersionUID = 5081877, local class serialVersionUID = 50818771 java.io.InvalidClassException: com.github.objects.User; local class incompatible: stream classdesc serialVersionUID = 5081877, local class serialVersionUID = 50818771
  • Die Werte der nicht-transienten und nicht-statischen Felder der class werden serialisiert.

Externalisierbare Schnittstelle

Bei Externalizable-Objekten wird nur die Identität der class des Objekts vom Container gespeichert. Die class muss den Inhalt speichern und wiederherstellen. Die Externalizable-Schnittstelle ist wie folgt definiert:

 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; } 
  • Die Externalizable-Schnittstelle verfügt über zwei Methoden. Ein externalisierbares Objekt muss eine writeExternal-Methode und eine readExternal-Methode implementieren, um den Status eines Objekts zu speichern / wiederherzustellen.
  • Der Programmierer muss sich darum kümmern, welche Objekte serialisiert werden sollen. Da ein Programmierer sich um die Serialisierung kümmert, wird hier das vorübergehende Schlüsselwort kein Objekt im Serialisierungsprozess einschränken.
  • Wenn ein Externalizable-Objekt rekonstruiert wird, wird eine Instanz mit dem öffentlichen Konstruktor no-arg erstellt und anschließend die Methode readExternal aufgerufen. Serialisierbare Objekte werden wiederhergestellt, indem sie von einem ObjectInputStream gelesen werden.
  • OptionalDataException «Die Felder MÜSSEN IN DER GLEICHEN ORDNUNG UND TYP sein, wie wir sie ausschrieben. Wenn der Typ nicht übereinstimmt, wird optionalDataException ausgetriggers.

     @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(); } 
  • Die ObjectOutput der class, die mit ObjectOutput geschrieben (belichtet) ObjectOutput , werden serialisiert.


Beispiel « implementiert Serializable

 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; } 

Beispiel « implementiert 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(); } } 

Beispiel

 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(); } } } 

@sehen

  • Was ist Objektserialisierung?
  • Objektserialisierung: Häufig gestellte Fragen

Wenn Sie Optionen zur Verbesserung der performance in Betracht ziehen, vergessen Sie nicht die benutzerdefinierte Serialisierung. Sie können Java das tun lassen, was es gut macht, oder zumindest gut genug, und es bietet benutzerdefinierte Unterstützung für das, was es schlecht macht. Dies ist in der Regel viel weniger Code als vollständige Externalisierbare Unterstützung.

Es gibt so viele Unterschiede zwischen Serializable und Externalisierbar, aber wenn wir den Unterschied zwischen benutzerdefinierten Serializable (überschrieben writeObject () & readObject ()) und Externalizable vergleichen, dann finden wir, dass benutzerdefinierte Implementierung eng mit ObjectOutputStream-class gebunden ist, wo wir in Externalizable Fall selbst sind eine Implementierung von ObjectOutput bereitstellen, die eine ObjectOutputStream-class sein kann, oder eine andere wie org.apache.mina.filter.codec.serialization.ObjectSerializationOutputStream

Im Falle von Externalizable Schnittstelle

 @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(); } 

Ich habe Beispielcode hinzugefügt, um besser zu erklären. Bitte überprüfen Sie den Objektfall von Externalizable. Diese sind nicht direkt an eine Implementierung gebunden.
Wo als Outstream / InStream sind eng an classn gebunden. Wir können ObjectOutputStream / ObjectInputStream erweitern, aber es wird ein bisschen schwierig zu benutzen.