对象的序列化和反序列化

1.序列化定义

  将内存中的java对象放到硬盘文件中(将对象切成一块块的、按序列顺序传输

2.反序列化

  将硬盘文件中的java对象放到内存中(将对象切成一块块的、按序列顺序传输)

 

                                                        

 3.单个对象的序列化及反序列化

序列化

首先,新建一个类,实现Serializable接口

 1 package bean;
 2 
 3 import java.io.Serializable;
 4 //参与序列化的类必须实现Serializable接口,否则会报错
 5 //Serializable只是一个标志接口,接口中什么代码也没有,起到一个标志的作用,java虚拟机看到这个类实现了这个接口,所以会对这个类给予特殊待遇
 6 //Serializable这个标志接口是给java虚拟机参考的,java虚拟机看到这个接口后,会为该类自动生成一个序列化版本号
 7 //序列化版本号的用处:
 8 public class Student implements Serializable  {
 9     
10     private int no;
11     private String name;
12     
13     public Student(int no, String name) {
14         super();
15         this.no = no;
16         this.name = name;
17     }
18     public int getNo() {
19         return no;
20     }
21     public void setNo(int no) {
22         this.no = no;
23     }
24     public String getName() {
25         return name;
26     }
27     public void setName(String name) {
28         this.name = name;
29     }
30     @Override
31     public String toString() {
32         return "Student [no=" + no + ", name=" + name + "]";
33     }
34     
35 
36 }

接下来,进行序列化操作

 1 package IoTest;
 2 
 3 import java.io.FileNotFoundException;
 4 import java.io.FileOutputStream;
 5 import java.io.IOException;
 6 import java.io.ObjectOutputStream;
 7 
 8 import bean.Student;
 9 
10 public class ObjectOutputStreamTest {
11     
12     
13     public static void main(String[] args) throws FileNotFoundException, IOException{
14         //创建java对象
15         Student s=new Student(111,"张三");
16         
17         //序列化
18         ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("students"));
19         
20         //序列化对象
21         oos.writeObject(s);
22         
23         oos.flush();
24         oos.close();
25     }
26 
27 }

运行结果:

 

 

 

反序列化

 1 package IoTest;
 2 
 3 import java.io.FileInputStream;
 4 import java.io.FileNotFoundException;
 5 import java.io.IOException;
 6 import java.io.ObjectInputStream;
 7 
 8 public class ObjectInputStreamTest {
 9     //反序列化
10     public static void main(String[] args) throws Exception{
11         
12             ObjectInputStream oii=new ObjectInputStream(new FileInputStream("students"));
13             
14             //开始反序列化,读
15             Object obj=oii.readObject();
16             //反序列化回来是一个学生对象,所以会调用学生对象的toString方法。
17             System.out.println(obj);
18             
19             oii.close();
20     }
21 
22 }

运行结果:

 

 4.多个对象序列化及反序列化

序列化

 1 package IoTest;
 2 
 3 import java.io.FileNotFoundException;
 4 import java.io.FileOutputStream;
 5 import java.io.IOException;
 6 import java.io.ObjectOutputStream;
 7 import java.util.ArrayList;
 8 import java.util.List;
 9 
10 //一次序列化多个对象:可以将对象放到序列化集合中
11 public class ObjectOutputStreamTest1 {
12     
13     public static void main(String[] args) throws Exception{
14         List<User> userList=new ArrayList<>();
15         userList.add(new User(1,"张大狗"));
16         userList.add(new User(2,"刘二狗"));
17         userList.add(new User(1,"钱小狗"));
18         ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("users"));
19         oos.writeObject(userList);
20         oos.flush();
21         oos.close();
22         System.out.println("完成");
23     }
24     
25 
26 }

运行结果:

 

 

反序列化

 1 package IoTest;
 2 
 3 import java.io.FileInputStream;
 4 import java.io.FileNotFoundException;
 5 import java.io.IOException;
 6 import java.io.ObjectInputStream;
 7 import java.util.List;
 8 
 9 //反序列化集合
10 public class ObjectInputStreamTest1 {
11     
12     public static void main(String[] args) throws Exception{
13         
14         ObjectInputStream oii=new ObjectInputStream(new FileInputStream("users"));
15         List<User>userList=(List<User>)oii.readObject();
16         for(User user:userList){
17             System.out.println(user.toString());
18         }
19         oii.close();
20         
21     }
22 
23 }

运行结果:

 

 5.若不想使某个属性参与序列化,则可以使用transient关键字

private transient String name;  //若不想使某个属性参与序列化,则可以使用transient关键字

经过序列化--->反序列化操纵后,运行结果为:

 

 

 6.

//过了很久,Student这个类源代码改动了。
    //源代码改动之后,需要重新编译,编译之后生成了全新的字节码文件
    //并且class文件再次运行的时候,java虚拟机生成的序列化版本号也会发生相应的改变
    private int st_age;

反序列化后结果:

 

 

 

 

序列化版本号不一致
Exception in thread "main" java.io.InvalidClassException: bean.Student; local class incompatible: 
stream classdesc serialVersionUID = -7370765433862913044(很久之前的序列化版本号)
local class serialVersionUID = -5935280165290247344(现在的序列化版本号)

序列化版本号的作用:
    java中是采用什么机制来区分类的?
    第一:首先通过类名进行比较,如果类名不一样,肯定不是同一个类。
    第二:如果类名一样,则是通过序列化版本号进行区分的。

    A编写了一个类:com.java.bean.Student implements Serializable
    B编写了一个类:com.java.bean.Student implements Serializable
    不用的人编写了同一个类,但是“这两个类确实不是同一个类”,这个时候就需要序列化版本号起作用了。对于java虚拟机来说,java虚拟机是可以区分开这两个类的,因为这两个类都实现了Serializable接口,都有默认的序列化版本号,它们的序列化版本号不一样,所以区分开了。这是自动生成序列化版本号的好处。

    思考:自动生成序列化版本号的缺陷?
   一旦代码确定后,不能做代码方面的修改,因为只要修改必然会重新编译,此时会生成全新的序列化版本号,此时java虚拟机会认为这是一个全新的类。
   结论:所以凡是一个类实现了Serializable接口,建议给该类提供一个固定不变的序列化版本号,这样以后这个类即使代码修改了,但是版本号不变,java虚拟机会认为是同一个类。
      建议将序列化版本号手动写出来,不要自动生成
   参考ArrayList的固定序列版本号写法:
private static final long serialVersionUID = 8683452581122892189L
 1 public class Student implements Serializable  {
 2     
 3     private static final long serialVersionUID =-7370765433862913044; //手动写序列化版本号,使两个序列化版本号保持一致
 4     private int no;
 5     private String name;
 7     //过了很久,Student这个类源代码改动了。
 8     //源代码改动之后,需要重新编译,编译之后生成了全新的字节码文件
 9     //并且class文件再次运行的时候,java虚拟机生成的序列化版本号也会发生相应的改变
10     private int st_age;

反序列化结果:

 

 

 

 

 

posted @ 2020-10-12 16:13  L1998  阅读(217)  评论(0)    收藏  举报