jy-03-JAVASE02 -- Java serialization

Posted by hwmetzger on Sat, 25 Sep 2021 07:18:21 +0200

This article focuses on Java serialization.

If you are interested in Java serialization, you can study it.

1, The role of Java serialization

Sometimes we want to transfer a Java object into a byte stream, and sometimes we want to restore a Java object from a byte stream. For example, sometimes we want to

When we write a Java object to the hard disk or transfer it to other computers on the network, we need to write the corresponding object into a byte stream through Java. For this general

Why don't we use a unified format? Yes, the concept of Java serialization appears here. The subclass ObjectOutput under the OutputStream class of Java-

The Stream class has a corresponding WriteObject(Object object), in which the corresponding object is required to implement the java serialization interface.

In order to better understand the application of java serialization, I give two examples I have encountered in development projects:

1) When using Tomcat to develop Java EE related projects, after we close tomcat, the objects in the corresponding session are stored on the hard disk. If we want to restart tomcat

If the contents in the corresponding session can be read from tomcat, the contents saved in the session must be serialized.

2) If the java object we use is to be used in distributed or rmi remote call network, the relevant object must implement java serialization interface.

Dear friends, you probably know the functions related to java serialization. Next, let's take a look at how to implement java serialization~

2, Realize the serialization and deserialization of java objects.

        There are two ways to serialize Java objects.

        a. It is the corresponding object that implements the Serializable interface, which is often used. For the Serializable interface, the Serializable interface is an empty interface, and its main function is

          This object can be serialized when it is identified, and the jre object will be encapsulated when it is transferred. I won't make too much introduction here.

          Here is an example of Java serialization that implements the serialization interface: very simple

12345678

package com.shop.domain;
        import java.util.Date;
public class Article implements java.io.Serializable {
    private static final long serialVersionUID = 1L;
    private Integer id;
    private String title;  //Article title
    private String content;  // Article content
    private String faceIcon;//Emoticon
    private Date postTime; //Time of publication
    private String ipAddr;  //User's ip
    private User author;  //Users replying
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    public String getContent() {
        return content;
    }
    public void setContent(String content) {
        this.content = content;
    }
    public String getFaceIcon() {
        return faceIcon;
    }
    public void setFaceIcon(String faceIcon) {
        this.faceIcon = faceIcon;
    }
    public Date getPostTime() {
        return postTime;
    }
    public void setPostTime(Date postTime) {
        this.postTime = postTime;
    }
    public User getAuthor() {
        return author;
    }
    public void setAuthor(User author) {
        this.author = author;
    }
    public String getIpAddr() {
        return ipAddr;
    }
    public void setIpAddr(String ipAddr) {
        this.ipAddr = ipAddr;
    }
}

b. the second way to realize serialization is to realize the interface Externalizable. Part of the source code of Externalizable is as follows:

* @see java.io.ObjectInput
        * @see java.io.Serializable
        * @since   JDK1.1
        */
public interface Externalizable extends java.io.Serializable {
/**
 * The object implements the writeExternal method to save its contents
 * by calling the methods of DataOutput for its primitive values or

  First, when serializing objects, this class implements Externalizable   Interface, which attributes can be serialized are defined in the writeExternal() method,

What can't be serialized? Therefore, the serialized objects that can be serialized are saved in the file. Those that can't be serialized are not processed, and then they are automatically called when deserializing

Use the readExternal() method to read the reverse sequence one by one according to the sequence order, and automatically encapsulate it into an object return, and then receive it in the test class to complete the reverse sequence.

  Therefore, exterminable is an extension of Serializable.

  In order to better understand the relevant contents, please see the following examples:

package com.xiaohao.test;
import java.io.Externalizable;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
    /**
     * Test entity class
     * @author Xiao Hao
     * @Creation date: March 12, 2015
     */
    class Person implements Externalizable{
        private static final long serialVersionUID = 1L;
        String userName;
        String password;
        String age;
        public Person(String userName, String password, String age) {
            super();
            this.userName = userName;
            this.password = password;
            this.age = age;
        }
        public Person() {
            super();
        }
        public String getAge() {
            return age;
        }
        public void setAge(String age) {
            this.age = age;
        }
        public String getUserName() {
            return userName;
        }
        public void setUserName(String userName) {
            this.userName = userName;
        }
        public String getPassword() {
            return password;
        }
        public void setPassword(String password) {
            this.password = password;
        }
        /** * Extension class for serialization operation */
        @Override
        public void writeExternal(ObjectOutput out) throws IOException {
            //Add a new object
            Date date=new Date();
            out.writeObject(userName);
            out.writeObject(password);
            out.writeObject(date);
        }
        /** * Deserialized extension class */
        @Override
        public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
            //Note that the order of acceptance here is limited. Otherwise, an error will occur
            // For example, if the object A is written first above, then the object A must be accepted first below
            userName=(String) in.readObject();
            password=(String) in.readObject();
            SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
            Date date=(Date)in.readObject();
            System.out.println("The date after deserialization is:"+sdf.format(date));
        }
        @Override public String toString() {
            //Note that the age here will not be serialized, so the data cannot be read during deserialization
            return "user name:"+userName+"password:"+password+"Age:"+age;
        }
    }
    /** * Serialization and deserialization related operation classes * @ author Xiaohao * @ creation date: March 12, 2015 */
    class Operate{
        /** * Serialization method * @ throws IOException * @throws FileNotFoundException */
        public void serializable(Person person) throws FileNotFoundException, IOException{
            ObjectOutputStream outputStream=new ObjectOutputStream(new FileOutputStream("a.txt"));
            outputStream.writeObject(person);
        }
        /** * Deserialization method * @ throws IOException * @throws FileNotFoundException * @throws ClassNotFoundException */
        public Person deSerializable() throws FileNotFoundException, IOException, ClassNotFoundException{
            ObjectInputStream ois=new ObjectInputStream(new FileInputStream("a.txt"));
            return (Person) ois.readObject();
        }
    }
    /** * Test entity main class * @ author Xiaohao * @ creation date: March 12, 2015 */
    public class Test{
        public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
            Operate operate=new Operate();
            Person person=new Person("Xiao Hao","123456","20");
            System.out.println("The relevant data before serialization is as follows:\n"+person.toString());
            operate.serializable(person);
            Person newPerson=operate.deSerializable();
            System.out.println("-------------------------------------------------------");
            System.out.println("The relevant data after serialization is as follows:\n"+newPerson.toString());
        }
    }

First, when serializing the UserInfo object, since this class implements the Externalizable interface, what attributes are defined in the writeExternal() method

To serialize, which can not be serialized. Therefore, the objects that can be serialized will be saved in the file after passing through. Those that cannot be serialized will not be processed, and then in the deserialization

The readExternal() method is automatically called to read the sequence one by one, reverse the sequence, and automatically encapsulate it into an object return, and then receive it in the test class to complete the reverse sequence.

 *** For the implementation of Java serialization interface, you should pay attention to the following points:

        1. When serializing in JAVA, transient variables (the keyword is used to tell JAVA that I cannot be serialized) and static variables will not be serialized (the following is a test example)

import java.io.*;

    class Student1 implements Serializable {
        private static final long serialVersionUID = 1L;
        private String name;
        private transient String password;
        private static int count = 0;

        public Student1(String name, String password) {
            System.out.println("call Student Construction method with parameters");
            this.name = name;
            this.password = password;
            count++;
        }

        public String toString() {
            return "Number of people: " + count + " full name: " + name + " password: " + password;
        }
    }

    public class ObjectSerTest1 {
        public static void main(String args[]) {
            try {
                FileOutputStream fos = new FileOutputStream("test.obj");
                ObjectOutputStream oos = new ObjectOutputStream(fos);
                Student1 s1 = new Student1("Zhang San", "12345");
                Student1 s2 = new Student1("Wang Wu", "54321");
                oos.writeObject(s1);
                oos.writeObject(s2);
                oos.close();
                FileInputStream fis = new FileInputStream("test.obj");
                ObjectInputStream ois = new ObjectInputStream(fis);
                Student1 s3 = (Student1) ois.readObject();
                Student1 s4 = (Student1) ois.readObject();
                System.out.println(s3);
                System.out.println(s4);
                ois.close();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e1) {
                e1.printStackTrace();
            }
        }
    }

