Home  >  Article  >  Java  >  Talk about serialization and deserialization in Java objects

Talk about serialization and deserialization in Java objects

青灯夜游
青灯夜游forward
2020-07-03 10:03:592451browse

Talk about serialization and deserialization in Java objects

1. The concept of serialization and deserialization

Convert the object to The process of byte sequence is called serialization of an object.

The process of restoring a byte sequence into an object is called object deserialization.

The serialization of objects has two main uses:

1) Permanently save the object's byte sequence to the hard disk, usually in a file;

2) Transmit the byte sequence of the object over the network.

In many applications, certain objects need to be serialized so that they can leave the memory space and move to the physical hard disk for long-term storage. For example, the most common one is the Session object in the Web server. When 100,000 users access it concurrently, there may be 100,000 Session objects, which may be too much for the memory, so the Web container will serialize some sessions to the hard disk first, and so on. When you want to use it, restore the object saved in the hard disk to the memory.

When two processes are communicating remotely, they can send various types of data to each other. No matter what type of data it is, it is transmitted over the network in the form of a binary sequence. The sender needs to convert this Java object into a byte sequence before it can be transmitted over the network; the receiver needs to restore the byte sequence into a Java object.

2. Serialization API in JDK class library

java.io.ObjectOutputStream represents the object output stream, and its writeObject(Object obj ) method can serialize the obj object specified by the parameter and write the resulting byte sequence to a target output stream.
 java.io.ObjectInputStream represents an object input stream. Its readObject() method reads a sequence of bytes from a source input stream, deserializes them into an object, and returns it.
Only objects of classes that implement the Serializable and Externalizable interfaces can be serialized. Externalizable interface inherits from Serializable interface. Classes that implement the Externalizable interface completely control the serialization behavior by themselves, while classes that only implement the Serializable interface can Use the default serialization method.
 Object serialization includes the following steps:
 1) Create an object output stream, which can wrap a target output stream of other types, such as a file output stream;
 2) Through writeObject() of the object output stream Method writes object.

The steps for object deserialization are as follows:
1) Create an object input stream, which can wrap an other type of source input stream, such as a file input stream;
2) Pass the object input stream The readObject() method reads the object.

Object serialization and deserialization examples:

Define a Person class and implement the Serializable interface

import java.io.Serializable;

/**
 * <p>ClassName: Person<p>
 * <p>Description:测试对象序列化和反序列化<p>
 * @author xudp
 * @version 1.0 V
 * @createTime 2014-6-9 下午02:33:25
 */
public class Person implements Serializable {

    /**
     * 序列化ID
     */
    private static final long serialVersionUID = -5809782578272943999L;
    private int age;
    private String name;
    private String sex;

    public int getAge() {
        return age;
    }

    public String getName() {
        return name;
    }

    public String getSex() {
        return sex;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }
}

  Serializing and deserializing Person class objects

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.text.MessageFormat;

/**
 * <p>ClassName: TestObjSerializeAndDeserialize<p>
 * <p>Description: 测试对象的序列化和反序列<p>
 * @author xudp
 * @version 1.0 V
 * @createTime 2014-6-9 下午03:17:25
 */
public class TestObjSerializeAndDeserialize {

    public static void main(String[] args) throws Exception {
        SerializePerson();//序列化Person对象
        Person p = DeserializePerson();//反序列Perons对象
        System.out.println(MessageFormat.format("name={0},age={1},sex={2}",
                                                 p.getName(), p.getAge(), p.getSex()));
    }

