3426 Stimmen

Was ist eine serialVersionUID und warum sollte ich sie verwenden?

Eclipse gibt Warnungen aus, wenn ein serialVersionUID fehlt.

Die serialisierbare Klasse Foo deklariert keine statische finale serialVersionUID-Feld vom Typ long

¿Qué es? serialVersionUID und warum ist sie wichtig? Bitte zeigen Sie ein Beispiel, bei dem fehlende serialVersionUID wird ein Problem verursachen.

7 Stimmen

Finden Sie eine gute Praxis über serialversionUID; dzone.com/articles/what-is-serialversionuid

81voto

Thalaivar Punkte 22174

Was ist ein serialVersionUID und warum sollte ich es verwenden?

SerialVersionUID ist ein eindeutiger Bezeichner für jede Klasse, JVM verwendet es, um die Versionen der Klasse zu vergleichen und sicherzustellen, dass dieselbe Klasse, die bei der Serialisierung verwendet wurde, bei der Deserialisierung geladen wird.

Wenn Sie eine angeben, haben Sie mehr Kontrolle, obwohl die JVM eine generiert, wenn Sie sie nicht angeben. Der generierte Wert kann sich zwischen verschiedenen Compilern unterscheiden. Außerdem möchte man manchmal aus irgendeinem Grund die Deserialisierung von alten serialisierten Objekten verbieten [ backward incompatibility ], und in diesem Fall müssen Sie nur die serialVersionUID ändern.

Le site Javadocs für Serializable sagen :

die Standardberechnung der serialVersionUID ist sehr empfindlich gegenüber der Klasse Details, die je nach Compiler-Implementierung variieren können, und kann daher zu unerwarteten InvalidClassException s während Deserialisierung.

Daher müssen Sie serialVersionUID deklarieren, weil wir damit mehr Kontrolle haben .

Dieser Artikel hat einige gute Punkte zu diesem Thema.

3 Stimmen

@Vinothbabu aber serialVersionUID ist statisch, so dass statische Variablen nicht serialisiert werden können. Wie kommt es dann, dass JVM die Version prüft, ohne zu wissen, welche Version das deserialisierende Objekt hat?

5 Stimmen

Was in dieser Antwort nicht erwähnt wird, ist, dass Sie unbeabsichtigte Folgen verursachen können, wenn Sie blindlings serialVersionUID ohne zu wissen warum. Tom Andersons Kommentar zur Antwort von MetroidFan2002 spricht dies an: "Ich würde sagen, wenn Sie die Serialisierung nicht für die permanente Speicherung verwenden, sollten Sie @SuppressWarnings verwenden, anstatt einen Wert hinzuzufügen. Dadurch wird die Klasse weniger überladen und die Fähigkeit des serialVersionUID-Mechanismus, Sie vor inkompatiblen Änderungen zu schützen, bleibt erhalten."

10 Stimmen

serialVersionUID es pas einen "eindeutigen Bezeichner für jede Klasse". Das ist der vollqualifizierte Klassenname. Er ist ein Version Indikator.

65voto

Rupesh Punkte 2547

In der ursprünglichen Frage wurde nach dem "Warum ist es wichtig" und dem "Beispiel" gefragt, wo dies Serial Version ID nützlich wäre. Nun, ich habe eine gefunden.

Angenommen, Sie erstellen eine Car Klasse, instanziiert sie und schreibt sie in einen Objektstrom. Das abgeflachte Auto-Objekt liegt eine Zeit lang im Dateisystem. In der Zwischenzeit, wenn die Car Klasse wird durch Hinzufügen eines neuen Feldes geändert. Wenn Sie später versuchen, die geglättete Klasse zu lesen (d.h. zu deserialisieren) Car Objekt, erhalten Sie die java.io.InvalidClassException - weil allen serialisierbaren Klassen automatisch ein eindeutiger Bezeichner zugewiesen wird. Diese Ausnahme wird ausgelöst, wenn der Bezeichner der Klasse nicht mit dem Bezeichner des reduzierten Objekts übereinstimmt. Wenn man genau darüber nachdenkt, wird die Ausnahme wegen der Hinzufügung des neuen Feldes ausgelöst. Sie können diese Ausnahme vermeiden, indem Sie die Versionierung selbst steuern, indem Sie eine explizite serialVersionUID deklarieren. Es gibt auch einen kleinen Leistungsvorteil durch die explizite Deklaration Ihrer serialVersionUID (denn es muss nicht berechnet werden). Daher ist es die beste Praxis, Ihre eigene serialVersionUID zu Ihren serialisierbaren Klassen hinzuzufügen, sobald Sie sie wie unten gezeigt erstellen:

public class Car {
    static final long serialVersionUID = 1L; //assign a long value
}

0 Stimmen

Und man sollte jeder UID eine zufällige lange Zahl, nicht 1L, zuweisen.