1234567891011121314151617181920212223242526272829303132333435363738394041424344

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
    public class Test{
        public static void main(String args[]){
            try {
                FileInputStream fis = new FileInputStream("test.obj");
                ObjectInputStream ois = new ObjectInputStream(fis);
                Student1 s3 = (Student1) ois.readObject();
                Student1 s4 = (Student1) ois.readObject();
                System.out.println(s3);
                System.out.println(s4);
                ois.close();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e1) {
                e1.printStackTrace();
            }
        }
    }

            2. It should also be noted that if you serialize object A and then serialize object B, you must remember the objects read first according to JAVA during deserialization

                It is the object to be serialized first. Do not receive object B first, which will report an error. Especially when using the above Externalizable, you must pay attention to reading

                Order of.

            3. The object implementing the serialization interface is not forced to declare a unique serialVersionUID. Whether to declare the serialVersionUID for object serialization

              Top down compatibility has a great impact. Let's do a test:

Train of thought I  

Remove the serialVersionUID from the User and save it serially. During deserialization, add or reduce fields to see whether it is successful.  

Java code

public class User implements Serializable{
        private String name;
        private int age;
        private long phone;
        private List<UserVo> friends;
...<br>
    }

Save to file:

Java code
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    ObjectOutputStream os = new ObjectOutputStream(bos);
os.writeObject(src);
os.flush();
os.close();
    byte[] b = bos.toByteArray();
bos.close();
    FileOutputStream fos = new FileOutputStream(dataFile);
fos.write(b);
fos.close();

After adding or reducing fields, read them from the file and deserialize them:

Java code
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    ObjectOutputStream os = new ObjectOutputStream(bos);
os.writeObject(src);
os.flush();
os.close();
    byte[] b = bos.toByteArray();
bos.close();
    FileOutputStream fos = new FileOutputStream(dataFile);
fos.write(b);
fos.close();

Result: exception information is thrown  

Java code

Exception in thread "main" java.io.InvalidClassException: serialize.obj.UserVo; local class incompatible: stream classdesc serialVersionUID = 3305402508581390189, local class serialVersionUID = 7174371419787432394 at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:560)
    at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1582)
    at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1495)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1731)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1328)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:350)
    at serialize.obj.ObjectSerialize.read(ObjectSerialize.java:74)
    at serialize.obj.ObjectSerialize.main(ObjectSerialize.java:27)

Train of thought II  

eclipse specifies to generate a serialVersionUID, serialize and save it, and deserialize it after modifying the field  

Omit code  

Result: deserialization succeeded  

conclusion  

If the serialVersionUID is not explicitly specified, a serialVersionUID will be generated according to the field and specific algorithm during serialization. When the attribute changes, the id changes, so during deserialization  

Will fail. Throw "the unique id of the local classd does not match the unique id of the class in the stream".  

Description of serialVersionUID in jdk document:  

Write  

If a serializable class does not explicitly declare a serialVersionUID, the serialization runtime will calculate the default serialVersionUID value of the class based on all aspects of the class, as described in the Java(TM) object serialization specification. However, it is strongly recommended that all serializable classes explicitly declare the serialVersionUID value because calculating the default serialVersionUID is highly sensitive to the details of the class and may vary widely according to the compiler implementation, which may lead to unexpected InvalidClassException during deserialization. Therefore, in order to ensure the consistency of serialVersionUID values across different java compiler implementations, the serialization class must declare an explicit serialVersionUID value. It is also strongly recommended to use the private modifier to display the declaration of serialVersionUID (if possible), because this declaration applies only to directly declaring classes – the serialVersionUID field is not useful as an inherited member. Array classes cannot declare an explicit serialVersionUID, so they always have a default calculated value, but array classes have no requirement to match the serialVersionUID value.

