【Java基础知识】IO流——序列化/反序列化/serialVersionUID

IO流——序列化/反序列化/serialVersionUID

1.什么是序列化?为什么Java IO流中要引入序列化?

序列化:将一个对象编码成字节流。对象按照流一样的方式存入文本文件或者在网络中传输。
反序列化:序列化的逆过程,把文本文件中的流对象数据或者网络中的流对象数据还原成对象。
至于为何要引入序列化?
一般情况,对象是存在于运行的JVM中,JVM停止运行,这些对象就消失。现实运用当中,我们要求JVM停止运行后,能够保存指定的对象的”状态”,(持久化对象)并在重新读取的时候重新读取被保存的对象。再如,在使用远程调用或在网络中传递对象时,都会用到对象的序列化。对象一旦被序列化之后,它的编码就可以从一台正在运行的虚拟机被传递到另一台虚拟上,或者被存储到磁盘上,供以后反序列化时用。序列化技术为远程通信提供了标准的线路级对象表示法,也为JavaBeans组件结构提供了标准的持久化数据格式。

2.如何进行Java对象的序列化/反序列化?

定义一个类,实现Serializable接口,那么这个类定义的对象就可进行序列化。
如果一个类中的某些成员变量,我不想进行序列化我们可以使用transient关键字声明不需要序列化的成员变量。
定义Student类实现了Serrializable接口,并且将age前加transient关键字,使其不参与序列化。

import java.io.Serializable;
public class Student implements Serializable {
    private static final long serialVersionUID = -4982435019207183360L;

    private String name;
    private transient int age ;
    private String ID;  
    public Student() {
        super();
    }   
    public Student(String name, int age, String iD) {
        super();
        this.name = name;
        this.age = age;
        ID = iD;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getID() {
        return ID;
    }
    public void setID(String iD) {
        ID = iD;
    }
    @Override
    public String toString() {
        return "Student [name=" + name + ", age=" + age + ", ID=" + ID + "]";
    }
}
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class SerializableDemo {
    public static void main(String[] args) throws Exception {
        //1.序列化操作
        //serialize();
        //2.反序列化操作
        deserialize();
    }
    private static void serialize() throws Exception{
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("Student.txt"));
        Student s = new Student("Jack", 18, "SA001016");
        oos.writeObject(s);
        oos.close();
    }
    private static void deserialize() throws Exception{
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("Student.txt"));
        Student s = (Student) ois.readObject();
        System.out.println(s);
    }
}
/* 运行结果:
   Student [name=Jack, age=0, ID=SA001016]
 * */

3.序列化/反序列化过程中可能会遇到的问题。

3.1NotSerializableException

此种异常属于未序列化异常。未实现此接口的类将无法使其任何状态序列化或反序列化。

3.2 serialVersionUID不一致异常

Exception in thread “main” java.io.InvalidClassException: cn.Student; local class incompatible:
stream classdesc serialVersionUID = 7172969039201122131, local class serialVersionUID = 4036652964699820801
问题:序列化的对象和类中定义的序列号不一致
原因:可能是因为,类实现了序列化接口,但是没有显式的定义serialVersionUID,由虚拟机自动计算,在修改类后,(或者不同的JVM计算的方式不同,造成serialVersionUID结果不同)反序列化之前的持久化对象,进行serialVersionUID校验,前后不一致而出现的异常。

4.为什么需要定义serialVersionUID ?为什么建议手动生成而非JVM生成?

4.1为何定义serialVersionUID ?

Java的序列化机制是通过判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常,即是InvalidCastException。

4.2如何定义serialVersionUID

4.2.1如果serialVersionUID在代码中没有定义

如果serialVersionUID在代码中没有定义,则由JVM计算,具体如下:
在序列化写的时候,JVM会依据class的信息,为它计算出一个serialVersionUID,并将serialVersionUID记录在序列化字节中。
在序列化读的时候,JVM也会为class计算出一个serialVersionUID,然后会和字节码中的serialVersionUID做比较。
那么如果两个虚拟机是不同类型的虚拟机,那么计算方法可能就不一样了,于是即使相同的class,serialVersionUID也可能会不同,所以serialVersionUID尽量由我们自己来指定,而不要由虚拟机来计算。

4.2.2显示定义serialVersionUID

serialVersionUID有两种显示的生成方式:
A、是默认的1L,如:private static final long serialVersionUID = 1L;
B、是根据类名、接口名、成员方法及属性等来生成一个64位的哈希字段,比如:
private static final long serialVersionUID = -4982435019207183360L;

4.3如果serialVersionUID一致,而class发生了变化呢?

1)如果虚拟机A中的Class有一个属性,而虚拟机B中的Class,没有这个属性,那么这个属性将被忽略,而不会有异常。
2)如果虚拟机A中的Class没有的属性,而在虚拟机B中多出来的属性,那么这个属性将被赋予一个缺省值,而不会有异常。
3)如果虚拟机A中的AClass有一个属性,在虚拟机B中的AClass也有这个属性,但这个属性的类型变了,
比如说int变成了long,抑或其他的变化,将会有异常:java.io.InvalidClassException:incompatible types for field …

所以由JVM生成serialVersionUID会出现潜在隐患,在单机上可能不会出现问题,但在分布式计算、或者你提供jar供别人使用的时候,这个问题就会暴露。

你可能感兴趣的:(Java基础知识)