Home  >  Article  >  Java  >  13.Java Basics - Serialization

13.Java Basics - Serialization

黄舟
黄舟Original
2017-02-27 10:53:471076browse


Basic concepts


##Before introducing serialization, you must first know the following concepts:

  • Non-persistence: For objects that exist in

    JVM (Java virtual machine), their internal state can only be maintained in memory. Once When the JVM stops working, the internal state disappears, so it is non-persistent.

  • Persistence: If you want to save the object permanently (i.e.

    Persistence), the usual approach is to save it to a file or database.

  • Serialization: If you want to achieve the persistence of objects in Java, you need to

    serialize. Through serialization, you can easily serialize it in the JVM. The active object is converted into a byte array (stream) for storage.

  • Deserialization: Convert a byte array (stream) in a file or database into a JVM active object.

In Java, classes can be serialized by implementing the Serializable and Externalizable interfaces.


Serializable

Once a class implements the Serializable interface, it means that it can be serialized.

Specific serialization/deserialization operations need to be implemented through object streams (ObjectOutputStream/ObjectInputStream).

Let’s look at a specific example:

class Person implements Serializable {    
private String name;    
private int age;    
public Person(String name ,int age){        
this.name = name;        
this.age =age;
    }    
    public String getName() {        
    return name;
    }    public int getAge() {        
    return age;
    }    
    @Override
    public String toString() {        
    return "name is " + name + " , age is " + age;
    }
}public class Test {

    private final static String TEMPFILE = "E:" + File.separator + "test.txt";    
    public static void main(String[] args) {
        Person person = new Person("test",100);

        write(person);        // 关键 -> 序列化之后重新对对象进行赋值
        person = new Person("hello",999);

        read(person);        // 输出结果:
        // name is test , age is 100,序列化成功
        // 反序列化成功,name is test , age is 100
    }    // 通过 ObjectOutputStream 进行对象的序列化操作
    private static void write(Person person) {        try {
            ObjectOutputStream oos = 
                new ObjectOutputStream(new FileOutputStream(TEMPFILE));
            oos.writeObject(person);
            oos.close();
            System.out.println(person+",序列化成功");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }    // 通过 ObjectInputStream 进行对象的反序列化操作
    private static void read(Person person) {        try {
            ObjectInputStream ois = 
                new ObjectInputStream(new FileInputStream(TEMPFILE));
            person = (Person) ois.readObject();
            ois.close();
            System.out.println("反序列化成功.,"+person);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Observe the output results. We reassign the value after the object is serialized, and the result of deserialization is still the same as the value before the object was serialized. Consistent, this also indirectly proves that the object is permanently stored and persistence is achieved.


Serialization failed

Serialization failed, there are two situations: do not want to be serialized, cannot be serialized.

  • In a class, member variables modified by the keywords transient and static indicate that they do not want to be serialized. It causes partial serialization failure.

  • In a class, if there is a member variable of type Thead, the class cannot be serialized, and its impact is overall.

Let’s look at these situations:

1.transient

Whenever a variable is modified by it, it means that The parameters are transient and do not want to be serialized (

is not unserializable).

// 序列化过程、调用过程与上述例子一致,省略代码...
// 这里只对内部类 Person 的 age 属性进行修改class Person implements Serializable {    
// 用 transient 修该变量
    private transient int age;    
    // 省略部分代码...}
    // 调用后的输出结果:
    // name is test , age is 100,序列化成功
    // 反序列化成功.,name is test , age is 0

Observe the output results and find that the age before serialization is 100, while the age read through deserialization is 0.

The initial value of the parameter of type int is 0, which means that the parameter has not been serialized.


2.static

As long as the variables modified by it represent global variables, you can access global variables without relying on class objects. The serialization operation is to save the object. It is precisely because of the contradiction between this feature and serialization that global variables are not serialized by default (

It is not that they cannot be serialized).

// 序列化过程、调用过程与上述例子一致,省略代码...
// 这里只对内部类 Person 的 name 属性进行修改class Person implements Serializable {    
// 用 static 修该变量
    private static String name;    
    // 省略部分代码...}
    // 输出结果:
    // name is test , age is 100,序列化成功//反序列化成功.,name is hello , age is 100

Observe the output results and find that the value of name is hello, not test before serialization.

The different values ​​before and after indicate that it has not been serialized. And because the characteristics of static are related to classes, the variable was reassigned after the serialization operation, resulting in its value not being null, but hello.


3.Thread

If the member variable of the object contains the Thread type, it

cannot be serialized.

// 序列化过程、调用过程与上述例子一致,省略代码...
// 这里只对内部类 Person 的 name 属性进行修改class Person implements Serializable {    
//新增成员变量
    private Thread myThread = new Thread();    
    //省略部分代码...}
    // 输出结果(抛出异常):
    // Caused by: java.io.NotSerializableException: java.lang.Thread


Custom serialization

The above introduces the situation of serialization failure. What if serialization must be implemented in some cases (for example, it must be Implement serialization of global variables). Then you need to customize the serialization/deserialization process in the class. Take a look at the following example:

// 序列化操作代码与上面一致...
// 这里只对内部类 Person 的属性进行修改。class Person implements Serializable {    
private static String name;    
private transient int age;    
// 自定义序列化操作
    private void writeObject(ObjectOutputStream out) throws IOException{
       out.defaultWriteObject(); 
       out.writeObject(name);
       out.writeInt(age);

    }    // 自定义反序列化操作
    private void readObject(ObjectInputStream in) throws IOException,ClassNotFoundException{ 
        in.defaultReadObject();
        name = (String) in.readObject();
        age = in.readInt();
    }    
    // 省略部分代码...}
    // 输出结果:
    // name is test , age is 100,序列化成功
    // 反序列化成功,name is test , age is 100


Externalizable

The class implements the Externalizable interface, It also means that it can be serialized, but it has two limitations:

  • The interface enforces the

    writeExternal and readExternal methods for custom serialization and deserialization ization process.

  • Requires that the class must contain a

    parameterless constructor.

At the same time, because its serialization process is defined in a public method, it is not safe.

Look at the following example:

// 序列化操作代码与上面一致,这里只对内部类 Person 的进行修改。
class Person implements Externalizable {    private static  String name;    private transient int age;    // 重点 ->必须有无参构造函数
    public Person(){

    }    
    public Person(String name ,int age){        
    this.name = name;        
    this.age =age;
    }    
    public String getName() {        
    return name;
    }    
    public int getAge() {        
    return age;
    }    
    @Override
    public String toString() {        
    return "name is " + name + " , age is " + age;
    }    
    // 实现接口的方法
    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeObject(name);
        out.writeInt(age);  

    }    
    // 实现接口的方法
    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        name = (String) in.readObject();
        age = in.readInt();
    }
}
// 输出结果:
// name is test , age is 100,序列化成功
// 反序列化成功,name is test , age is 100


serialVersionUID

1. Concept

serialVersionUID, which is the serialized version number.

Its role is to maintain version compatibility during serialization, that is, deserialization still maintains the uniqueness of the object when the version is upgraded.

There are two ways to generate serialVersionUID:

  • One is a fixed value: 1L

  • The other is through JVM computing, different JVMs may adopt different computing algorithms.

For the method generated by JVM, it has the following characteristics:

  • If the code of the object remains unchanged, the serialVersionUID generated multiple times It is also unchanged.

  • When the

    method is modified, the serialVersionUID ** remains unchanged **.

  • When the object's

    properties are modified, the regenerated serialVersionUID** will change**.

因此说明序列化是作用于对象属性上的


2.实例

下面通过实例来探究下 serialVersionUID 的具体作用:

  • 首先我们对对象进行序列化操作(这里取消了反序列化的操作)

// 序列化操作代码与上面例子一致...// 这里取消了反序列化的操作public static void main(String[] args) {
    Person person = new Person("test",100);
    write(person);
    person = new Person("java", 200);    //read(person);}
  • 然后新增一个对象的属性,再进行反序列化操作

// 省略部分代码,与上面的代码一致...class Person implements Serializable {    private String name;    private age;    //新增成员变量
    private int phone;    //省略部分代码...

    }
}public static void main(String[] args) {
    Person person = new Person("test",100);    //write(person);
    person = new Person("java", 200);
    read(person);
}// 输出结果(抛出异常):// java.io.InvalidClassException: Person;

观察代码,在序列化对象并没有添加 serialVersionUID 的情况,在对象序列化之后如果改变了对象的属性,反序列化就会抛出异常。如果对象添加了 serialVersionUID 就不会出现这种情况,这里就不验证了。

 以上就是13.Java 基础 - 序列化的内容,更多相关内容请关注PHP中文网(www.php.cn)!


Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn