A detailed explanation of the serialization of objects in Java programming _java

Source: Internet
Author: User
Tags object serialization serialization static class

1. What is Java object serialization

The Java platform allows us to create reusable Java objects in memory, but in general, these objects can exist only when the JVM is running, that is, the life cycle of these objects is no longer than the life cycle of the JVM. In real-world applications, however, it is possible to require that the specified object be saved (persisted) after the JVM has stopped running, and that the saved object be re-read in the future. Java object Serialization can help us implement this functionality.

With Java object serialization, when you save an object, it saves its state as a set of bytes, and then assembles the bytes into objects in the future. It is important to note that object serialization holds the object's "state", which is its member variable. Therefore, object serialization does not focus on static variables in the class.

In addition to object serialization when objects are persisted, object serialization is used when using RMI (remote method invocation) or passing objects across the network. The Java Serialization API provides a standard mechanism for processing object serialization, which is easy to use and is described in successive chapters of this article.

2. Simple example

In Java, as long as a class implements the Java.io.Serializable interface, it can be serialized. A serializable class person is created here, and all the examples in this article revolve around the class or its modified version.

Gender class, is an enumeration type that represents the gender

public enum Gender { 
  MALE, FEMALE 
} 

If you are familiar with the Java enumeration type, you should know that each enumeration type inherits the class Java.lang.Enum by default, and that the class implements the Serializable interface, so the enumeration type objects can be serialized by default.

The

