Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to realize the empty interface of Serializable

2025-01-18 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/02 Report--

This article mainly explains "how to achieve the empty interface of Serializable". The content of the explanation is simple and clear, and it is easy to learn and understand. Please follow the editor's train of thought to study and learn "how to achieve the empty interface of Serializable".

01. Let's start with some theory.

Java serialization is a groundbreaking set of features introduced in JDK 1.1 to convert Java objects into byte arrays for easy storage or transfer. After that, you can still convert the byte array back to the original state of the Java object.

The idea of serialization is to "freeze" the object state and then write it to disk or transmit it over the network; the idea of deserialization is to "unfreeze" the object state and retrieve the available Java objects.

Let's look at the definition of the serialized Serializbale interface:

Public interface Serializable {}

It is obvious that an empty interface can guarantee that the "class objects" that implement it are serialized and deserialized?

02. A little more actual combat

Before answering the above question, let's create a class (only two fields and the corresponding getter/setter) for serialization and deserialization.

Class Wanger {private String name; private int age; public String getName () {return name;} public void setName (String name) {this.name = name;} public int getAge () {return age;} public void setAge (int age) {this.age = age;}}

Then create a test class to write "18-year-old Wang er" to the file through ObjectOutputStream, which is actually a serialization process, and then read "18-year-old Wang er" out of the file through ObjectInputStream is actually a deserialization process.