3, Other ways to realize serialization (this is an extended content, which can be extended if you are interested)

    1) Is to wrap the object into a JSON string transmission.

      The JSON format is used here, and Google's gson-2.2.2.jar is used at the same time   Escape

  2) Using Google's ProtoBuf

      With the open source of Google tool protobuf, protobuf is also a good choice. For JSON,Object   Serialize (serialization and deserialization of Java),

      ProtoBuf   Make a comparison.

      Define a general object UserVo to be transferred:

  public class User
            <span style="white-space: pre;">private static final long serialVersionUID = -5726374138698742258L;</span>
    { private String name;
        private int age;
        private long phone;
        private List<User> friends;
 ...set and get method
    }
 

Initialize User instance src:

User user1 = new UserVo();
user1 .setName("user1 ");
 user1 .setAge(30);
 user1 .setPhone(13789126278L);
 UserVo f1 = new UserVo();
 f1.setName("tmac");
 f1.setAge(32);
 f1.setPhone(123L);
 User user2 = new User();
 user2 .setName("user2 ");
 user2 .setAge(29);
 user2 .setPhone(123L); <br> List<User> friends = new ArrayList<User>();
 friends.add(user1 );
 friends.add(user2 );
 user1 .setFriends(friends);

1. First, use JOSN to realize serialization.

Gson gson = new Gson();<br>String json = gson.toJson(src);

Obtained string:

 {"name":"user1 ","age":30,"phone":123,"friends":[{"name":"user1 ","age":32,"phone":123},{"name":"user2 ","age":29,"phone":123}]}

The number of bytes is 153  

Jason's advantages: the plaintext structure is clear at a glance, can cross languages, and the increase and decrease of attributes have little impact on the parsing end. Disadvantages: too many bytes, depending on different third-party class libraries.

Object Serialize (serialization and deserialization of Java)  

UserVo implements the Serializalbe interface and provides a unique version number:  

Serialization method:

 ByteArrayOutputStream bos = new ByteArrayOutputStream();
    ObjectOutputStream os = new ObjectOutputStream(bos);
os.writeObject(src);
os.flush();
os.close();
    byte[] b = bos.toByteArray();
bos.close();

Deserialization:

 ObjectInputStream ois = new ObjectInputStream(fis);
    vo = (UserVo) ois.readObject();
ois.close();
fis.close();

Advantages of Object Serializalbe: java Native support, no need to provide a third-party class library, and it is relatively simple to use.  

Disadvantages: it cannot cross language, occupies a large number of bytes, and is sensitive to changes in object attributes in some cases.  

When serializing and deserializing objects, you must implement the Serializable interface, but you are not forced to declare a unique serialVersionUID  

Whether to declare serialVersionUID has a great impact on the up-down compatibility of object serialization.

Google ProtoBuf 