Person class implements the Serializable interface, which contains three fields: name,string type; age,integer type; Gender,gender type. In addition, the ToString () method of the class is overridden to facilitate printing of the contents of the person instance.

 public class Person implements Serializable {private String name = NULL; 
 
  Private Integer age = null; 
 
  Private Gender Gender = null; 
  Public person () {System.out.println ("None-arg constructor"); 
    (String name, Integer age, Gender Gender) {System.out.println ("Arg constructor"); 
    THIS.name = name; 
    This.age = age; 
  This.gender = gender; 
  Public String GetName () {return name; 
  public void SetName (String name) {this.name = name; 
  Public Integer Getage () {return age; 
  public void Setage (Integer age) {this.age = age; 
  Public Gender Getgender () {return Gender; 
  public void Setgender (Gender Gender) {this.gender = Gender; 
  @Override public String toString () {return "[+ name +]," + Age + "," + Gender + "]; } 
} 

Simpleserial is a simple serializer that first saves a person object to a file person.out and then reads the stored person object from the file and prints the object.

public class Simpleserial {public 
 
  static void Main (string[] args) throws Exception { 
    file File = new file (' Person . Out "); 
 
    ObjectOutputStream oout = new ObjectOutputStream (new FileOutputStream (file)); 
    person who = new Person ("John", Gender.male); 
    Oout.writeobject (person); 
    Oout.close (); 
 
    ObjectInputStream oin = new ObjectInputStream (new FileInputStream (file)); 
    Object Newperson = Oin.readobject (); No cast to Person type 
    oin.close (); 
    System.out.println (Newperson); 
  } 
 

The result of the output from the above program is:

Arg constructor 
[John, MALE] 

It must be noted at this point that when the saved person object is re-read, no constructor for person is invoked, and it looks as if the person object was restored directly using bytes.

After the person object is saved to the Person.out file, we can read the file elsewhere to restore the object, but we must ensure that the classpath of the reader contains Person.class ( Even if the person class is not displayed when reading the person object, as shown in the previous example, ClassNotFoundException is thrown.

3. The role of Serializable

Why does a class implement the Serializable interface, and it can be serialized? In the example in the previous section, you use ObjectOutputStream to persist the object, which has the following code in the class:

private void WriteObject0 (Object obj, Boolean unshared) throws IOException { 
   ...
  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 ()); 
    } 
  } 
  ... 
} 

From the code above, if the type of the object being written is string, or an array, or an enum, or a serializable, the object can be serialized, otherwise the notserializableexception will be thrown.

4. Default serialization mechanism

If you just want a class to implement the serializable interface without any other processing, the default serialization mechanism is used. Using the default mechanism, when serializing an object, not only serializes the current object itself, but also serializes other objects referenced by the object, as well as other objects referenced by these other objects, which are also serialized, and so on. Therefore, if an object contains a member variable that is a container class object, and the elements contained in those containers are container class objects, then the serialization process is more complex and expensive.

5. Impact serialization

In real-world applications, there are times when the default serialization mechanism cannot be used. For example, you want to ignore sensitive data during serialization, or simplify the serialization process. Several methods that affect serialization are described below.

5.1 Transient keyword

When a field is declared as transient, the default serialization mechanism ignores the field. The age field in the person class is declared here as transient, as shown below,

public class Person implements Serializable { 
  ... 
  Transient private Integer age = null; 
  ... 
} 

To execute the simpleserial application, you will have the following output:

Arg constructor 
[John, NULL, MALE] 

Visible, the Age field is not serialized.

5.2 WriteObject () method and ReadObject () method

For the above field age, which has been declared as transitive, is there any other way to enable it to be serialized, in addition to removing the transitive keyword? One way to do this is to add two methods to the person class: WriteObject () and ReadObject (), as follows:

public class Person implements Serializable { 
  ... 
  Transient private Integer age = null; 
  ... 
 
  private void WriteObject (ObjectOutputStream out) throws IOException { 
    out.defaultwriteobject (); 
    Out.writeint (age); 
  } 
 
  private void ReadObject (ObjectInputStream in) throws IOException, ClassNotFoundException { 
    in.defaultreadobject () ; 
    Age = In.readint (); 
  } 
} 

The Defaultwriteobject () method in ObjectOutputStream is called first in the WriteObject () method, which executes the default serialization mechanism, as described in section 5.1, where the age field is ignored. The Writeint () method is then called to display the age field to the ObjectOutputStream. The function of ReadObject () is to read the object in the same way as the WriteObject () method. Once you execute the simpleserial application again, you will have the following output:

Arg constructor 
[John, MALE] 

It must be noted that WriteObject () and ReadObject () are private methods, so how are they invoked? There is no doubt that the use of reflection. Details can look at the Writeserialdata method in ObjectOutputStream, and the Readserialdata method in ObjectInputStream.

5.3 Externalizable Interface

Whether using the Transient keyword or using the writeobject () and ReadObject () methods, it is based on the serialization of serializable interfaces. Another serialization interface--externalizable is provided in the JDK, and after this interface, the serialization mechanism based on the serializable interface is invalidated. The person class is now modified as follows,

 public class Person implements externalizable {private String name = NULL; 
 
  Transient private Integer age = null; 
 
  Private Gender Gender = null; 
  Public person () {System.out.println ("None-arg constructor"); 
    (String name, Integer age, Gender Gender) {System.out.println ("Arg constructor"); 
    THIS.name = name; 
    This.age = age; 
  This.gender = gender; 
    private void WriteObject (ObjectOutputStream out) throws IOException {Out.defaultwriteobject (); 
  Out.writeint (age); } private void ReadObject (ObjectInputStream in) throws IOException, ClassNotFoundException {IN.DEFAULTREADOBJEC 
    T (); 
  Age = In.readint (); @Override public void Writeexternal (ObjectOutput out) throws IOException {} @Override public void 
Readexternal (ObjectInput in) throws IOException, ClassNotFoundException {} ...} 

After you execute the simpleserial program, you will get the following results:

Arg constructor 
None-arg constructor 
[null, NULL, NULL] 

From this result, on the one hand, you can see that none of the fields in the person object are serialized. On the other hand, if you are careful, you can also find that this serialization process calls the person class's parameterless constructor.

Externalizable inherits from Serializable, and when the interface is used, the details of serialization need to be done by the programmer. As shown in the code, the serialization behavior will not save/read any of the fields because the writeexternal () and the Readexternal () method do not handle anything. This is why the values for all fields in the output result are null.

In addition, when you use externalizable for serialization, when you read an object, the parameterless constructor of the serialized class is invoked to create a new object, which then populates the values of the fields of the saved object into the new object, respectively. This is why the parameterless constructor for the person class is invoked during the serialization process. For this reason, the class that implements the Externalizable interface must provide a parameterless constructor, and its access rights are public.

Make further modifications to the person class above to enable it to serialize the name and age fields, but ignore the gender field, as shown in the following code:

public class Person implements externalizable {private String name = NULL; 
 
  Transient private Integer age = null; 
 
  Private Gender Gender = null; 
  Public person () {System.out.println ("None-arg constructor"); 
    (String name, Integer age, Gender Gender) {System.out.println ("Arg constructor"); 
    THIS.name = name; 
    This.age = age; 
  This.gender = gender; 
    private void WriteObject (ObjectOutputStream out) throws IOException {Out.defaultwriteobject (); 
  Out.writeint (age); } private void ReadObject (ObjectInputStream in) throws IOException, ClassNotFoundException {IN.DEFAULTREADOBJEC 
    T (); 
  Age = In.readint (); 
    @Override public void Writeexternal (ObjectOutput out) throws IOException {out.writeobject (name); 
  Out.writeint (age); @Override public void Readexternal (ObjectInput in) throws IOException, classnotfoundexception {name = (Str 
    ing) in.readobject (); Age= In.readint (); 
 } 
  ... 
}

After performing the simpleserial, the following results are available:

Arg constructor 
None-arg constructor 
[John, NULL] 

5.4 Readresolve () method

When we use the singleton pattern, it should be expected that an instance of a class should be unique, but if the class is serializable, the situation may be slightly different. The person class used in section 2nd is modified to implement Singleton mode, as follows:

public class Person implements Serializable {private static class Instanceholder {private static final person 
  Instatnce = new Person ("John", Gender.male); 
  public static Person getinstance () {return instanceholder.instatnce; 
 
  Private String name = NULL; 
 
  Private Integer age = null; 
 
  Private Gender Gender = null; 
  Private person () {System.out.println ("None-arg constructor"); 
    Private person (String name, Integer age, Gender Gender) {System.out.println ("Arg constructor"); 
    THIS.name = name; 
    This.age = age; 
  This.gender = gender; 
} 
  ... 
} At the same time, modify the simpleserial application so that you can save/retrieve the single Instance object and compare the object equality, as shown in the following code: public class Simpleserial {public static void main (string[) 
    args) throws Exception {File File = new file ("Person.out"); 
    ObjectOutputStream oout = new ObjectOutputStream (new FileOutputStream (file)); Oout.writeobject (Person.getinstance ()); 
 
    Save a single Instance object Oout.close (); ObjectinputstReam oin = new ObjectInputStream (new FileInputStream (file)); 
    Object Newperson = Oin.readobject (); 
    Oin.close (); 
 
    System.out.println (Newperson); System.out.println (person.getinstance () = = Newperson); 

 Equality comparison of acquired objects with a single instance object in the Person class}

The following results are obtained when you execute the above application:

Arg constructor 
[John, MALE] 
false 

It is noteworthy that the person object obtained from the file person.out is not equal to the single Instance object in the person class. In order to maintain the characteristics of a single case in the serialization process, you can add a readresolve () method in the person class that returns the single instance object of person directly in the method, as follows:

public class Person implements Serializable { 
 
  private static class Instanceholder { 
    private static final Statnce = new Person ("John", Gender.male); 
  } 
 
  public static Person getinstance () {return 
    instanceholder.instatnce; 
  } 
 
  Private String name = NULL; 
 
  Private Integer age = null; 
 
  Private Gender Gender = null; 
 
  Private person () { 
    System.out.println ("None-arg constructor"); 
  } 
 
  Private person (String name, Integer age, Gender Gender) { 
    System.out.println ("Arg constructor"); 
    this.name = name; 
    This.age = age; 
    This.gender = gender; 
  } 
 
  Private Object Readresolve () throws Objectstreamexception {return 
    instanceholder.instatnce; 
  } 
  ... 
} 

After you perform the simpleserial application of this section again, the following output will be:

Arg constructor 
[John, MALE] 
True 

Either implementing the Serializable interface or the Externalizable interface, the Readresolve () method is invoked when the object is read from the I/O stream. In effect, the object that is created during deserialization is replaced directly with the object returned in Readresolve ().

6. Some advanced usage
All that is said is done in the comments. Just give it to the program.

Package TEST.JAVAPUZZLER.P5;
Import java.io.*;
Import Java.io.ObjectInputStream.GetField;

Import Java.io.ObjectOutputStream.PutField; A class implements serializable to indicate that it can be serialized;//one thing that needs special attention is://If the subclass implements serializable and the parent class does not, the parent class is not serialized; public class Serializableobject implements Serializable {//generated serialized version number varies depending on the compilation environment, the declared class name, the member name, and the number of changes;//That is, the version number records the definition of the class in some way, If the definition of a class changes, it is best to regenerate the version number;//If the new code uses the old version number, it is compatible to read the old class's bytecode without an error when deserializing; private static final long Serialversionuid = 903854

 2591452547920L;
 public String name;
 public String password;
 If you do not want a non-static member to be serialized, you can use transient to modify it; public transient int age;

 Static members are not serialized because the serialization holds the state information of the instance, while the static member is the state information of the class; public static int version = 1;
 Public Serializableobject (string name, string password) {this.name = name;
 This.password = password; //Each class can write a writeobject method that will be responsible for the serialization of the class itself;//For sensitive information such as password, can be encrypted and then serialized;//This process requires Putfield, which can specify which fields will be serialized,
 How to serialize (such as encryption); If this method is not defined, the ObjectOutputStream defaultwriteobject will be invoked;//You can comment out the ReadoBject method, and then run the test case to test whether the password is encrypted; private void WriteObject (ObjectOutputStream out) throws IOException {Putfield putfields = O
 Ut.putfields ();
 Putfields.put ("name", name);
 Analog Encryption Password Putfields.put ("password", "Thepassword:" + password);
 Out.writefields (); //Each class can write a ReadObject method, the method is responsible for the deserialization process of the class itself;//such as decrypting the encrypted password when serialized;//This process requires GetField, he can read each domain concretely, or perform decryption action, etc.////If no definition of this method, the ObjectInputStream defaultreadobject is invoked, private void ReadObject (ObjectInputStream in) throws Classnotfoundexcep
 tion, IOException {GetField readfields = In.readfields ();
 After reading the value of the member, it is assigned directly to the field, that is, to complete the deserialization of the domain; name = (String) readfields.get ("name", "DefaultName");
 Analog decryption password String Encpassword = (string) readfields.get ("Password", "Thepassword:defaultvalue");
 Password = encpassword.split (":") [1]; ///serialization///primary use to objectoutputstream; public void Save () throws IOException {FileOutputStream fout = new Fileoutputstr
 EAM ("E:\\obj");
 ObjectOutputStream oout = new ObjectOutputStream (fout); Oout.writeobject (this);
 Oout.close ();
 Fout.close (); ///deserialization///primary use to ObjectInputStream public static serializableobject load () throws IOException, Classnotfoundexceptio
 n {fileinputstream fin = new FileInputStream ("E:\\obj");
 ObjectInputStream oin = new ObjectInputStream (FIN);
 Object o = Oin.readobject ();

 Return (Serializableobject) O;
 @Override public String toString () {return "Name: + name +", Password: "+ password; }//test case public static void main (string[] args) throws IOException, ClassNotFoundException {serializableobject
 = new Serializableobject ("http://blog.csdn.net/sunxing007", "123456");
 So.save ();
 System.out.println (SO);
 System.out.println (Serializableobject.load ());

 }

}

Serialization is bad for a single case pattern because it can be deserialized to break a single case. This is the time to ask for readresolve this method. For example, the following program:

public class Dog extends Exception {
 //private static final long serialversionuid = -7156412195888553079l;
 public static final Dog INSTANCE = new Dog ();
 Private Dog () {} public
 String toString () {return
 "Woof";
 }
 By Readresolve, the return object can be handled completely autonomously when deserialization is committed.
 Private Object Readresolve () {return
      INSTANCE;
    }
 public static void Main (string[] args) throws IOException, classnotfoundexception{Dog
 d = dog.instance;
 Bytearrayoutputstream bro = new Bytearrayoutputstream ();
 ObjectOutputStream oout = new ObjectOutputStream (bro);
 Oout.writeobject (d);
 ObjectInputStream oin = new ObjectInputStream (New Bytearrayinputstream (Bro.tobytearray ()));
 Dog D1 = (Dog) oin.readobject ();
 System.out.println (D1==d);
 }

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.