Serialization: the process of converting a java object to a byte sequence is called serialization.
Deserialization: the process of converting byte objects to java objects is called deserialization.
In general, serialization has two purposes :,
1) store the object's byte sequence permanently on the hard disk.
2) Transmit the object's byte sequence on the network
Corresponding API
Java. io. ObjectOutputStream
WriteObject (Object obj)
Java. io. ObjectInputStream
ReadObject ()
Only objects of classes that implement the Serializable or Externalizable interface can be serialized. Otherwise, IOException occurs when the writeObject method is called.
Note that the Externalizable interface inherits from the Serializable interface. The differences between the two are as follows:
Only the classes that implement the Serializable interface should adopt the default serialization method. For example, the String class.
Suppose there is a Customer class object to be serialized. If this class only implements this interface, the serialization and deserialization methods are as follows: ObjectOutputStream adopts the default serialization method, serialize non-static and non-transient instance variables of this class. ObjectInputStream adopts the default deserialization method. It deserializes non-static and non-transient instance variables of this class.
If this class not only implements the Serializable interface, but also defines the readObject (ObjectInputStream in) and writeObject (ObjectOutputStream out) methods, it will be serialized and deserialized as follows: objectOutputStream calls the writeObject method of this class for serialization. ObjectInputStream calls the corresponding readObject Method for deserialization.
The class implementing the Externalizable interface controls the serialization behavior completely by itself. WriteExternal (ObjectOutput out) and readExternal (ObjectInput in) must be implemented ). The serialization and deserialization will follow the following method: ObjectOutputStream will call the writeExternal method of this class for serialization, and ObjectInputStream will call the corresponding readExternal Method for deserialization.
The following is a simple example:
Package com. java; import java. io. fileInputStream; import java. io. fileOutputStream; import java. io. objectInputStream; import java. io. objectOutputStream; import java. io. serializable; public class simpleSerializableTest {public static void main (String [] args) throws Exception {ObjectOutputStream out = new ObjectOutputStream (new FileOutputStream ("d :\\ objectFile. obj "); String strObj =" name "; Customer customer = new Customer (" rollen "); // serialize the same object twice out. writeObject (strObj); out. writeObject (customer); out. writeObject (customer); out. close (); // deserialize ObjectInputStream in = new ObjectInputStream (new FileInputStream ("d: \ objectFile. obj "); String strobj1 = (String) in. readObject (); Customer cus1 = (Customer) in. readObject (); Customer cus2 = (Customer) in. readObject ();
In. close (); System. out. println (strobj1 + ":" + cus1); System. out. println (strObj = strobj1); System. out. println (cus1 = customer); System. out. println (cus1 = cus2) ;}} class Customer implements Serializable {private static final long serialVersionUID = 1L; private String name; public Customer () {System. out. println ("construction method without Parameters");} public Customer (String name) {System. out. println ("constructor with Parameters"); this. name = name;} public String toString () {return "[" + name + "]" ;}}
Output result:
Construction Method with Parameters
Name: [rollen]
False
False
True
It can be seen that the class constructor is not called during Reverse Sequence calls. Instead, they directly create new objects in memory based on their serialized data. In addition, if the same object is serialized multiple times by an ObjectOutputStream object, the deserialization of the right objectInputStream object is also the same object. (The result of cus1 = cus2 is true)
Check a piece of code to prove that static is not serialized:
Package com. java; import java. io. IOException; import java. io. objectOutputStream; import java. io. serializable; import java.net. serverSocket; import java.net. socket; public class SerializableServer {public void send (Object obj) throws IOException {ServerSocket serverSocket = new ServerSocket (8000); while (true) {Socket socket Socket = serverSocket. accept (); ObjectOutputStream out = new ObjectOutputStream (socket. getOutputStream (); out. writeObject (obj); out. writeObject (obj); out. close (); socket. close () ;}} public static void main (String [] args) throws Exception {Customer customer = new Customer ("rollen", "male"); new SerializableServer (). send (customer) ;}} class Customer implements Serializable {private static final long serialVersionUID = 1L; private String name; private static int count; private transient String sex; static {System. out. println ("Call static code block");} public Customer () {System. out. println ("construction method without Parameters");} public Customer (String name, String sex) {System. out. println ("constructor with Parameters"); this. name = name; this. sex = sex; count ++;} public String toString () {return "[" + count + "" + name + "" + sex + "]" ;}}
package com.java;import java.io.ObjectInputStream;import java.net.Socket;public class SerializableClient {public void recive() throws Exception {Socket socket = new Socket("localhost", 8000);ObjectInputStream in = new ObjectInputStream(socket.getInputStream());Object obj1 = in.readObject();Object obj2 = in.readObject();System.out.println(obj1);System.out.println(obj1==obj2);}public static void main(String[] args) {try {new SerializableClient().recive();} catch (Exception e) {e.printStackTrace();}}}
In the running result, the value of count is 0.
Let's look at another situation:
class A implements Serializable{B b;//...}class B implements Serializable{//...}
When we serialize an object of A, it will also automatically serialize the object of B associated with it. That is to say, by default, the object output stream serializes the entire object graph. As a result, the following problems may occur. Check the code (in this example, a bidirectional list is used as the internal structure, but the demo is provided, and the complete implementation is not provided, just to illustrate the situation ):
Package com. java; import java. io. byteArrayInputStream; import java. io. byteArrayOutputStream; import java. io. objectInputStream; import java. io. objectOutputStream; import java. io. serializable; public class SeriListTest implements Serializable {private static final long serialVersionUID = 1L; private int size; private Node head = null; private Node end = null; private static class Node implements Serializable {Private static final long serialVersionUID = 1L; String data; Node next; Node previous;} // add a public void add (String data) String at the end of the list) {Node node = new Node (); node. data = data; node. next = null; node. previous = end; if (null! = End) {end. next = node;} size ++; end = node; if (size = 1) {head = end ;}} public int getSize () {return size ;} // other methods... public static void main (String [] args) throws Exception {SeriListTest list = new SeriListTest (); for (int I = 0; I <10000; ++ I) {list. add ("rollen" + I);} ByteArrayOutputStream buf = new ByteArrayOutputStream (); ObjectOutputStream out = new ObjectOutputStream (buf); out. writeObject (list); ObjectInputStream in = new ObjectInputStream (new ByteArrayInputStream (buf. toByteArray (); list = (SeriListTest) in. readObject (); System. out. println ("size is:" + list. getSize ());}}
The following error occurs in this Code:
Exception in thread "main" java. lang. StackOverflowError
At java. lang. ref. ReferenceQueue. poll (ReferenceQueue. java: 82)
At java. io. ObjectStreamClass. processQueue (ObjectStreamClass. java: 2234)
....
The whole problem is caused by serialization of the entire object graph during serialization. In this case, we need to customize the serialization process:
Package com. java; import java. io. byteArrayInputStream; import java. io. byteArrayOutputStream; import java. io. IOException; import java. io. objectInputStream; import java. io. objectOutputStream; import java. io. serializable; public class SeriListTest implements Serializable {private static final long serialVersionUID = 1L; transient private int size; transient private Node head = null; transient private Node end = Null; private static class Node implements Serializable {private static final long serialVersionUID = 1L; String data; Node next; Node previous ;} // add a public void add (String data) {Node node = new Node (); node at the end of the list. data = data; node. next = null; node. previous = end; if (null! = End) {end. next = node;} size ++; end = node; if (size = 1) {head = end ;}} public int getSize () {return size ;} // other methods... private void writeObject (ObjectOutputStream outStream) throws IOException {outStream. defaultWriteObject (); outStream. writeInt (size); for (Node node = head; node! = Null; node = node. next) {outStream. writeObject (node. data) ;}} private void readObject (ObjectInputStream inStream) throws IOException, ClassNotFoundException {inStream. defaultReadObject (); int count = inStream. readInt (); for (int I = 0; I <count; ++ I) {add (String) inStream. readObject () ;}} public static void main (String [] args) throws Exception {SeriListTest list = new SeriListTest (); for (int I = 0; I <10000; ++ I) {list. add ("rollen" + I);} ByteArrayOutputStream buf = new ByteArrayOutputStream (); ObjectOutputStream out = new ObjectOutputStream (buf); out. writeObject (list); ObjectInputStream in = new ObjectInputStream (new ByteArrayInputStream (buf. toByteArray (); list = (SeriListTest) in. readObject (); System. out. println ("size is:" + list. getSize ());}}
Running result: 10000
Now let's summarize the situations in which we need to customize the serialization method:
1) encryption of some sensitive information to ensure serialization Security
2) ensure that the object's member variables meet the correct Constraints
3) optimized serialization performance (this has been explained in the previous example)
The following is an example:
First take a look: to ensure the security of serialization, encryption of some sensitive information
Package com. java; import java. io. byteArrayInputStream; import java. io. byteArrayOutputStream; import java. io. IOException; import java. io. objectInputStream; import java. io. objectOutputStream; import java. io. serializable; public class SeriDemo1 implements Serializable {private String name; transient private String password; // note the transientpublic SeriDemo1 () {} public SeriDemo1 (String name, String password) {this. name = name; this. password = password;} // The password is encrypted here. The simplified private String change (String password) {return password + "rollen";} private void writeObject (ObjectOutputStream outStream) throws IOException {outStream. defaultWriteObject (); outStream. writeObject (change (password);} private void readObject (ObjectInputStream inStream) throws IOException, ClassNotFoundException {inStream. defaultReadObject (); String strPassowrd = (String) inStream. readObject (); // simulate password decryption password = strPassowrd. substring (0, strPassowrd. length ()-6) ;}@ Overridepublic String toString () {return "SeriDemo1 [name =" + name + ", password =" + password + "]";} public static void main (String [] args) throws Exception {SeriDemo1 demo = new SeriDemo1 ("hello", "1234"); ByteArrayOutputStream buf = new ByteArrayOutputStream (); objectOutputStream out = new ObjectOutputStream (buf); out. writeObject (demo); ObjectInputStream in = new ObjectInputStream (new ByteArrayInputStream (buf. toByteArray (); demo = (SeriDemo1) in. readObject (); System. out. println (demo );}}
Then let's take a look: Make sure that the object's member variables meet the correct constraints. For example, in general, we will check the validity of the parameters in the constructor, but the default serialization does not call the constructor of the class, and an object is constructed directly from the serialized data of the object, in this case, we may provide serialized data that is inherited illegally to construct an object that does not meet the constraints.
To avoid this situation, we can customize the anti-sequence method. For example, check in the readObject method. When the data does not meet the constraints (for example, when the age is less than 0 and so on), an exception can be thrown.
Next let's take a look at the usage of the readResolve () method in singleton mode:
The Singleton mode should be clear to everyone. I will not talk about it. Let's take a look at the following code:
package com.java;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.io.Serializable;public class ReadResolveDemo implements Serializable {private static final long serialVersionUID = 1L;private ReadResolveDemo() {}public static ReadResolveDemo getInstance() {return new ReadResolveDemo();}public static void main(String[] args) throws Exception {ReadResolveDemo demo=ReadResolveDemo.getInstance();ByteArrayOutputStream buf = new ByteArrayOutputStream();ObjectOutputStream out = new ObjectOutputStream(buf);out.writeObject(demo);ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(buf.toByteArray()));ReadResolveDemo demo1 = (ReadResolveDemo) in.readObject();System.out.println(demo==demo1); //false}}
In the singleton mode, there is only one instance. However, during serialization, a new object will be generated during deserialization regardless of the default or custom method, therefore, the program running above outputs false.
So we can see that deserialization breaks the convention that the singleton mode only has one instance. To avoid this situation, we can useReadReslove:
package com.java;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.io.Serializable;public class ReadResolveDemo implements Serializable {private static final long serialVersionUID = 1L;private static final ReadResolveDemo INSTANCE = new ReadResolveDemo();private ReadResolveDemo() {}public static ReadResolveDemo getInstance() {return INSTANCE;}private Object readResolve() {return INSTANCE;}public static void main(String[] args) throws Exception {ReadResolveDemo demo = ReadResolveDemo.getInstance();ByteArrayOutputStream buf = new ByteArrayOutputStream();ObjectOutputStream out = new ObjectOutputStream(buf);out.writeObject(demo);ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(buf.toByteArray()));ReadResolveDemo demo1 = (ReadResolveDemo) in.readObject();System.out.println(demo == demo1); // true}}
Finally, let's briefly introduce how to implement the Externalizable interface.The class implementing the Externalizable interface controls the serialization behavior completely by itself. WriteExternal (ObjectOutput out) and readExternal (ObjectInput in) must be implemented ).
Note that when deserializing the object implementing this interface, the class constructor without parameters will be called first, which is different from the default deserialization method.
Example:
Package com. java; import java. io. byteArrayInputStream; import java. io. byteArrayOutputStream; import java. io. externalizable; import java. io. IOException; import java. io. objectInput; import java. io. objectInputStream; import java. io. objectOutput; import java. io. objectOutputStream; public class ExternalizableDemo implements Externalizable {private String name; static {System. out. println ("Call static code block");} public ExternalizableDemo () {System. out. println ("No parameter constructor is called by default");} public ExternalizableDemo (String name) {this. name = name; System. out. println ("calling constructors with Parameters");} @ Overridepublic void writeExternal (ObjectOutput out) throws IOException {out. writeObject (name) ;}@ Overridepublic void readExternal (ObjectInput in) throws IOException, ClassNotFoundException {name = (String) in. readObject () ;}@ Overridepublic String toString () {return "[" + name + "]";} public static void main (String [] args) throws Exception {ExternalizableDemo demo = new ExternalizableDemo ("rollen"); ByteArrayOutputStream buf = new ByteArrayOutputStream (); ObjectOutputStream out = new ObjectOutputStream (buf); out. writeObject (demo); ObjectInputStream in = new ObjectInputStream (new ByteArrayInputStream (buf. toByteArray (); demo = (ExternalizableDemo) in. readObject (); System. out. println (demo );}}
Output:
Call static code blocks
Call a constructor with Parameters
Call the default no-argument Constructor
[Rollen]
References:
1. advanced understanding of java serialization