    /**
     * MethodName: SerializePerson
     * Description: 序列化Person对象
     * @author xudp
     * @throws FileNotFoundException
     * @throws IOException
     */
    private static void SerializePerson() throws FileNotFoundException,
            IOException {
        Person person = new Person();
        person.setName("gacl");
        person.setAge(25);
        person.setSex("男");
        // ObjectOutputStream 对象输出流,将Person对象存储到E盘的Person.txt文件中,完成对Person对象的序列化操作
        ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(
                new File("E:/Person.txt")));
        oo.writeObject(person);
        System.out.println("Person对象序列化成功!");
        oo.close();
    }

    /**
     * MethodName: DeserializePerson
     * Description: 反序列Perons对象
     * @author xudp
     * @return
     * @throws Exception
     * @throws IOException
     */
    private static Person DeserializePerson() throws Exception, IOException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
                new File("E:/Person.txt")));
        Person person = (Person) ois.readObject();
        System.out.println("Person对象反序列化成功!");
        return person;
    }

}

The code running results are as follows:

Talk about serialization and deserialization in Java objects

After serializing Person successfully A Person.txt file is generated on the E drive, and the deserialized Person generates a Person object after reading the Person.txt of the E drive

3. The role of serialVersionUID

serialVersionUID: literally means the serialized version number. Every class that implements the Serializable interface has a static variable representing the serialized version identifier

private static final long serialVersionUID

If a class that implements the Serializable interface does not add serialVersionUID to the class, the following warning will appear

Talk about serialization and deserialization in Java objects

Click with the mouse A dialog box for generating serialVersionUID will pop up, as shown below:

Talk about serialization and deserialization in Java objects

There are two ways to generate serialVersionUID:

Use this The serialVersionUID generated by this method is 1L, for example:

private static final long serialVersionUID = 1L;

The serialVersionUID generated by is based on the class name, interface name, method and attribute, etc., for example:

private static final long serialVersionUID = 4603642343377807741L;

After adding it, the warning prompt will not appear, as shown below:

Talk about serialization and deserialization in Java objects

After so much, what is the serialVersionUID (serialized version number)? What's the use? Let's use the following example to illustrate the role of serialVersionUID. Look at the following code:

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class TestSerialversionUID {

    public static void main(String[] args) throws Exception {
        SerializeCustomer();// 序列化Customer对象
        Customer customer = DeserializeCustomer();// 反序列Customer对象
        System.out.println(customer);
    }

    /**
     * MethodName: SerializeCustomer
     * Description: 序列化Customer对象
     * @author xudp
     * @throws FileNotFoundException
     * @throws IOException
     */
    private static void SerializeCustomer() throws FileNotFoundException,
            IOException {
        Customer customer = new Customer("gacl",25);
        // ObjectOutputStream 对象输出流
        ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(
                new File("E:/Customer.txt")));
        oo.writeObject(customer);
        System.out.println("Customer对象序列化成功!");
        oo.close();
    }

    /**
     * MethodName: DeserializeCustomer
     * Description: 反序列Customer对象
     * @author xudp
     * @return
     * @throws Exception
     * @throws IOException
     */
    private static Customer DeserializeCustomer() throws Exception, IOException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
                new File("E:/Customer.txt")));
        Customer customer = (Customer) ois.readObject();
        System.out.println("Customer对象反序列化成功!");
        return customer;
    }
}

/**
 * <p>ClassName: Customer</p><p>
 * </p><p>Description: Customer实现了Serializable接口,可以被序列化</p><p>
 * @author xudp
 * @version 1.0 V
 * @createTime 2014-6-9 下午04:20:17
 */
class Customer implements Serializable {
    //Customer类中没有定义serialVersionUID
    private String name;
    private int age;

    public Customer(String name, int age) {
        this.name = name;
        this.age = age;
    }