5 Stimmen

@abbas 'Man sollte' das tun, warum? Bitte erklären Sie, welchen Unterschied es macht.

0 Stimmen

Wie der Name schon sagt, stellt sie die Version der Klasse dar, die zur Serialisierung des Objekts verwendet wurde. Wenn Sie ihr jedes Mal dieselbe Nummer zuweisen, werden Sie nicht in der Lage sein, die richtige Klasse zu finden, um sie zu de-serialisieren. Wenn Sie also eine Änderung an der Klasse vornehmen, sollten Sie auch die Version ändern.

62voto

JegsVala Punkte 1669

Zunächst muss ich erklären, was Serialisierung ist.

Serialisierung ermöglicht es, ein Objekt in einen Stream zu konvertieren, um dieses Objekt über das Netzwerk zu senden ODER in eine Datei zu speichern ODER in der DB für die Briefverwendung zu speichern.

Es gibt einige Regeln für die Serialisierung .

  • Ein Objekt ist nur serialisierbar, wenn seine Klasse oder seine Oberklasse die Schnittstelle Serializable implementiert

  • Ein Objekt ist serialisierbar (implementiert selbst die Schnittstelle Serializable), auch wenn seine Oberklasse dies nicht ist. Die erste Oberklasse in der Hierarchie der serialisierbaren Klasse, die die Schnittstelle Serializable nicht implementiert, MUSS jedoch einen No-Arg-Konstruktor haben. Wenn dies nicht der Fall ist, erzeugt readObject() zur Laufzeit eine java.io.InvalidClassException

  • Alle primitiven Typen sind serialisierbar.

  • Transiente Felder (mit transientem Modifikator) werden NICHT serialisiert (d. h. nicht gespeichert oder wiederhergestellt). Eine Klasse, die Serializable implementiert, muss transiente Felder von Klassen markieren, die keine Serialisierung unterstützen (z. B. ein Dateistrom).

  • Statische Felder (mit statischem Modifikator) werden nicht serialisiert.

Wenn Object serialisiert wird, assoziiert Java Runtime die serielle Versionsnummer, auch bekannt als die serialVersionID .

Wo wir serialVersionID benötigen:

Während der Deserialisierung, um zu prüfen, ob Sender und Empfänger in Bezug auf die Serialisierung kompatibel sind. Wenn der Empfänger die Klasse mit einer anderen serialVersionID dann wird die Deserialisierung mit InvalidClassCastException .
Eine serialisierbare Klasse kann ihre eigene serialVersionUID explizit durch Deklaration eines Feldes namens serialVersionUID die statisch, endgültig und vom Typ long sein müssen.

Versuchen wir dies anhand eines Beispiels.

import java.io.Serializable;

public class Employee implements Serializable {
    private static final long serialVersionUID = 1L;
    private String empname;
    private byte empage;

    public String getEmpName() {
        return name;
    }

    public void setEmpName(String empname) {
        this.empname = empname;
    }

    public byte getEmpAge() {
        return empage;
    }

    public void setEmpAge(byte empage) {
        this.empage = empage;
    }

    public String whoIsThis() {
        return getEmpName() + " is " + getEmpAge() + "years old";
    }
}

Serialisierungsobjekt erstellen

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

public class Writer {
    public static void main(String[] args) throws IOException {
        Employee employee = new Employee();
        employee.setEmpName("Jagdish");
        employee.setEmpAge((byte) 30);

        FileOutputStream fout = new
                FileOutputStream("/users/Jagdish.vala/employee.obj");
        ObjectOutputStream oos = new ObjectOutputStream(fout);
        oos.writeObject(employee);
        oos.close();
        System.out.println("Process complete");
    }
}

Das Objekt deserialisieren

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

public class Reader {
    public static void main(String[] args) throws ClassNotFoundException, IOException {
        Employee employee = new Employee();
        FileInputStream fin = new FileInputStream("/users/Jagdish.vala/employee.obj");
        ObjectInputStream ois = new ObjectInputStream(fin);
        employee = (Employee) ois.readObject();
        ois.close();
        System.out.println(employee.whoIsThis());
    }
}

HINWEIS: Ändern Sie nun die serialVersionUID der Klasse Employee und speichern Sie:

private static final long serialVersionUID = 4L;

Und führen Sie die Klasse Reader aus. Wenn Sie die Writer-Klasse nicht ausführen, erhalten Sie die Ausnahme.

Exception in thread "main" java.io.InvalidClassException: 
com.jagdish.vala.java.serialVersion.Employee; local class incompatible: 
stream classdesc serialVersionUID = 1, local class serialVersionUID = 4
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:616)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1623)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1518)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1774)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1351)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:371)
at com.krishantha.sample.java.serialVersion.Reader.main(Reader.java:14)

0 Stimmen

