Serialisierbare Java-Schnittstelle

Bei der Serialisierung handelt es sich um einen Prozess, bei dem Datenstrukturen oder Objekte in ein Format oder eine Folge von Bytes übersetzt werden (oft als Marshalling bezeichnet), die später in derselben/anderen Umgebung entpackt werden können (oft als Unmarshalling bezeichnet). Da die beiden Umgebungen völlig unterschiedlich sein können, ist eine gemeinsame Methode der Serialisierung und anschließenden Deserialisierung erforderlich.

Fragen:

  • Wozu dient die Serialisierung?
  • Wie implementiert man die Java-Serialisierungs-API?
  • Wie verwende ich die Serialisierung?

  • Client/Server-Modell: Angenommen, wir müssen einige Informationen vom Client zum Server übertragen, dann können wir das Objekt serialisieren und über das Netzwerk senden, damit der Server es entpacken und die erforderlichen Vorgänge ausführen kann. Bedenken Sie, dass es sich bei dem Client/Server um zwei verschiedene Computer handeln kann.
  • Datenpersistenz für die spätere Verwendung (kann ein Datei-/Datenbank-Blob-Datensatz sein).
  • RMI und JNDI von Java verwenden Serialisierungstechniken.
  • Um Objekte in ein Byte-Array im Speicher zu reduzieren.
  • Angenommen, wir möchten ein Spiel nach einiger Zeit fortsetzen, damit der aktuelle Status in einer Datei serialisiert/gespeichert werden kann, sodass der Benutzer, wenn er das nächste Mal dasselbe Spiel spielt, mit dem aktuellen Status beginnt, anstatt von vorne zu beginnen.
  • Java-Serialisierungs-API

    Wie serialisieren wir und stellen wir sicher, dass wir es korrekt deserialisieren? Glücklicherweise kümmert sich Java um die interne Implementierung des Serialisierens und Entpackens. Wir müssen lediglich Java mitteilen, dass wir ein Objekt serialisieren müssen. Und zu diesem Zweck verwenden wir java.util.serializable Marker-Schnittstelle. Sie wird als Markierungsschnittstelle bezeichnet, da ihr keine Methoden zugeordnet sind, sondern nur als Markierung/Platzhalter verwendet wird.

    class SerializeEx implements Serializable {
           int perAge;
           String perName;
    }
    

    Eine einfache SerializeEx-Klasse, die die java.util.Serializable-Schnittstelle implementiert. Wie bereits erwähnt, handelt es sich bei Serializable lediglich um eine Markierungsschnittstelle. Nachdem wir nun die Schnittstelle als serialisierbar markiert haben, müssen wir das Objekt tatsächlich speichern. Und Java stellt auch dafür APIs bereit.

    Eine Beispielklasse, die die Serialisierung eines Objekts in eine Datei zeigt. Die Schritte zum Serialisieren eines Objekts in eine Datei sind:

  • Stellen Sie sicher, dass das zu serialisierende Objekt java.util.Serializable implementiert.
  • Erstellen Sie eine neue Datei mit dem FileOutputStream-Objekt.
  • Erstellen Sie ein neues ObjectOutputStream-Objekt und übergeben Sie die FileOutputStream-Instanz.
  • Schreiben Sie das Objekt mit der Methode writeObject() in die Datei.
  • public class JavaSerializableEx {
    
           public static void main(String args()) throws FileNotFoundException, IOException  {
                  SerializeEx javaEx = new SerializeEx();
                  javaEx.perAge =28;
                  javaEx.perName="Test";
    
                  try(ObjectOutputStream objOut = new ObjectOutputStream
                                 (new FileOutputStream("serial.out"))) {
                          objOut.writeObject(javaEx);
                  }
    
           }
    
    }
    

    Möglicherweise stellt sich die Frage, was genau gespeichert wird, wenn ein Objekt serialisiert wird. Nur der Inhalt des Objekts wird serialisiert und nicht die Klassendefinition, dh Felder, die zur Rekonstruktion des Objekts erforderlich sind, werden gespeichert. Dies umfasst Instanzvariablen, jedoch nicht die Klassenmethoden. In unserem Fall sind die Instanzvariablen also „perAge“ und „perName“, die in der Datei gespeichert werden.
    Serialisierbar-1Öffnen Sie die gespeicherte Datei in einem Hex-Editor und so sieht sie aus.
    Serialisierbar-2

    (Es gibt ein separates Protokoll namens Object Serialization Stream Protocol, das vorschreibt, wie das Objekt gespeichert werden soll, damit es später erfolgreich abgerufen werden kann.)

    Schauen wir uns ein anderes Beispiel an. Angenommen, ich ändere mein SerializeEx-Objekt so, dass es wie folgt aussieht:

    class SerializeEx implements Serializable {
    
           int perAge;
           String perName;
           private Child child = new Child();
    
       }
    
       class Child implements Serializable {
           int childAge;
           String childFather;
           String childMother;
       }
    

    Eine Objektreferenz innerhalb von SerializeEx. Müssen wir etwas Besonderes tun? Nein. Was würde Ihrer Meinung nach serialisiert werden? Es ist ganz klar, dass wir zum Neuerstellen von SerializeEx auch ein Child-Objekt benötigen und daher das Child-Objekt und seine entsprechenden Instanzvariablen auch serialisiert werden, wenn SerializeEx serialisiert wird.
    Serialisierbar-3Jetzt würden das Child- und SerializeEx-Objekt zusammen mit seinen Instanzvariablen serialisiert, wenn das SerializeEx-Objekt serialisiert wird. Wir müssen uns nicht um die Serialisierung der abhängigen Objekte kümmern. Dies wird von Java erledigt und daher müssen wir nur ein Objekt serialisieren und alle entsprechenden abhängigen Objekte würden auch von Java serialisiert. Aber das Einzige, woran Sie denken sollten, ist, dass jedes abhängige Objekt auch Serializable implementieren muss, um serialisiert zu werden, andernfalls wird eine Laufzeitausnahme ausgelöst.

    Vorübergehend

    Was passiert, wenn ein bestimmtes Instanzfeld aus bestimmten Gründen nicht serialisiert werden muss? Wie sagen wir Java, ein Feld nicht zu serialisieren? Verwenden Sie einfach das Schlüsselwort „transient“ für Felder, die nicht serialisiert werden müssen (Lesen Sie: Was ist ein transientes Schlüsselwort in Java?). Im folgenden Beispiel würde childAge also nicht serialisiert, während andere Instanzvariablen serialisiert würden.

    class Child implements Serializable {
                  transient int childAge;
                  String childFather;
                  String childMother;
    }
    

    Deserialisierung

    Nachdem wir uns kurz mit der Serialisierung befasst haben, wollen wir nun sehen, wie man die serialisierte Datei zurückliest, was als Deserialisierung bezeichnet wird. Die Deserialisierung ist genau das Gegenteil der Serialisierung. Die Beispielklasse für die Deserialisierung würde etwa so aussehen:

    public class DeSerializeEx {
                 public static void main(String() args) throws FileNotFoundException,
                                          IOException,   ClassNotFoundException {
    
                    try(ObjectInputStream objOut = new ObjectInputStream
                                 (new FileInputStream("serial.out"))) {
                          SerializeEx exam = (SerializeEx) objOut.readObject();
                          System.out.println(exam.perAge);
                          System.out.println(exam.perName);
                    }
    
                }
    
            }
    

    Die Beispielklasse verwendet ObjectInputStream und FileInputStream genau wie ObjectOutputStream und FileOutputStream, um Daten aus der serialisierten Datei zu lesen. Da wir „28“ und „Test“ in einer serialisierten Datei gespeichert haben, wäre die Ausgabe „28“ und „Test“, also dieselben Werte, die serialisiert wurden.

    serialVersionUID

    Nachdem wir uns mit den Grundlagen der Serialisierung befasst haben, werden wir uns mit dem Konzept von serialVersionUID befassen. Standardmäßig jedes Mal, wenn ein Objekt serialisiert wird. Java ordnet ihm automatisch eine automatisch berechnete Versions-ID basierend auf der Objektstruktur zu. Und während der Deserialisierung sucht Java nach derselben Versions-ID und löst bei Nichtübereinstimmung eine Ausnahme aus. Warum führt Java diese Prüfung durch? Stellen Sie sich ein Szenario vor, in dem wir das Objekt serialisiert haben, aber später das Schlüsselwort „transient“ aus einer der Instanzvariablen entfernt haben und sich daher die Versions-ID ändert. Gehen wir jedoch davon aus, dass wir das Objekt nach dieser Änderung nicht serialisiert und anschließend deserialisiert haben. Woher weiß die Deserialisierungsklasse von dieser Änderung?

    Eine Möglichkeit, wie Java diese Diskrepanz beheben kann, besteht darin, zu prüfen, ob die Versions-ID zwischen der deserialisierten und der serialisierten Klasse übereinstimmt. Diese serialVersionUID fungiert also wie eine Versionskontrolle für die Serializable-Klasse, indem sie die Kompatibilität des deserialisierten Objekts mit der aktuellen Definition derselben Klasse überprüft.

    Warum sollte man sich darüber Sorgen machen, wenn Java sich darum kümmert? Wenn wir die Eclipse-IDE verwenden und nach der Implementierung von java.util.Serializable eine Warnung angezeigt wird: „Es wurde keine statische finale serialVersionUID vom Typ long deklariert.“ Wenn wir die Eclipse-IDE verwenden, wird durch Klicken auf diese Warnung eine serialVersionUID generiert. Und es gibt zwei Möglichkeiten: Verwenden Sie eine „Standard“- oder „generierte Versions-ID“. Laut Java-Experten sollten wir die Verwendung dieser Standardversions-ID vermeiden. Daher sollten wir immer die andere Option „generierte Versions-ID“ verwenden.

    class SerializeEx implements Serializable{
           private static final long serialVersionUID = -6181741326791855053L;
           int perAge;
           String perName;
    
      }
    

    Unsere Klasse hat eine SerialVersionUID erhalten. Sind wir fertig? Was aber, wenn sich unsere Klassendefinition ändert? Angenommen, ich ändere das Objekt in:

     class SerializeEx implements Serializable{
           private static final long serialVersionUID = -6181741326791855053L;
           int perAge;
           String perName;
           String newField;
    
      }
    

    Ich habe eine Instanzvariable hinzugefügt, „newField“, aber die VersionUID bleibt gleich, da wir diesen Wert nicht geändert haben. Nehmen wir nun an, wir führen Folgendes aus: Deserialisieren Sie SerializeEx mit „serial.out“, das zuvor ohne diese neue Instanzvariable generiert wurde.

    Ob das funktioniert? Ja. Weil die serialVersionUID übereinstimmt … aber ist die deserialisierte Klasse nicht mit unserer aktuellen Klassendefinition inkompatibel????? Wer ist dann schuld daran, dass die Deserialisierung funktioniert hat, obwohl wir die Kompatibilität mit der aktuellen Klassenversion nicht gewahrt haben????? Es liegt in der Verantwortung des Entwicklers, die serialVersionUID jedes Mal zu aktualisieren, wenn Änderungen an der Serializable-Klasse vorgenommen werden, die sich auf die Einzigartigkeit des Objekts auswirken können. Wenn wir die serialVersionUID nicht aktualisieren können, meldet Java die Inkompatibilität nicht, da sie mit derselben alten VersionUID übereinstimmt. Daher müssen Entwickler sicherstellen, dass serialVersionUID immer dann aktualisiert wird, wenn sich das Serializable-Objekt erheblich ändert, was sich auf die Einzigartigkeit des Objekts auswirken kann …

    In diesem Artikel habe ich mich darauf konzentriert, nur einige Aspekte der Serialisierung in Java darzustellen, da es sich um ein umfangreiches Thema handelt. Ich hoffe, dass dieser Artikel als Grundlage für weitere Lektüre zum Thema Java-Serialisierung dienen wird. Danke fürs Lesen.

    Dieser Artikel wurde ursprünglich unter veröffentlicht Java-Tutorials – Lasst uns ins Meer springenhier mit Genehmigung des Autors und als Teil des JBC-Programms erneut veröffentlicht.

    Kommentar verfassen

    Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

    Nach oben scrollen