The goal of object serialization is to either save the object on disk or transfer it over the network. The implementation mechanism is to allow the object to be transformed into a platform-independent binary stream.
The serialization mechanism for objects in Java is to convert the allowed objects to byte sequences. These byte sequences can leave Java objects out of the program, which can be saved on disk or transferred between networks.
The serialization of an object is the writing of a Java object to the IO stream, and in this case, deserialization restores a Java object from the IO stream.
Implementing serialization
If you are serializing a Java object, the object's class needs to be serializable. To make the class serializable, the class needs to implement the following two interfaces:
- Serializable
- Externalizable
using serializable serialization
The implementation of the serializable interface is very simple, as long as the Java implementation of the serializable interface, no need to implement any method.
Once a class implements the Serializable interface, the object of that class is serializable. The serialization of an object that implements a class can use ObjectOutputStream, which is implemented as follows:
- Create a ObjectOutputStream object;
- Invokes the ObjectOutputStream WriteObject method output object.
Here is an example:
Package Com.zhyea.test;import Java.io.fileoutputstream;import Java.io.ioexception;import Java.io.objectoutputstream;import java.io.serializable;/** * Serialization Test class * * @author Robin * @date December 18, 2014 */public class serialtest {public static void main (string[] args) {ObjectOutputStream oos = null; try {oos = new ObjectOutputStream (New FileOutputStream ("D:\\object.txt")); Person Robin = new Person ("Robin", 29); Oos.writeobject (Robin); } catch (IOException e) {e.printstacktrace (); } finally {if (null! = Oos) {try {oos.close (); } catch (IOException e) {e.printstacktrace (); }}}}}/** * Serialization Test Object * * @author Robin * @date December 18, 2014 */class person implements serializable{ Private static final long serialversionuid = -6412852654889352693l; /** * Name * * * Private String name; /** * Age */private int; Public person () {} public person (String name, int age) {this.name = name; This.age = age; } public String GetName () {return name; } public void SetName (String name) {this.name = name; } public int Getage () {return age; public void Setage (int.) {this.age = age; }}
The code above implements the saving of a person object on a text file on disk object.txt. The running program generated a object.txt file on the D drive. Here are the contents of the file:
There are garbled characters (the stream of bytes is caused by streams), but still does not affect us to tell whether the inside is the object we save.
The next step is to deserialize the person object out of the disk. The class to be used for the corresponding deserialization is ObjectInputStream, and the deserialization steps are as follows:
- Create a ObjectInputStream object;
- Use the ObjectInputStream ReadObject method to remove the object.
Next, refactor our code to implement the deserialization, as follows:
Package Com.zhyea.test;import Java.io.fileinputstream;import Java.io.fileoutputstream;import java.io.IOException; Import Java.io.objectinputstream;import java.io.objectoutputstream;import java.io.serializable;/** * Serialization Test class * * @ Author Robin * @date December 18, 2014 */public class Serialtest {public static void main (string[] args) {person Rob in = new Person ("Robin", 29); String Savepath = "D:\\object.txt"; Serialtest test = new Serialtest (); try {test.serialize (Robin, Savepath); Person person = (person) test.deserialize (Savepath); System.out.println ("Name:" + person.getname () + "Age:" + person.getage ()); } catch (IOException e) {e.printstacktrace (); } catch (ClassNotFoundException e) {e.printstacktrace (); }}/** * Implements serialization * * @param obj * object to be serialized * @param path * Save Address * @throws IOException * * PublIC void serialize (Object obj, String path) throws IOException {ObjectOutputStream oos = null; try {oos = new ObjectOutputStream (new FileOutputStream (path)); Oos.writeobject (obj); } finally {if (null! = Oos) oos.close (); }}/** * Deserializing Object * * @param path * location Saved by serialized object * @return * @throws IOException * @throws classnotfoundexception */public Object Deserialize (String path) throws IOException, Clas snotfoundexception {ObjectInputStream ois = null; try {ois = new ObjectInputStream (new FileInputStream (path)); return Ois.readobject (); } finally {if (null! = OIS) ois.close (); }}}/** * Serialization Test Object * * @author Robin * @date December 18, 2014 */class person implements Serializable {private static fi nal long serialversionuid = -6412852654889352693l; /** * * Name * * PRIvate String name; /** * Age */private int; Public person () {} public person (String name, int age) {this.name = name; This.age = age; } public String GetName () {return name; } public void SetName (String name) {this.name = name; } public int Getage () {return age; public void Setage (int.) {this.age = age; }}
There are a couple of things to note about object serialization and deserialization:
- Deserialization does not require initialization of objects through the constructor;
- If more than one object is written to a file using the serialization mechanism, the order of fetching and writing must be the same;
- When Java serializes an object of a class, if an object reference exists in the class (and the value is not null), the reference object of the class is serialized as well.
using transient
In some special scenarios, such as bank account objects, the deposit amount is not expected to be serialized for security reasons. Or, members of some reference types of a class are not serializable. You can use the Transient keyword to modify member variables that you do not want to be or cannot serialize.
Continue to tweak our code to do the demo:
Package Com.zhyea.test;import Java.io.fileinputstream;import Java.io.fileoutputstream;import java.io.IOException; Import Java.io.objectinputstream;import java.io.objectoutputstream;import java.io.serializable;/** * Serialization Test class * * @ Author Robin * @date December 18, 2014 */public class Serialtest {public static void main (string[] args) {person Rob in = new Person ("Robin", 29); School School = new School ("xx school"); Teacher trobin = new Teacher (Robin); Trobin.setschool (school); Trobin.setsalary (12.0); String Savepath = "D:\\object.txt"; Serialtest test = new Serialtest (); try {test.serialize (Savepath, Trobin); Teacher t = (Teacher) test.deserialize (Savepath); System.out.println ("Name:" + T.getperson (). GetName () + "Salary:" + t.getsalary ()); } catch (IOException e) {e.printstacktrace (); } catch (ClassNotFoundException e) { E.printstacktrace (); }}/** * Implements serialization * * @param obj * object to be serialized * @param path * Save Address * @throws IOException */public void serialize (String path, Object ... obj) throws IOException {...} /** * Deserializing Object * * @param path * location Saved by serialized object * @return * @throws IOException * @ Throws ClassNotFoundException */public Object Deserialize (String path) throws IOException, Classnotfoun dexception {...}} /** * Teacher class * @author Robin * @date December 18, 2014 */class Teacher implements serializable{private static final lo ng serialversionuid = -8751853088437904443l; private person person; private transient School School; private transient double salary; Public Teacher (person person) {This.person = person; }/* Omit get, set, please supplement */}/** * School class, non-serializable * * @author Robin * @date December 18, 2014 */class school{privAte String name; Public School (String name) {this.name = name; }/* Omit get, set, please self-supplement */}/** * Person class, Serializable * * @author Robin * @date December 18, 2014 */class person implements Serializable { ....}
In cases where the transient identity is not added to the school member of the teacher class, Notserializableexception is reported if the school value is not null. The exception information is as follows:
When the transient identity is not added to the salary member of the teacher class, the value of salary is output, and only the default initial value of salary is 0.0 when added.
It is important to note that transient can only be decorated with attributes (filed) and cannot be decorated with classes or methods.
Custom Serialization
Transient provides a neat way to completely isolate member properties that are transient decorated outside of the serialization mechanism. This is good, but Java also provides a custom serialization mechanism that gives developers more freedom to control how individual member properties are serialized, or to not serialize certain attributes (the same as the transient effect).
The following methods are required in a class that requires custom serialization and deserialization:
- private void WriteObject (ObjectOutputStream out)
- private void ReadObject (ObjectInputStream in)
- private void Readobjectnodata ()
First of all, the first two methods WriteObject and ReadObject, these two methods and ObjectOutputStream and ObjectInputStream corresponding method name is the same. In fact, although the two methods are private, they are still called by the external class ObjectOutputStream (or ObjectInputStream) during the serialization (or deserialization) phase. In the case of serialization only, ObjectOutputStream finds a custom WriteObject method by reflecting in the class of the object to be serialized (a bit around it) before executing its own WriteObject method, if any. The custom WriteObject method is called first. Because Getprivatemethod is used to find the reflection method, the scope of the custom WriteObject method is set to private. By customizing the WriteObject and ReadObject methods, you can fully control the serialization and deserialization of objects.
Here is the sample code:
Package Com.zhyea.test;import Java.io.fileinputstream;import Java.io.fileoutputstream;import java.io.IOException; Import Java.io.objectinputstream;import Java.io.objectoutputstream;import Java.io.serializable;import com.sun.xml.internal.ws.encoding.soap.deserializationexception;/** * Serialization Test class * * @author Robin * @date December 18, 2014 */PUBL IC class Serialtest {public static void main (string[] args) {person Robin = new Person ("Robin", 29); String Savepath = "D:\\object.txt"; Serialtest test = new Serialtest (); try {test.serialize (Savepath, Robin); Person person = (person) test.deserialize (Savepath); System.out.println ("Name:" + person.getname () + "Age:" + person.getage ()); } catch (IOException e) {e.printstacktrace (); } catch (ClassNotFoundException e) {e.printstacktrace (); }}/** * Implementation serialization * * @param obj * Object to be serialized * @param path * Save Address * @throws IOException */public void Seria Lize (String path, person ... obj) throws IOException {ObjectOutputStream oos = null; ... } /** * Deserializing Object * * @param path * location Saved by serialized object * @return * @throws IOException * @th Rows ClassNotFoundException */public Object Deserialize (String path) throws IOException, Classnotfounde xception {...}} /** * Person class, Serializable * * @author Robin * @date December 18, 2014 */class person implements Serializable {private static final Long serialversionuid = -6412852654889352693l; /** * Name * * * Private String name; /** * Age */private int; Public person () {} public person (String name, int age) {this.name = name; This.age = age; }/* Omit get and set, implement your own */private void WriteObject (ObjectOutputStream out) throws ioexception{OUT.WRiteobject (name); Out.writeint (age + 1); System.out.println ("My Write"); } private void ReadObject (ObjectInputStream in) throws IOException, classnotfoundexception{this.name = "Zha Ngsan "; This.age = 30; SYSTEM.OUT.PRINTLN ("my read"); }}
The following are the output results:
About Readobjectnodata, on the Internet to find the following paragraph description:
Readobjectnodata Original case Pojo public class person implements Serializable {private int Age Public person () {}//setter getter ...} Serializes person P = new person (); P.setage (10); ObjectOutputStream oos = new ObjectOutputStream (New FileOutputStream ("C:/person.ser")); Oos.writeobject (P); Oos.flush (); Oos.close (); After the class structure changes, the serialized data is unchanged Pojo Animal implements Serializable explicitly write Readobjectnodata public class Animal Implements Serializable {private String name; Public Animal () {}//setter getter ... private void Reado Bjectnodata () {this.name = "Zhangsan"; } } Person extends Animal public class person extends Animal implements Serializable { private int age; Public person () {}//Setter getter ...} Deserialization ObjectInputStream ois = new ObjectInputStream (New FileInputStream ("C:/person.ser")); Person sp = (person) ois.readobject (); System.out.println (Sp.getname ()); ReadObject, the readobjectnodata is called when
Readobjectnodata as I understand it, it looks like an exception handling mechanism that returns the correct value in the case of a serialized stream that is incomplete.
using Writereplace and Readresolve
Writereplace and Readresolve are a more thorough serialization mechanism that can even replace serialized target objects with other objects.
But unlike WriteObject and ReadObject, the two are not required to be used together, and should be used separately as far as possible. If used together, only writereplace will take effect.
The code can explain everything, first of all, Writereplace:
Package Com.zhyea.test;import Java.io.fileinputstream;import Java.io.fileoutputstream;import java.io.IOException; Import Java.io.objectinputstream;import Java.io.objectoutputstream;import Java.io.objectstreamexception;import java.io.serializable;/** * Serialization Test class * * @author Robin * @date December 18, 2014 */public class Serialtest {public static void Main (string[] args) {person Robin = new Person ("Robin", 29); String Savepath = "D:\\object.txt"; Serialtest test = new Serialtest (); try {//Serialization of Test.serialize (Savepath, Robin); Deserializes String person = (string) test.deserialize (Savepath); SYSTEM.OUT.PRINTLN (person); } catch (IOException e) {e.printstacktrace (); } catch (ClassNotFoundException e) {e.printstacktrace (); }}/** * Implements serialization * * @param obj * object to be serialized * @param path * Save Address* @throws IOException */public void serialize (String path, person ... obj) throws IOException {OBJECTOUTPU TStream oos = null; .... } /** * Deserializing Object * * @param path * location Saved by serialized object * @return * @throws IOException * @th Rows ClassNotFoundException */public Object Deserialize (String path) throws IOException, Classnotfounde xception {...}} /** * Person class, Serializable * * @author Robin * @date December 18, 2014 */class person implements Serializable {private static final Long serialversionuid = -6412852654889352693l; /** * Name * * * Private String name; /** * Age */private int; Public person () {} public person (String name, int age) {this.name = name; This.age = age; }/* Set and Get methods add your own */private Object Writereplace () throws objectstreamexception{System.out.pr Intln ("My Writereplace"); return "Robin"; } Private OBject Readresolve () throws objectstreamexception{System.out.println ("My Readresolve"); return "Zhangsan"; private void WriteObject (ObjectOutputStream out) throws ioexception{....} private void ReadObject (ObjectInputStream in) throws IOException, classnotfoundexception{...}}
The following is the result of the operation:
In the person class, the previous WriteObject and ReadObject methods are preserved, and the Readresolve method is added. However, it can be seen from the running results that none of the 3 methods are called, only the Writereplace method is used. It can be understood that other custom methods are not called when Writereplace is used, that is, Writereplace has the highest precedence.
Now comment out the Writereplace method and execute it again, the result is as follows:
This time the Writeobject,readobject and Readresolve methods are called. The Readresolve method is followed by the call of the ReadObject method and the value returned by the Readresolve is the value returned by the ReadObject, and the object generated by the deserialization is discarded.
There is also a point to note: Writereplace and readresolve can use any scope, which means that subclasses can also call these two methods of a superclass. However, if subclasses have different serialization and deserialization requirements, this requires subclasses to rewrite the method, and sometimes it is not necessary to do so. Therefore, the scope of the two methods is generally set to private.
using Externalizable
It was mentioned at the outset that implementing the Externalizable interface can also implement serialization of classes. Using this approach, it is up to the developer to decide exactly how to serialize and deserialize the target object. The Externalizable interface provides two methods of writeexternal and readexternal.
In fact, this approach is similar to the previous custom serialization method, except that externalizable enforces custom serialization. The Writereplace and Readresolve methods can still be used in classes that use Externalizable. Using Externalizable for serialization is slightly better than using serializable, but has a high degree of complexity.
Version Issue
You may encounter JRE version issues when you perform serialization and deserialization. This is especially true when communicating on both ends of the network.
To solve this problem, Java allows the serialized class to be provided with a Serialversionuid constant that identifies the version of the class. As long as the values of the Serialversionuid are constant, Java treats them as the same serialized version.
If you do not explicitly define SERIALVERSIONUID, the JVM calculates a serialversionuid value. Different serialversionuid values are generated under different compilers. Different Serialversionuid values can cause compilation to fail. You can use the Serial.exe in the bin directory of the JDK to view the serialversionuid of the serializable class, as instructed below:
Serial person
If modifications to a class do cause deserialization to fail, you should actively adjust the value of Serialversionuid. There are several scenarios in which the deserialization of a class fails to modify:
- It just modifies the method of the class and does not affect deserialization.
- Just modifies the static field or transient field of the class, and does not affect deserialization.
- Modifying the non-static and non-transient Field of a class can affect serialization.
Serialization Considerations
For the serialization of objects, the following considerations are summarized:
- The object's class name, field (including primitive types, arrays, and references to other objects) are serialized, and the static Field,transient Field and method of the object are not serialized;
- Classes that implement serializable interfaces, such as those that do not want a field to be serialized, can be decorated with the transient keyword;
- The class that guarantees the reference type of the serialized object filed is also serializable, such as non-serializable and can be decorated with the transient keyword, otherwise serialization fails;
- The class file of classes that must have a serialized object when deserializing;
- When serializing objects through a file network, they must be read in the order in which they are written.
Serialization and deserialization of Java objects