Public class Test {public static void main (String [] args) {/ / initialize Wanger wanger = new Wanger (); wanger.setName (Wang er); wanger.setAge (18); System.out.println (wanger); / / write objects to a file try (ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream ("chenmo")) ) {oos.writeObject (wanger);} catch (IOException e) {e.printStackTrace ();} / / read the object try from the file (ObjectInputStream ois = new ObjectInputStream (new FileInputStream ("chenmo"));) {Wanger wanger1 = (Wanger) ois.readObject (); System.out.println (wanger1) } catch (IOException | ClassNotFoundException e) {e.printStackTrace ();}

However, because Wanger does not implement the Serializbale interface, an exception is thrown when running the test class, with the stack information as follows:

Java.io.NotSerializableException: com.cmower.java_demo.xuliehua.Wanger at java.io.ObjectOutputStream.writeObject0 (ObjectOutputStream.java:1184) at java.io.ObjectOutputStream.writeObject (ObjectOutputStream.java:348) at com.cmower.java_demo.xuliehua.Test.main (Test.java:21)

Following the stack information, let's take a look at ObjectOutputStream's writeObject0 () method. Some of the source codes are as follows:

If (obj instanceof String) {writeString ((String) obj, unshared);} else if (cl.isArray ()) {writeArray (obj, desc, unshared);} else if (obj instanceof Enum) {writeEnum ((Enum) obj, desc, unshared);} else if (obj instanceof Serializable) {writeOrdinaryObject (obj, desc, unshared) } else {if (extendedDebugInfo) {throw new NotSerializableException (cl.getName () + "\ n" + debugInfoStack.toString ());} else {throw new NotSerializableException (cl.getName ());}}

In other words, when serializing, ObjectOutputStream will determine which type of serialized object is, the string? Array? Enumeration? Or Serializable, if none of it is true, throw NotSerializableException.

If Wanger implements the Serializable interface, it can be serialized and deserialized.

Class Wanger implements Serializable {private static final long serialVersionUID =-2095916884810199532L; private String name; private int age;}

How to serialize exactly?

Take ObjectOutputStream, for example, which calls writeObject () → writeObject0 () → writeOrdinaryObject () → writeSerialData () → invokeWriteObject () → defaultWriteFields () when serializing.

Private void defaultWriteFields (Object obj, ObjectStreamClass desc) throws IOException {Class cl = desc.forClass (); desc.checkDefaultSerialize (); int primDataSize = desc.getPrimDataSize (); desc.getPrimFieldValues (obj, primVals); bout.write (primVals, 0, primDataSize, false); ObjectStreamField [] fields = desc.getFields (false); Object [] objVals = new Object [desc.getNumObjFields ()] Int numPrimFields = fields.length-objVals.length; desc.getObjFieldValues (obj, objVals); for (int I = 0; I < objVals.length; iTunes +) {try {writeObject0 (objVals [I], fields [numPrimFields + I]. IsUnshared ());}

So how do you deserialize?

Take ObjectInputStream as an example. When deserializing, it calls readObject () → readObject0 () → readOrdinaryObject () → readSerialData () → defaultReadFields ().

Private void defaultWriteFields (Object obj, ObjectStreamClass desc) throws IOException {Class cl = desc.forClass (); desc.checkDefaultSerialize (); int primDataSize = desc.getPrimDataSize (); desc.getPrimFieldValues (obj, primVals); bout.write (primVals, 0, primDataSize, false); ObjectStreamField [] fields = desc.getFields (false); Object [] objVals = new Object [desc.getNumObjFields ()] Int numPrimFields = fields.length-objVals.length; desc.getObjFieldValues (obj, objVals); for (int I = 0; I < objVals.length; iTunes +) {try {writeObject0 (objVals [I], fields [numPrimFields + I]. IsUnshared ());}

I want to see this, you should suddenly realize "Oh". The Serializable interface is defined as empty because it serves only as an identifier, telling the program that the object that implements it can be serialized, but it is not required for real serialization and deserialization.

03. A few more precautions

To put it bluntly, static and transient decorated fields are not serialized.

Why? Let's prove it first and then explain why.

First, add two fields to the Wanger class.

Class Wanger implements Serializable {private static final long serialVersionUID =-2095916884810199532L; private String name; private int age; public static String pre= "Silence"; transient String meizi= "Wang San"; @ Override public String toString () {return "Wanger {" + "name=" + name + ", age=" + age + ", pre=" + pre + ", meizi=" + meizi + "}";}}

Second, print pre-serialized and deserialized objects in the test class, and change the value of the static field after serialization and before deserialization. The specific code is as follows:

Class Wanger implements Serializable {private static final long serialVersionUID =-2095916884810199532L; private String name; private int age; public static String pre= "Silence"; transient String meizi= "Wang San"; @ Override public String toString () {return "Wanger {" + "name=" + name + ", age=" + age + ", pre=" + pre + ", meizi=" + meizi + "}";}}

From the comparison of the results, we can find that:

1) before serialization, the value of pre is "silent". After serialization, the value of pre is modified to "not silent". After deserialization, the value of pre is "not silent", rather than the pre-serialization state "silent".

Why? Because serialization holds the state of the object, and static-decorated fields belong to the state of the class, it can be proved that serialization does not save static-decorated fields.

2) before serialization, the value of meizi is "Wang San". After deserialization, the value of meizi is null, not the pre-serialization state "Wang San".

Why? Transient, which means "temporary" in Chinese (on the importance of English), prevents the field from being serialized into the file. After being deserialized, the value of the transient field is set to the initial value, such as the initial value of the int type is 0 and the initial value of the object type is null.

If you want to delve into the source code, you can find the following code in ObjectStreamClass:

Private static ObjectStreamField [] getDefaultSerialFields (Class cl) {Field [] clclFields = cl.getDeclaredFields (); ArrayList list = new ArrayList (); int mask = Modifier.STATIC | Modifier.TRANSIENT; int size = list.size (); return (size = = 0)? NO_FIELDS: list.toArray (new ObjectStreamField [size]);}

Does it feel better to see Modifier.STATIC | Modifier.TRANSIENT?

04. Some more practical information.

In addition to Serializable, Java provides a serialization interface, Externalizable (which is a bit of a mouthful to read).

Is there any difference between the two interfaces? Just give it a try.

First, replace the interface Serializable implemented by the Wanger class with Externalizable.

Class Wanger implements Externalizable {private String name; private int age; public Wanger () {} public String getName () {return name;} @ Override public String toString () {return "Wanger {" + "name=" + name + ", age=" + age + "}" } @ Override public void writeExternal (ObjectOutput out) throws IOException {} @ Override public void readExternal (ObjectInput in) throws IOException, ClassNotFoundException {}}

There are some differences between the Wanger class that implements the Externalizable interface and the Wanger class that implements the Serializable interface:

1) A new construction method without parameters has been added.

When you use Externalizable for deserialization, the no-parameter constructor of the serialized class is called to create a new object, and then the field value of the saved object is copied. Otherwise, the following exception is thrown:

Java.io.InvalidClassException: com.cmower.java_demo.xuliehua1.Wanger No valid constructor at java.io.ObjectStreamClass$ExceptionInfo.newInvalidClassException (ObjectStreamClass.java:150) at java.io.ObjectStreamClass.checkDeserialize (ObjectStreamClass.java:790) at java.io.ObjectInputStream.readOrdinaryObject (ObjectInputStream.java:1782) at java.io.ObjectInputStream.readObject0 (ObjectInputStream.java:1353) at java.io.ObjectInputStream.readObject (ObjectInputStream.java:373) at com.cmower.java_demo.xuliehua1.Test.main (Test.java:27)

2) two new methods, writeExternal () and readExternal (), have been added to implement the Externalizable interface.

Then we print the pre-serialized and deserialized objects in the test class.

/ / initialize Wanger wanger = new Wanger (); wanger.setName (Wang er); wanger.setAge (18); System.out.println (wanger); / / write objects to a file try (ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream ("chenmo"));) {oos.writeObject (wanger);} catch (IOException e) {e.printStackTrace () } / / read the object try from the file (ObjectInputStream ois = new ObjectInputStream (new FileInputStream (new File ("chenmo"));) {Wanger wanger1 = (Wanger) ois.readObject (); System.out.println (wanger1);} catch (IOException | ClassNotFoundException e) {e.printStackTrace ();} / Wanger {name= Wang, age=18} / / Wanger {name=null,age=0}

From the result of the output, the object fields obtained after deserialization become the default values, that is, the object state before serialization is not "frozen".

Why? Because we didn't override the specific writeExternal () and readExternal () methods for the Wanger class. So how do you rewrite it?

@ Override public void writeExternal (ObjectOutput out) throws IOException {out.writeObject (name); out.writeInt (age);} @ Override public void readExternal (ObjectInput in) throws IOException, ClassNotFoundException {name = (String) in.readObject (); age = in.readInt ();}

1) call the writeObject () method of ObjectOutput to write the name of string type to the output stream

