对象的序列化和反序列化

什么叫做对象的序列化?

  一个对象产生之后实际上就在内存中开辟了一个存储空间,方便存储信息。

  对象的序列化就是将一个对象变成二进制的数据流的一种方法,通过对象的序列化可以方便的实现对象的存储和传输。如果一个类的对象需要被序列化,则该类必须实现Serializable接口,该接口的定义如下:

1 public interface Serializable {
2     
3 }

  Serializable接口中没有任何方法,只表示该类有这样具备这样一种能力,属于标识接口。下面的一个Student因为实现了Serializable接口,所以它就是可序列化的了:

 1 package io.Serializable;
 2 
 3 import java.io.Serializable;
 4 
 5 public class Student implements Serializable {
 6     private String name;
 7     private int age;
 8 
 9     public Student(String name, int age) {
10         super();
11         this.name = name;
12         this.age = age;
13     }
14 
15     public String getName() {
16         return name;
17     }
18 
19     public void setName(String name) {
20         this.name = name;
21     }
22 
23     public int getAge() {
24         return age;
25     }
26 
27     public void setAge(int age) {
28         this.age = age;
29     }
30 
31     @Override
32     public String toString() {
33         return "Student [name=" + name + ", age=" + age + "]";
34     }
35 
36 }

  以上的代码仅仅是实现了Serializable接口,其他部分并没有任何改变,以上的类产生的对象就是可序列化(二进制比特流)的了。

如果要进行对象的序列化必须依靠两个类:

ObjectInputStream和ObjectOutputStream

 serialVersionUID

  在对对象进行序列化和反序列化的时候要考虑到JDK版本的问题,如果序列化的JDK版本和反序列化的JDK版本不统一就会抛出异常。所以在对象序列化的操作中引入了一个serialVersionUID的常量。可以通过此常量验证版本的一致性。在进行反序列化的操作时,JVM会把传来的字节流中的serialVersionUID与本地响应实体(类)的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化的操作,否则就会抛出序列化版本不一致的异常。

  如果使用Eclipse进行编写,如果没有指定serialVersionUID则会出现一些警告信息,如图:

  按照Eclipse的提示,我们加上这个常量,这样这个类的设计就完成了。

对象的序列化和反序列化:

 1 package io.Serializable;
 2 
 3 import java.io.File;
 4 import java.io.FileInputStream;
 5 import java.io.FileNotFoundException;
 6 import java.io.FileOutputStream;
 7 import java.io.IOException;
 8 import java.io.ObjectInputStream;
 9 import java.io.ObjectOutputStream;
10 
11 public class SerializableDemo {
12 
13     public static void main(String[] args) {
14 
15         File file = new File("E:" + File.separator + "tmp" + File.separator
16                 + "stu.obj"); // 保存路径
17         try {
18             ObjectOutputStream oos = new ObjectOutputStream(
19                     new FileOutputStream(file));
20 
21             Student[] students = { new Student("张三", 12),
22                     new Student("李四", 15), new Student("王五", 18) };
23             oos.writeObject(students); // 直接写入一个对象数组,因为数组也是对象
24             oos.close();
25 
26             ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
27                     file));
28             Student[] stus = (Student[]) ois.readObject();
29             ois.close();
30 
31             for (Student student : stus) {
32                 System.out.println(student);
33             }
34 
35         } catch (FileNotFoundException e) {
36             e.printStackTrace();
37         } catch (IOException e) {
38             e.printStackTrace();
39         } catch (ClassNotFoundException e) {
40             e.printStackTrace();
41         }
42 
43     }
44 
45 }

  运行结果:

 Externalizable接口

  被Serializable接口声明的类的对象的内容都能够被序列化,如果现在用户希望可以自己制定序列化的内容,则可以让一个类实现Externalizable接口,该接口的定义如下:

1 public interface Externalizable extends Serializable {
2     public void readExternal(ObjectInput in)throws IOException,ClassNotFoundException;
3     public void writeExternal(ObjectOutput out)throws IOException;                  
4 }

 利用此接口修改之前的程序:

 1 package io.Serializable;
 2 
 3 import java.io.Externalizable;
 4 import java.io.IOException;
 5 import java.io.ObjectInput;
 6 import java.io.ObjectOutput;
 7 
 8 public class Student implements Externalizable {
 9     private String name;
10     private int age;
11 
12     public Student(String name, int age) {
13         super();
14         this.name = name;
15         this.age = age;
16     }
17 
18     public String getName() {
19         return name;
20     }
21 
22     public void setName(String name) {
23         this.name = name;
24     }
25 
26     public int getAge() {
27         return age;
28     }
29 
30     public void setAge(int age) {
31         this.age = age;
32     }
33 
34     @Override
35     public String toString() {
36         return "Student [name=" + name + ", age=" + age + "]";
37     }
38 
39     @Override
40     public void readExternal(ObjectInput in) throws IOException,
41             ClassNotFoundException {
42         this.name = (String) in.readObject();    //读取属性
43         this.age = in.readInt();
44     }
45 
46     @Override
47     public void writeExternal(ObjectOutput out) throws IOException {
48         out.writeObject(this.name);    //保存属性
49         out.writeInt(this.age);
50     }
51 
52 }
 1 package io.Serializable;
 2 
 3 import java.io.File;
 4 import java.io.FileInputStream;
 5 import java.io.FileNotFoundException;
 6 import java.io.FileOutputStream;
 7 import java.io.IOException;
 8 import java.io.ObjectInputStream;
 9 import java.io.ObjectOutputStream;
10 
11 public class SerializableDemo {
12 
13     public static void main(String[] args) {
14 
15         File file = new File("E:" + File.separator + "tmp" + File.separator
16                 + "stu.obj"); // 保存路径
17         try {
18             ObjectOutputStream oos = new ObjectOutputStream(
19                     new FileOutputStream(file));
20 
21             Student[] students = { new Student("张三", 12),
22                     new Student("李四", 15), new Student("王五", 18) };
23             oos.writeObject(students); // 直接写入一个对象数组,因为数组也是对象
24             oos.close();
25 
26             
27             ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
28                     file));
29             Student[] stus = (Student[]) ois.readObject();
30             ois.close();
31 
32             for (Student student : stus) {
33                 System.out.println(student);
34             }
35 
36         } catch (FileNotFoundException e) {
37             e.printStackTrace();
38         } catch (IOException e) {
39             e.printStackTrace();
40         } catch (ClassNotFoundException e) {
41             e.printStackTrace();
42         }
43 
44     }
45 
46 }

  运行结果:

  在使用Externalizable接口的时候需要原有的类中有无参构造,在Student类中加入无参构造后一切正常了!此外在Externalizable接口中也可以自定义哪些属性需要序列化,见以下代码:

 1     @Override
 2     public void readExternal(ObjectInput in) throws IOException,
 3             ClassNotFoundException {
 4         this.name = (String) in.readObject();    //读取属性
 5 //        this.age = in.readInt();
 6     }
 7 
 8     @Override
 9     public void writeExternal(ObjectOutput out) throws IOException {
10         out.writeObject(this.name);    //保存属性
11 //        out.writeInt(this.age);
12     }

  以上代码在运行的时候age属性将不会序列化!

transient关键字:

  在序列化对象的时候如果不需要某个属性被序列化可以使用transient关键字进行修饰。如此一来Externalizable接口就变得毫无用途。

1 private  transient int age;    //age属性不会被序列化

  运行结果:

  由此可见Externalizable接口的功能完全可以由Serializable接口和transient关键字的组合来取代!

原文地址:https://www.cnblogs.com/happyfans/p/4230480.html