Einführung in die Java-Serialisierung
1. Einführung
Serialisierung ist die Umwandlung des Status eines Objekts in einen Bytestrom. Deserialisierung macht das Gegenteil. Anders ausgedrückt ist Serialisierung die Umwandlung eines Java-Objekts in einen statischen Strom (eine Folge) von Bytes, die dann in einer Datenbank gespeichert oder über ein Netzwerk übertragen werden können.
2. Serialisierung und Deserialisierung
Der Serialisierungsprozess ist instanzunabhängig, d. H. Objekte können auf einer Plattform serialisiert und auf einer anderen deserialisiert werden. Classes that are eligible for serialization need to implement a special marker interfaceSerializable.
SowohlObjectInputStream als auchObjectOutputStream sind hochrangige Klassen, diejava.io.InputStream bzw.java.io.OutputStream erweitern. ObjectOutputStream kann primitive Typen und Diagramme von Objekten als Bytestrom inOutputStream schreiben. Diese Streams können anschließend mitObjectInputStream gelesen werden.
Die wichtigste Methode inObjectOutputStream ist:
public final void writeObject(Object o) throws IOException;
Wobei ein serialisierbares Objekt in eine Folge (Stream) von Bytes konvertiert wird. Ebenso ist die wichtigste Methode inObjectInputStream:
public final Object readObject()
throws IOException, ClassNotFoundException;
Dieser kann einen Bytestrom lesen und ihn wieder in ein Java-Objekt konvertieren. Dies kann dann auf das ursprüngliche Objekt zurückgesetzt werden.
Lassen Sie uns die Serialisierung mit einerPerson-Klasse veranschaulichen. Beachten Sie, dassstatic fields belong to a class (as opposed to an object) and are not serialized. Beachten Sie außerdem, dass wir das Schlüsselworttransient verwenden können, um Klassenfelder während der Serialisierung zu ignorieren:
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
static String country = "ITALY";
private int age;
private String name;
transient int height;
// getters and setters
}
Der folgende Test zeigt ein Beispiel für das Speichern eines Objekts vom TypPerson in einer lokalen Datei und das Zurücklesen dieses Werts in:
@Test
public void whenSerializingAndDeserializing_ThenObjectIsTheSame() ()
throws IOException, ClassNotFoundException {
Person person = new Person();
person.setAge(20);
person.setName("Joe");
FileOutputStream fileOutputStream
= new FileOutputStream("yourfile.txt");
ObjectOutputStream objectOutputStream
= new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(person);
objectOutputStream.flush();
objectOutputStream.close();
FileInputStream fileInputStream
= new FileInputStream("yourfile.txt");
ObjectInputStream objectInputStream
= new ObjectInputStream(fileInputStream);
Person p2 = (Person) objectInputStream.readObject();
objectInputStream.close();
assertTrue(p2.getAge() == p.getAge());
assertTrue(p2.getName().equals(p.getName()));
}
Wir habenObjectOutputStream verwendet, um den Status dieses Objekts mitFileOutputStream in einer Datei zu speichern. Die Datei“yourfile.txt” wird im Projektverzeichnis erstellt. Diese Datei wird dann mitFileInputStream.ObjectInputStream geladen, nimmt diesen Stream auf und konvertiert ihn in ein neues Objekt namensp2.
Schließlich testen wir den Status des geladenen Objekts und er stimmt mit dem Status des ursprünglichen Objekts überein.
Beachten Sie, dass das geladene Objekt explizit in den TypPersonumgewandelt werden muss.
3. Vorsichtsmaßnahmen bei der Java-Serialisierung
Es gibt einige Einschränkungen, die die Serialisierung in Java betreffen.
3.1. Vererbung und Zusammensetzung
Wenn eine Klasse diejava.io.Serializable-Schnittstelle implementiert, sind auch alle ihre Unterklassen serialisierbar. Im Gegenteil, wenn ein Objekt einen Verweis auf ein anderes Objekt hat, müssen diese Objekte dieSerializable-Schnittstelle separat implementieren, sonst wird einNotSerializableException ausgelöst:
public class Person implements Serializable {
private int age;
private String name;
private Address country; // must be serializable too
}
Wenn eines der Felder in einem serialisierbaren Objekt aus einem Array von Objekten besteht, müssen auch alle diese Objekte serialisierbar sein, sonst wird einNotSerializableException ausgelöst.
3.2. UID der Serienversion
The JVM associates a version (long) number with each serializable class. Hiermit wird überprüft, ob die gespeicherten und geladenen Objekte dieselben Attribute haben und daher bei der Serialisierung kompatibel sind.
Diese Nummer kann von den meisten IDEs automatisch generiert werden und basiert auf dem Klassennamen, seinen Attributen und den zugeordneten Zugriffsmodifikatoren. Änderungen führen zu einer anderen Zahl und könnenInvalidClassException verursachen.
Wenn eine serialisierbare Klasse keinserialVersionUID deklariert, generiert die JVM zur Laufzeit automatisch eines. Es wird jedoch dringend empfohlen, dass jede Klasse ihreserialVersionUID deklariert, da die generierte vom Compiler abhängig ist und daher zu unerwartetenInvalidClassExceptions führen kann.
3.3. Benutzerdefinierte Serialisierung in Java
Java gibt eine Standardmethode an, mit der Objekte serialisiert werden können. Java-Klassen können dieses Standardverhalten überschreiben. Benutzerdefinierte Serialisierung kann besonders nützlich sein, wenn Sie versuchen, ein Objekt mit einigen unserialisierbaren Attributen zu serialisieren. Dies kann erreicht werden, indem zwei Methoden in der Klasse bereitgestellt werden, die serialisiert werden sollen:
private void writeObject(ObjectOutputStream out) throws IOException;
and
private void readObject(ObjectInputStream in)
throws IOException, ClassNotFoundException;
Mit diesen Methoden können wir diese unserialisierbaren Attribute in andere Formen serialisieren, die serialisiert werden können:
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
private transient Address address;
private Person person;
// setters and getters
private void writeObject(ObjectOutputStream oos)
throws IOException {
oos.defaultWriteObject();
oos.writeObject(address.getHouseNumber());
}
private void readObject(ObjectInputStream ois)
throws ClassNotFoundException, IOException {
ois.defaultReadObject();
Integer houseNumber = (Integer) ois.readObject();
Address a = new Address();
a.setHouseNumber(houseNumber);
this.setAddress(a);
}
}
public class Address {
private int houseNumber;
// setters and getters
}
Der folgende Komponententest testet diese benutzerdefinierte Serialisierung:
@Test
public void whenCustomSerializingAndDeserializing_ThenObjectIsTheSame()
throws IOException, ClassNotFoundException {
Person p = new Person();
p.setAge(20);
p.setName("Joe");
Address a = new Address();
a.setHouseNumber(1);
Employee e = new Employee();
e.setPerson(p);
e.setAddress(a);
FileOutputStream fileOutputStream
= new FileOutputStream("yourfile2.txt");
ObjectOutputStream objectOutputStream
= new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(e);
objectOutputStream.flush();
objectOutputStream.close();
FileInputStream fileInputStream
= new FileInputStream("yourfile2.txt");
ObjectInputStream objectInputStream
= new ObjectInputStream(fileInputStream);
Employee e2 = (Employee) objectInputStream.readObject();
objectInputStream.close();
assertTrue(
e2.getPerson().getAge() == e.getPerson().getAge());
assertTrue(
e2.getAddress().getHouseNumber() == e.getAddress().getHouseNumber());
}
In diesem Code erfahren Sie, wie Sie einige unserialisierbare Attribute speichern, indem SieAddress mit benutzerdefinierter Serialisierung serialisieren. Beachten Sie, dass wir die unserialisierbaren Attribute alstransient markieren müssen, umNotSerializableException. zu vermeiden
4. Fazit
In diesem kurzen Tutorial haben wir uns mit der Java-Serialisierung befasst, wichtige Dinge besprochen und gezeigt, wie eine benutzerdefinierte Serialisierung durchgeführt wird.
Wie immer ist der in diesem Tutorial verwendete Quellcodeover on GitHub verfügbar.