首页 > 技术文章 > Java 之 序列化与反序列化

niujifei 2020-01-31 11:08 原文

 

 

  Demo:

 1 public interface Emp extends Serializable{
 2     public void work();
 3 }
 4 
 5 public class Employee implements Emp{
 6     /* 序列化编号     */
 7     private static final long serialVersionUID = 1L;
 8     
 9     private String empNo;
10     //透明化处理,不能持久化 瞬时态属性
11     private transient String dept;
12     private String name;
13     
14     public Employee(String empNo, String dept, String name) {
15         super();
16         this.empNo = empNo;
17         this.dept = dept;
18         this.name = name;
19     }
20     
21     public Employee() {
22         super();
23     }
24  
25     public void work() {
26         System.out.println("name:" + this.name + ",empNo:" + this.empNo + ",dept:" + this.dept + " is working.");
27     }
28  
29     
30     @Override
31     public String toString() {
32         return "Employee [empNo=" + empNo + ", dept=" + dept + ", name=" + name
33                 + "]";
34     }
35 }

 

  序列化的过程:

 1 public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
 2     String file = "D:emp.dat";
 3     Emp e1 = new Employee("001", "技术部", "张三");
 4  
 5     ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(file));
 6     out.writeObject(e1);
 7     out.close();
 8     System.out.println(e1 + "序列化成功");
 9     
10     ObjectInputStream in = new ObjectInputStream(new FileInputStream(file));
11     Emp e2 = (Emp)in.readObject();;
12     in.close();
13     System.out.println(e2 + "反序列化成功");
14 }

 

  但是看控制台输出的日志信息:

   这里就有一个问题,为什么 dept 这个属性没有被序列化呢?

  由于 Employee 类中定义的 dept 属性,使用了 transient 关键字修饰,使用了 transient 修饰的属性是不能被序列化的。

  解决办法

  这个属性已经存在这个类中,删除这个属性是不可行的,SUN还是提供了一些后门给我们处理这种问题的,就是实现了Serializable接口的类。

  如果需要序列化被 transient 修饰的属性,可以通过定义:writeObject(java.io.ObjectOutputStream s)序列化对象 readObject(java.io.ObjectInputStream s)反序列化对象。这两个方法来实现序列化和反序列化transient的属性。

  上面的案例进行修改:

 1 public class Employee implements Emp{
 2     /* 序列化编号     */
 3     private static final long serialVersionUID = 3694902274397865665L;
 4     
 5     private String empNo;
 6     //透明化处理,不能持久化
 7     private transient String dept;
 8     private String name;
 9     
10     public Employee(String empNo, String dept, String name) {
11         super();
12         this.empNo = empNo;
13         this.dept = dept;
14         this.name = name;
15     }
16     
17     public Employee() {
18         super();
19     }
20  
21     public void work() {
22         System.out.println("name:" + this.name + ",empNo:" + this.empNo + ",dept:" + this.dept + " is working.");
23     }
24  
25     
26     @Override
27     public String toString() {
28         return "Employee [empNo=" + empNo + ", dept=" + dept + ", name=" + name
29                 + "]";
30     }
31  
32     // 强行序列化对象属性的方法
33     private void writeObject(java.io.ObjectOutputStream s)
34             throws java.io.IOException{
35         s.defaultWriteObject();
36         s.writeObject(this.dept);
37     }
38     //  强行反序列化对象属性的方法
39     private void readObject(java.io.ObjectInputStream s)
40             throws java.io.IOException, ClassNotFoundException {
41         s.defaultReadObject();
42         this.dept = (String)s.readObject();
43     }
44 }

   这时候就可以把 transient 修饰的属性进行序列化和反序列化了。

 

   总结:

    1. 通过实现 Serializable 接口的,默认是不会序列化 transient 修饰的属性,除非重写 writeObject readObject 两个方法。其实这两个方法就是 JVM 在执行 Serializable 接口序列化对象时执行的方法。

    2. 如果对象有多个属性都被transient修饰了,例如是empNo和dept,那么在重写writeObject和readObject这两个方法时,一定要注意这两个属性的写入顺序和读取顺序必须保持一致,否则会导致反序列化失败。但是在这里,由于都是字符串格式的,不会报错,只是出现顺序颠倒。

 

 

 

 

推荐阅读