Korrigieren Sie mich, wenn ich falsch liege - die lokale Klasse ist diejenige, die Sie gerade im Klassenpfad haben/verwenden, und die Stream-Klasse ist diejenige, die von einer anderen Partei verwendet wird (z. B. ein Server, der Ihnen eine Antwort zurückgibt und die Antwort serialisiert hat). Diese Situation kann auftreten, wenn Sie mit einem Server kommunizieren, der seine 3rd-Party-Libs aktualisiert hat, aber Sie (der Client) haben dies nicht getan.

43voto

eishay Punkte 1296

Wenn Sie Ihre Objekte niemals in ein Byte-Array serialisieren und senden/speichern müssen, dann brauchen Sie sich darüber keine Gedanken zu machen. Wenn doch, dann müssen Sie Ihre serialVersionUID berücksichtigen, da der Deserialisierer des Objekts sie mit der Version des Objekts abgleichen wird, die sein Classloader hat. Lesen Sie mehr darüber in der Java Language Specification.

11 Stimmen

Wenn Sie die Objekte nicht serialisieren wollen, warum sind sie dann serialisierbar?

9 Stimmen

@erickson - die übergeordnete Klasse kann serialisierbar sein, z.B. ArrayList, aber Sie wollen Ihr eigenes Objekt (z.B. eine geänderte Array-Liste) als Basis verwenden, werden aber niemals die von Ihnen erstellte Collection serialisieren.

6 Stimmen

Sie wird nirgendwo in der Java-Sprachenspezifikation erwähnt. Sie wird in der Spezifikation der Objektversionierung erwähnt.

37voto

Paŭlo Ebermann Punkte 70779

Wenn Sie diese Warnung bei einer Klasse erhalten, an deren Serialisierung Sie nie denken und die Sie nicht selbst deklariert haben implements Serializable ist, liegt es oft daran, dass Sie von einer Oberklasse geerbt haben, die Serializable implementiert. Oft wäre es dann besser, an ein solches Objekt zu delegieren, anstatt die Vererbung zu nutzen.

Also, statt

public class MyExample extends ArrayList<String> {

    public MyExample() {
        super();
    }
    ...
}

tun

public class MyExample {
    private List<String> myList;

    public MyExample() {
         this.myList = new ArrayList<String>();
    }
    ...
}

und rufen in den entsprechenden Methoden myList.foo() anstelle von this.foo() (oder super.foo() ). (Dies trifft nicht in allen Fällen zu, aber doch recht häufig.)

Ich sehe oft Leute, die JFrame oder ähnliches erweitern, obwohl sie eigentlich nur das hier delegieren müssen. (Dies hilft auch für die automatische Vervollständigung in einer IDE, da JFrame Hunderte von Methoden hat, die Sie nicht benötigen, wenn Sie Ihre benutzerdefinierten in Ihrer Klasse aufrufen möchten).

Ein Fall, in dem die Warnung (oder die serialVersionUID) unvermeidlich ist, ist, wenn Sie von AbstractAction erweitern, normalerweise in einer anonymen Klasse, nur das Hinzufügen der actionPerformed-Methode. Ich denke, dass es in diesem Fall keine Warnung geben sollte (da man normalerweise solche anonymen Klassen sowieso nicht zuverlässig über verschiedene Versionen der Klasse hinweg serialisieren und deserialisieren kann), aber ich bin mir nicht sicher, wie der Compiler dies erkennen könnte.

4 Stimmen

Ich denke, Sie haben Recht, dass Komposition über Vererbung mehr Sinn macht, vor allem wenn es um Klassen wie ArrayList geht. Allerdings verlangen viele Frameworks, dass man von abstrakten Superklassen erweitert, die serialisierbar sind (wie z.B. Struts 1.2's ActionForm Klasse oder Saxon's ExtensionFunctionDefinition), in diesem Fall ist diese Lösung nicht praktikabel. Ich denke, Sie haben Recht, es wäre schön, wenn die Warnung in bestimmten Fällen ignoriert würde (z.B. wenn Sie von einer abstrakten serialisierbaren Klasse erweitern)

2 Stimmen

Wenn man eine Klasse als Mitglied hinzufügt, anstatt von ihr zu erben, müsste man für JEDE Methode der Mitgliedsklasse, die man verwenden möchte, eine Wrapper-Methode schreiben, was in vielen Situationen nicht praktikabel wäre... es sei denn, Java hat eine Funktion ähnlich der von Perl __AUTOLOAD die ich nicht kenne.

4 Stimmen

@M_M: Wenn Sie viele Methoden an Ihr umhülltes Objekt delegieren würden, wäre es natürlich nicht angemessen, die Delegation zu verwenden. Aber ich vermute, dass dieser Fall ein Zeichen für einen Designfehler ist - die Benutzer Ihrer Klasse (z.B. "MainGui") sollten nicht viele Methoden des umhüllten Objekts (z.B. JFrame) aufrufen müssen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X