序列化和反序列化

一、序列化和反序列化的定义

1.序列化:把java对象转换为二进制字节序列的过程
反序列化:把二进制字节恢复为对象的过程。

2.序列化的主要用途

  • 把一个java对象通过序列化后永久的保存到硬盘上,例如通过文件保存在硬盘上
  • 在网络上通过流传送对象的字节序列

二.序列化和反序列化使用

在java中,只有实现了Serializable接口和Externalizable接口的类的对象才能被序列化,Externalizable接口继承自 Serializable接口,实现Externalizable接口的类完全由自身来控制序列化的行为,而仅实现Serializable接口的类可以 采用默认的序列化方式 对象序列化的过程如下:

 1 /**      
 2 *    序列化和反序列化
 3 */
 4 class Test implements Serializable{
 5 
 6     /**
 7      * 序列化ID
 8      */
 9     private static final long serialVersionUID = -2281553568822627366L;
10     private String name;
11     private int num;
12     public static int num2 = 10; 
13     public String getName() {
14         return name;
15     }
16     public void setName(String name) {
17         this.name = name;
18     }
19     public int getNum() {
20         return num;
21     }
22     public void setNum(int num) {
23         this.num = num;
24     }
25 }
26 public class Demo{
27     
28     public static void main(String[] args) {
29         //将对象序列化成字节并存放到文件中
30         Test test = new Test();
31         test.setName("aa");
32         test.setNum(1);
33         System.out.println("序列化前:"+test.num2);
34         ObjectOutputStream oos=null;
35         try {
36             oos = new ObjectOutputStream(new FileOutputStream("E:\test\result.txt"));
37             oos.writeObject(test);
38             oos.close();
39             //序列化后改变静态变量的值
40             Test.num2 = 1000;
41             ObjectInputStream ois = new ObjectInputStream(new FileInputStream("E:\test\result.txt"));
42             Test test2 = (Test) ois.readObject();
43             ois.close();
44             
45             System.out.println("反序列回来的对象的信息:");
46             System.out.println("name:"+test2.getName());
47             System.out.println("num:"+test2.getNum());
48             System.out.println("num2:"+test.num2);
49         } catch (FileNotFoundException e) {
50             e.printStackTrace();
51         } catch (IOException e) {
52             e.printStackTrace();
53         } catch (ClassNotFoundException e) {
54             e.printStackTrace();
55         }
56     }
57     
58 }

结果输出:

结果输出:
序列化前:10
反序列回来的对象的信息:
name:aa
num:1
num2:1000

结果可知,序列化保存的是对象的状态,被序列化的对象的静态变量是属于类的状态,因此序列化并不保存静态变量。

三、serialVersionUID的作用

s​e​r​i​a​l​V​e​r​s​i​o​n​U​I​D​:​ ​字​面​意​思​上​是​序​列​化​的​版​本​号​,凡是实现Serializable接口的类都有一个表示序列化版本标识符的静态变量,实现Serializable接口的类如果类中没有添加serialVersionUID,一般,在开发工具中都会给预警,那么意味着这个字段肯定是有作用的。那么具体的作用是什么呢?其实,这个序列化ID起着关键的作用,它决定着是否能够成功反序列化!简单来说,java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地实体类中的serialVersionUID进行比较,如果相同则认为是一致的,便可以进行反序列化,否则就会报序列化版本不一致的异常。如修改上述代码:

/**      
*    序列化和反序列化
*/
class Test implements Serializable{

    /**
     * 序列化ID
     */
    //private static final long serialVersionUID = -2281553568822627366L;
    private static final long serialVersionUID = -2281553568822627367L;//对对象序列化后,修改序列化版本ID
    private String name;
    private int num;
    public static int num2 = 10; 
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getNum() {
        return num;
    }
    public void setNum(int num) {
        this.num = num;
    }
}
public class Demo{
    
    public static void main(String[] args) {
        //将对象序列化成字节并存放到文件中
        /*Test test = new Test();
        test.setName("aa");
        test.setNum(1);
        ObjectOutputStream oos=null;*/
        try {
            /*oos = new ObjectOutputStream(new FileOutputStream("E:\test\result.txt"));
            oos.writeObject(test);
            oos.close();
            //序列化后改变静态变量的值
*/            Test.num2 = 1000;
            //修改序列化ID后,重新进行反序列化,此时本地Test类的序列化ID已经修改!
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("E:\test\result.txt"));
            Test test2 = (Test) ois.readObject();
            ois.close();
            
            System.out.println("反序列回来的对象的信息:");
            System.out.println("name:"+test2.getName());
            System.out.println("num:"+test2.getNum());
            System.out.println("num2:"+test2.num2);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
    
}

结果输出:

java.io.InvalidClassException: com.demo.Test; local class incompatible: stream classdesc serialVersionUID = -2281553568822627366, local class serialVersionUID = -2281553568822627367
    at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:687)
    at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1876)
    at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1745)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2033)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1567)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:427)
    at com.demo.Demo.main(Demo.java:60)

结果表明,序列化ID的作用正如前面描述的那样, 虚拟机是否允许反序列化,不仅取决于类路径和功能代码是否一致,一个非常重要的一点是两个类的序列化 ID 是否一致(就是 private static final long serialVersionUID = 1L)。

原文地址:https://www.cnblogs.com/liupiao/p/9274146.html