Java >> Java tutorial >  >> Java

Java serialVersionUID – Sådan genereres serialVersionUID

Java-serialisering er processen med at konvertere et objekt til en strøm af bytes, så vi kan gøre ting som at gemme det på disk eller sende det over netværket. Deserialisering er den omvendte proces - at konvertere en strøm af bytes til et objekt i hukommelsen.

Under serialisering knytter java runtime et versionsnummer til hver serialiserbar klasse. Dette nummer kaldes serialVersionUID , som bruges under deserialisering til at verificere, at afsenderen og modtageren af ​​et serialiseret objekt har indlæst klasser for det objekt, der er kompatible med hensyn til serialisering. Hvis modtageren har indlæst en klasse for objektet, der har en anden serialVersionUID end den tilsvarende afsenderens klasse, vil deserialisering resultere i en InvalidClassException .

1. Java serialVersionUID-syntaks

En serialiserbar klasse kan erklære sin egen serialVersionUID eksplicit ved at erklære et felt med navnet "serialVersionUID ”, der skal være statisk, endelig og af typen lang.

private static final long serialVersionUID = 4L;

Her repræsenterer serialVersionUID klasseversionen, og vi bør øge den, hvis den nuværende version af din klasse er ændret, så den ikke længere er bagudkompatibel med dens tidligere version.

2. Eksempel på Java-serialisering og deserialisering

Lad os se et eksempel på, hvordan en klasse serialiseres og derefter deserialiseres.

package com.howtodoinjava.demo.serialization;

import java.io.*;
import java.util.logging.Logger;
 
public class DemoClass implements java.io.Serializable {
 
	private static final long serialVersionUID = 4L;			//Default serial version uid
	private static final String fileName = "DemoClassBytes.ser"; //Any random name
	private static final Logger logger = Logger.getLogger("");
	//Few data fields
	//Able to serialize
	private static String staticVariable;
	private int intVariable;
	
	//Not able to serialize
	transient private String transientVariable = "this is a transient instance field";
	private Thread threadClass;
	 
	public static void main(String[] args) throws IOException, ClassNotFoundException 
	{
		//Serialization
	
	    DemoClass test = new DemoClass();
	    test.intVariable = 1;
	    staticVariable = "this is a static variable";
	    writeOut(test);
	    System.out.println("DemoClass to be saved: " + test);
	
	    //De-serialization
	    
	    System.out.println("DemoClass deserialized: " + readIn());
	}
	 
	private static Object readIn() throws IOException, ClassNotFoundException {
	    ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File(fileName)));
	    return ois.readObject();
	}
	 
	private static void writeOut(java.io.Serializable obj) throws IOException {
	    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File(fileName)));
	    oos.writeObject(obj);
	    oos.close();
	}
	 
	@Override public String toString() {
	    return "DemoClass: final static fileName=" + fileName + ", final static logger=" + logger
	            + ", non-final static staticVariable=" + staticVariable + ", instance intVariable=" + intVariable
	            + ", transient instance transientVariable=" + transientVariable + ", non-serializable instance field threadClass:=" + threadClass;
	}
}

Programoutput.

DemoClass to be saved: DemoClass: 
final static fileName=DemoClassBytes.ser, 
final static logger=java.util.logging.LogManager$RootLogger@1d99a4d, 
non-final static staticVariable=this is a static variable, 
instance intVariable=1, 
transient instance transientVariable=this is a transient instance field, 
non-serializable instance field threadClass:=null

//Execute readIn() function from a separate main() method 
//to get given below output correctly. It will flush out the static fields.

DemoClass deserialized: DemoClass: 
final static fileName=DemoClassBytes.ser, 
final static logger=java.util.logging.LogManager$RootLogger@cd2c3c, 
non-final static staticVariable=null, 
instance intVariable=1, 
transient instance transientVariable=null, 
non-serializable instance field threadClass:=null

Hvis en serialiserbar klasse ikke eksplicit erklærer en serialVersionUID , så vil serialiseringens køretid beregne en standard serialVersionUID værdi for den pågældende klasse baseret på forskellige aspekter af klassen.

3. Sådan genereres serialVersionUID

Joshua Bloch siger i Effective Java, at det automatisk genererede UID er genereret baseret på et klassenavn, implementerede grænseflader og alle offentlige og beskyttede medlemmer. Ændring af nogen af ​​disse på nogen måde vil ændre serialVersionUID.

Det anbefales dog kraftigt, at alle klasser, der kan serialiseres, eksplicit erklærer serialVersionUID-værdier, da standarden serialVersionUID beregning er meget følsom over for klassedetaljer, der kan variere afhængigt af compilerimplementeringer og kan producere forskellige serialVersionUID i forskellige miljøer . Dette kan resultere i uventet InvalidClassException under deserialisering.

Derfor for at garantere en konsistent serialVersionUID værdi på tværs af forskellige java compiler implementeringer, skal en serialiserbar klasse erklære en eksplicit serialVersionUID værdi. Det anbefales også kraftigt, at eksplicit serialVersionUID erklæringer bruger private modifikator i serialVersionUID hvor det er muligt, da sådanne erklæringer kun gælder for den umiddelbart indberettende klasse.

Bemærk også, at serialVersionUID feltet er ikke nyttigt som nedarvet medlem.

Baseret på min korte karriere kan jeg sige, at lagring af serialiserede data i lang tid [spatial serialisering ] er ikke særlig almindelig brug. Det er langt mere almindeligt at bruge serialiseringsmekanismen til midlertidigt at skrive data [temporal serialization ] til for eksempel en cache eller send den over netværket til et andet program for at bruge informationen.

I sådanne tilfælde er vi ikke interesserede i at opretholde bagudkompatibilitet. Vi er kun optaget af at sikre, at de kodebaser, der kommunikerer på netværket, faktisk har den samme version af relevante klasser. For at lette en sådan kontrol skal vi vedligeholde serialVersionUID lige som det og ikke ændre det. Glem heller ikke at opdatere det, når du laver inkompatible ændringer af dine klasser, på begge sider af applikationer på netværket.

4. Java-klasser uden serialVersionUID

Det er ikke den situation, vi nogensinde ønsker at stå over for. Men det er virkeligheden, og nogle gange sker det (skal jeg sige sjældent?). Hvis vi har brug for at ændre en sådan klasse på en inkompatibel måde, men ønsker at bevare serialiserings-/deserialiseringskapaciteten med den gamle version af klassen, kan vi bruge JDK-værktøjet "serialver". Dette værktøj genererer serialVersionUID på den gamle klasse , og indstil det eksplicit på den nye klasse. Glem ikke at implementere readObject() og writeObject() metoder, fordi den indbyggede deserialiseringsmekanisme (in.defaultReadObject() ) vil nægte at deserialisere fra gamle versioner af dataene.

Hvis vi definerer vores egen readObject() funktion som kan læse gamle data tilbage. Denne brugerdefinerede kode bør kontrollere serialVersionUID for at vide, hvilken version dataene er i og beslutte, hvordan de skal deserialiseres. Denne versioneringsteknik er nyttig, hvis vi gemmer serialiserede data, som overlever flere versioner af din kode.

Læs mere:Java-serialiseringskompatible og inkompatible ændringer

5. Java serialVersionUID – Resumé

  1. transient og static felter ignoreres i serialisering. Efter deserialisering transient felter og ikke-endelige statiske felter vil være nul.

    final og static felter har stadig værdier, da de er en del af klassedataene.

  2. ObjectOutputStream.writeObject(obj) og ObjectInputStream.readObject() bruges i serialisering og deserialisering.
  3. Under serialisering skal vi håndtere IOException; under deserialisering skal vi håndtere IOException og ClassNotFoundException . Så den deserialiserede klassetype skal være i klassestien.
  4. Uinitialiserede, ikke-serialiserbare, ikke-forbigående forekomstfelter tolereres.

    Når du tilføjer "private Thread th; “, ingen fejl i serialiserbar. Men "private Thread threadClass = new Thread(); ” vil forårsage undtagelse:

    Exception in thread "main" java.io.NotSerializableException: java.lang.Thread
    at java.io.ObjectOutputStream.writeObject0(Unknown Source)
    at java.io.ObjectOutputStream.defaultWriteFields(Unknown Source)
    at java.io.ObjectOutputStream.writeSerialData(Unknown Source)
    at java.io.ObjectOutputStream.writeOrdinaryObject(Unknown Source)
    at java.io.ObjectOutputStream.writeObject0(Unknown Source)
    at java.io.ObjectOutputStream.writeObject(Unknown Source)
    at com.howtodoinjava.demo.serialization.DemoClass.writeOut(DemoClass.java:42)
    at com.howtodoinjava.demo.serialization.DemoClass.main(DemoClass.java:27)
    
  5. Serialisering og deserialisering kan bruges til at kopiere og klone objekter. Den er langsommere end almindelig klon, men kan producere en dyb kopi meget nemt.
  6. Hvis jeg har brug for at serialisere en Serializable klasse Employee , men en af ​​dens superklasser kan ikke serialiseres, kan Employee klasse stadig serialiseres og deserialiseres? Svaret er ja, forudsat at den ikke-serialiserbare superklasse har en no-arg-konstruktør, som påkaldes ved deserialisering for at initialisere denne superklasse.
  7. Vi skal være forsigtige, når vi ændrer en klasse, der implementerer java.io.Serializable . Hvis klassen ikke indeholder en serialVersionUID felt, dets serialVersionUID vil automatisk blive genereret af compileren.

    Forskellige compilere eller forskellige versioner af den samme compiler vil generere potentielt forskellige værdier.

  8. Beregning af serialVersionUID er baseret på ikke kun felter, men også på andre aspekter af klassen, såsom implement-klausul, konstruktører osv. Så den bedste praksis er eksplicit at erklære en serialVersionUID felt for at opretholde bagudkompatibilitet. Hvis vi har brug for at ændre den serialiserbare klasse væsentligt og forventer, at den er inkompatibel med tidligere versioner, så skal vi øge serialVersionUID for at undgå at blande forskellige versioner.

God læring !!


Java tag