    /*
     * @MethodName toString
     * @Description 重写Object类的toString()方法
     * @author xudp
     * @return string
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return "name=" + name + ", age=" + age;
    }
}</p>

Running results:

Talk about serialization and deserialization in Java objectsTalk about serialization and deserialization in Java objects

序列化和反序列化都成功了。

下面我们修改一下Customer类,添加多一个sex属性,如下:

class Customer implements Serializable {
    //Customer类中没有定义serialVersionUID
    private String name;
    private int age;

    //新添加的sex属性
    private String sex;

    public Customer(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Customer(String name, int age,String sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;
    }

    /*
     * @MethodName toString
     * @Description 重写Object类的toString()方法
     * @author xudp
     * @return string
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return "name=" + name + ", age=" + age;
    }
}

然后执行反序列操作,此时就会抛出如下的异常信息:

1 Exception in thread "main" java.io.InvalidClassException: Customer; 
2 local class incompatible: 
3 stream classdesc serialVersionUID = -88175599799432325, 
4 local class serialVersionUID = -5182532647273106745

意思就是说,文件流中的class和classpath中的class,也就是修改过后的class,不兼容了,处于安全机制考虑,程序抛出了错误,并且拒绝载入。那么如果我们真的有需求要在序列化后添加一个字段或者方法呢?应该怎么办?那就是自己去指定serialVersionUID。在TestSerialversionUID例子中,没有指定Customer类的serialVersionUID的,那么java编译器会自动给这个class进行一个摘要算法,类似于指纹算法,只要这个文件 多一个空格,得到的UID就会截然不同的,可以保证在这么多类中,这个编号是唯一的。所以,添加了一个字段后,由于没有显指定 serialVersionUID,编译器又为我们生成了一个UID,当然和前面保存在文件中的那个不会一样了,于是就出现了2个序列化版本号不一致的错误。因此,只要我们自己指定了serialVersionUID,就可以在序列化后,去添加一个字段,或者方法,而不会影响到后期的还原,还原后的对象照样可以使用,而且还多了方法或者属性可以用。

下面继续修改Customer类,给Customer指定一个serialVersionUID,修改后的代码如下:

class Customer implements Serializable {
    /**
     * Customer类中定义的serialVersionUID(序列化版本号)
     */
    private static final long serialVersionUID = -5182532647273106745L;
    private String name;
    private int age;

    //新添加的sex属性
    //private String sex;

    public Customer(String name, int age) {
        this.name = name;
        this.age = age;
    }

    /*public Customer(String name, int age,String sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;
    }*/

    /*
     * @MethodName toString
     * @Description 重写Object类的toString()方法
     * @author xudp
     * @return string
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return "name=" + name + ", age=" + age;
    }
}

重新执行序列化操作,将Customer对象序列化到本地硬盘的Customer.txt文件存储,然后修改Customer类,添加sex属性,修改后的Customer类代码如下:

class Customer implements Serializable {
    /**
     * Customer类中定义的serialVersionUID(序列化版本号)
     */
    private static final long serialVersionUID = -5182532647273106745L;
    private String name;
    private int age;

    //新添加的sex属性
    private String sex;

    public Customer(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Customer(String name, int age,String sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;
    }

    /*
     * @MethodName toString
     * @Description 重写Object类的toString()方法
     * @author xudp
     * @return string
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return "name=" + name + ", age=" + age;
    }
}

执行反序列操作,这次就可以反序列成功了,如下所示:

Talk about serialization and deserialization in Java objects

四、serialVersionUID的取值

serialVersionUID的取值是Java运行时环境根据类的内部细节自动生成的。如果对类的源代码作了修改,再重新编译,新生成的类文件的serialVersionUID的取值有可能也会发生变化。

类的serialVersionUID的默认值完全依赖于Java编译器的实现,对于同一个类,用不同的Java编译器编译,有可能会导致不同的 serialVersionUID,也有可能相同。为了提高serialVersionUID的独立性和确定性,强烈建议在一个可序列化类中显示的定义serialVersionUID,为它赋予明确的值

显式地定义serialVersionUID有两种用途:

1、 在某些场合,希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有相同的serialVersionUID;

2、 在某些场合,不希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有不同的serialVersionUID。

推荐学习:Java视频教程

The above is the detailed content of Talk about serialization and deserialization in Java objects. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:cnblogs.com. If there is any infringement, please contact admin@php.cn delete