2) call the writeInt () method of ObjectOutput to write the age of the integer to the output stream

3) call the readObject () method of ObjectInput to read the name of string type into the input stream

4) call the readInt () method of ObjectInput to read the age of string type into the input stream

Run the test class again and you will find that the object can be serialized and deserialized normally.

Before serialization: Wanger {name= King, age=18}

After serialization: Wanger {name= King, age=18}

05. Have some more dessert

Let me ask you first, do you know the function of this code private static final long serialVersionUID =-2095916884810199532L;?

Um...

SerialVersionUID, known as serialization ID, is an important factor that determines whether Java objects can be deserialized successfully. During deserialization, the Java virtual machine compares the serialVersionUID in the byte stream with the serialVersionUID in the serialized class, and if the same, it can be deserialized, otherwise an exception with an inconsistent serialization version will be thrown.

When a class implements the Serializable interface, IDE reminds the class that it is best to generate a serialized ID, like this:

1) add a default version of serialized ID:

Private static final long serialVersionUID = 1L.

2) add a randomly generated non-repeating serialization ID.

Private static final long serialVersionUID =-2095916884810199532L

3) add @ SuppressWarnings annotation.

@ SuppressWarnings ("serial")

How do you choose?

First, we take the second approach by adding a randomly generated serialized ID to the serialized class.

Class Wanger implements Serializable {private static final long serialVersionUID =-2095916884810199532L; private String name; private int age; / / other code ignore}

Then, serialize an Wanger object into a file.

/ / initialize Wanger wanger = new Wanger (); wanger.setName (Wang er); wanger.setAge (18); System.out.println (wanger); / / write objects to a file try (ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream ("chenmo"));) {oos.writeObject (wanger);} catch (IOException e) {e.printStackTrace ();}

At this time, we quietly change the serialization ID of the Wanger class, hehe.

/ / private static final long serialVersionUID =-2095916884810199532L; private static final long serialVersionUID =-2095916884810199533L

All right, get ready for deserialization.

Try (ObjectInputStream ois = new ObjectInputStream (new FileInputStream (new File ("chenmo"));) {Wanger wanger = (Wanger) ois.readObject (); System.out.println (wanger);} catch (IOException | ClassNotFoundException e) {e.printStackTrace ();}

Oh, there's been a mistake.

Java.io.InvalidClassException: local class incompatible: stream classdesc serialVersionUID =-2095916884810199532, local class serialVersionUID =-2095916884810199533 at java.io.ObjectInputStream.readClassDesc (ObjectInputStream.java:1521) at com.cmower.java_demo.xuliehua1.Test.main (Test.java:27)

The exception stack information tells us that the serialization ID read from the persistence file is inconsistent with the local serialization ID and cannot be deserialized.

What if we use the third method to add a @ SuppressWarnings ("serial") annotation to the Wanger class?

@ SuppressWarnings ("serial") class Wanger3 implements Serializable {/ / omit other code}

All right, let's deserialize again. It is a pity that the report is still wrong.

Java.io.InvalidClassException: local class incompatible: stream classdesc serialVersionUID =-2095916884810199532, local class serialVersionUID =-3818877437117647968 at java.io.ObjectInputStream.readClassDesc (ObjectInputStream.java:1521) at com.cmower.java_demo.xuliehua1.Test.main (Test.java:27)

The exception stack information tells us that the local serialization ID is-3818877437117647968, which is still inconsistent with the serialization ID read in the persistence file and cannot be deserialized. What does that mean? When you use the @ SuppressWarnings ("serial") annotation, it automatically generates a random serialized ID for the serialized class.

It can be proved that whether the Java virtual machine allows deserialization depends not only on whether the classpath and functional code are consistent, but also on whether the serialization ID is consistent.

That is, if there are no special requirements, the default serialization ID (1L) is fine, which ensures that deserialization succeeds when the code is consistent.

Class Wanger implements Serializable {private static final long serialVersionUID = 1L; / / omit other code} Thank you for reading, the above is the content of "how to achieve the empty interface of Serializable". After the study of this article, I believe you have a deeper understanding of how to achieve the empty interface of Serializable, and the specific use needs to be verified in practice. Here is, the editor will push for you more related knowledge points of the article, welcome to follow!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report