protocol buffers is an internal transmission protocol of google. At present, the project has been open source( http://code.google.com/p/protobuf/).  

It defines a compact and extensible binary protocol format, which is suitable for network transmission, and has different versions for multiple languages.  

Take protobuf-2.5.0rc1 as an example, preparation:  

Download the source code, extract, compile and install  

Shell code  

tar zxvf protobuf-2.5.0rc1.tar.gz ./configure ./make ./make install 

Test:  

Shell code  

MacBook-Air:~ ming$ protoc –version libprotoc 2.5.0 

Installation succeeded!  

Enter the Java directory of the source code, compile and generate the required jar package with mvn tool, protobuf-java-2.5.0rc1.jar

1. Write a. Proto file and name it UserVo.proto

package serialize;
    option java_package = "serialize";
    option java_outer_classname="UserVoProtos";
    message User{
        optional string name = 1;
        optional int32 age = 2;
        optional int64 phone = 3;
        repeated serialize.UserVo friends = 4;
    }

2. Use the protoc ol tool to generate the builder class on the command line  

Shell code  

protoc -IPATH=.proto file directory – java_out=java file output path. proto name  

Get UserProtos class

3. Writing serialization code

 UserVoProtos.User.Builder builder = UserVoProtos.User.newBuilder();
builder.setName("Yaoming"); builder.setAge(30);
builder.setPhone(13789878978L);
    UserVoProtos.UserVo.Builder builder1 = UserVoProtos.UserVo.newBuilder();
builder1.setName("tmac"); builder1.setAge(32); builder1.setPhone(138999898989L);
    UserVoProtos.UserVo.Builder builder2 = UserVoProtos.UserVo.newBuilder();
builder2.setName("liuwei"); builder2.setAge(29); builder2.setPhone(138999899989L);
builder.addFriends(builder1);
builder.addFriends(builder2);
    UserVoProtos.UserVo vo = builder.build();  byte[] v = vo.toByteArray();

Number of bytes 53  

Deserialization

    UserVoProtos.UserVo uvo = UserVoProtos.UserVo.parseFrom(dstb);
System.out.println(uvo.getFriends(0).getName());

Result: tmac, deserialization succeeded  

Advantages of google protobuf: the number of bytes is very small, suitable for network transmission, saving io and cross language.  

Disadvantages: you need to rely on tools to generate code.

Working mechanism  

The proto file is a description of data, including field name, type and position in bytes. The protoc tool reads the proto file and generates a class library corresponding to the builder code. protoc xxxxx – java_out=xxxxxx generates a Java class library. The builder class serializes the data into a byte stream according to its own algorithm, or reverses the byte stream into an object according to the principle of reflection. The official example is Example: https://developers.google.com/protocol-buffers/docs/javatutorial.  

Correspondence between field types in proto file and java:  

See: https://developers.google.com/protocol-buffers/docs/proto 

.proto Type java Type c++ Type 

double double double 

float float float 

int32 int int32 

int64 long int64 

uint32 int uint32 

unint64 long uint64 

sint32 int int32 

sint64 long int64 

fixed32 int uint32 

fixed64 long uint64 

sfixed32 int int32 

sfixed64 long int64 

bool boolean bool 

string String string 

bytes byte string 

Description of field properties:  

Write  

required: a well-formed message must have exactly one of this field. optional: a well-formed message can have zero or one of this field (but not more than one). repeated: this field can be repeated any number of times (including zero) in a well-formed message. The order of the repeated values will be preserved. 

When serializing and deserializing protobuf, it depends on the builder class generated by the. proto file. If the field change is not reflected in the. proto file, it will not affect the deserialization, which is more suitable for the field change.  

Do a test: serialize UserVo into a file:

   UserProtos.User vo = builder.build();
    byte[] v = vo.toByteArray();
    FileOutputStream fos = new FileOutputStream(dataFile);
fos.write(vo.toByteArray());
fos.close();


    by User Add field, corresponding to.proto File:

    Text code

package serialize;
    option java_package = "serialize";
    option java_outer_classname="UserVoProtos";
    message User{
        optional string name = 1;
        optional int32 age = 2;
        optional int64 phone = 3;
        repeated serialize.UserVo friends = 4;
        optional string address = 5; }



    Deserialize from file:

    Java code
    FileInputStream fis = new FileInputStream(dataFile);
    byte[] dstb = new byte[fis.available()];
for(int i=0;i<dstb.length;i++){ dstb[i] = (byte)fis.read(); }
fis.close(); UserProtos.User uvo = UserProtos.User.parseFrom(dstb);
System.out.println(uvo.getFriends(0).getName());

12345678910111213141516171819202122232425262728293031

Deserialize from file:

Java code

FileInputStream fis = new FileInputStream(dataFile);
    byte[] dstb = new byte[fis.available()];
for(int i=0;i<dstb.length;i++){ dstb[i] = (byte)fis.read(); }
fis.close(); UserProtos.User uvo = UserProtos.User.parseFrom(dstb);
System.out.println(uvo.getFriends(0).getName());

12345

Successful results.

Compared with the three methods of transmitting the same data, google protobuf has only 53 bytes, which is the least. Conclusion:

Advantages and disadvantages of this method  

JSON 

Cross language and clear format at a glance  

The number of bytes is relatively large, and a third-party class library is required  

The Object Serialize java Native method does not rely on external class libraries. The number of bytes is large and cannot cross language  

Google protobuf 

Cross language, less bytes  

Write. proto configuration and generate corresponding code with protoc tool

By Darren

Tel: 15110448224

QQ: 603026148

Jin Ye owns the above contents. If there are any mistakes or deficiencies, please contact me and hope we can make progress together.

Topics: Java Struts Tomcat