服务器之家

服务器之家 > 正文

Java对象的序列化与反序列化详解

时间:2020-12-15 14:40     来源/作者:珀伽

一、序列化反序列化的概念

对象转换为字节序列的过程称为对象的序列化,把字节序列恢复为对象的过程称为对象的反序列化。
对象的序列化主要有两种途径:

Ⅰ . 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中
Ⅱ.  在网络上传送对象的字节序列。

当两个进程在进行远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。发送方需要把这个Java对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为Java对象。

二、序列化API

1. 对象输出流(ObjectOutputStream)的常用方法:

?
1
2
3
4
// 创建写入指定 OutputStream 的 ObjectOutputStream。此构造方法将序列化流部分写入底层流
public ObjectOutputStream(OutputStream out) throws IOException
// 将指定的对象写入 ObjectOutputStream
public final void writeObject(Object obj) throws IOException

2. 对象输入流(ObjectInputStream)的常用方法:

?
1
2
3
4
// 创建从指定 InputStream 读取的 ObjectInputStream。
public ObjectInputStream(InputStream in) throws IOException
// 从 ObjectInputStream 读取对象。对象的类、类的签名和类及所有其超类型的非瞬态和非静态字段的值都将被读取。
public final Object readObject() throws IOException, ClassNotFoundException

范例:对象序列化与反序列化

①. 定义一个Person类,实现Serializable接口

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Person implements Serializable {
  /**
   * 序列化ID
   */
  private static final long serialVersionUID = 3817849972563375707L;
  private String name;
  private int age;
  private String sex;
   
  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 getSex() {return sex; }
  public void setSex(String sex) {this.sex = sex; }
}

 ②. 序列化和反序列化Person类对象

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public class TestObjSerializeAndDeserialize {
  public static void main(String[] args) throws FileNotFoundException,
      IOException, ClassNotFoundException {
    serializePerson();
    Person person = deserializePerson();
    System.out.println(MessageFormat.format("name={0},age={1},sex={2}",
        person.getName(), person.getAge(), person.getSex()));
  }
 
  /**
   * 反序列化Person对象
   *
   * @throws IOException
   * @throws FileNotFoundException
   * @throws ClassNotFoundException
   */
  private static Person deserializePerson() throws FileNotFoundException,
      IOException, ClassNotFoundException {
    ObjectInputStream in = new ObjectInputStream(new FileInputStream(
        new File("E:\\person.txt")));
    Person person = (Person) in.readObject();
    System.out.println("反序列化成功!");
    return person;
  }
 
  /**
   * 序列化Person对象
   *
   * @throws IOException
   * @throws FileNotFoundException
   */
  private static void serializePerson() throws FileNotFoundException,
      IOException {
    Person person = new Person();
    person.setName("pegasus");
    person.setAge(24);
    person.setSex("男");
    ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(
        new File("E:\\person.txt")));
    out.writeObject(person);
    System.out.println("对象序列化成功!");
    out.close();
  }
}

运行如图所示:

Java对象的序列化与反序列化详解

三、serialVersionUID的作用

将对象序列化与反序列化范例中的serialVersionUID从Person类中去除,从新运行程序,结果会发现对象序列化成功、反序列化也成功了。现在添加一个属性address,如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Person implements Serializable {
  private String name;
  private int age;
  private String sex;
  private String address;
   
  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 getSex() {return sex; }
  public void setSex(String sex) {this.sex = sex; }
  public String getAddress() {return address; }
  public void setAddress(String address) {this.address = address; }  
  @Override
  public String toString() {
    return "Person [name=" + name + ", age=" + age + ", sex=" + sex
        + ", address=" + address + "]";
  }
}

然后执行反序列操作:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class DeserializePerson {
  public static void main(String[] args) throws FileNotFoundException, ClassNotFoundException, IOException {
    Person person = deserializePerson();
    System.out.println(person);
  }
 
  /**
   * 反序列化Person对象
   *
   * @throws IOException
   * @throws FileNotFoundException
   * @throws ClassNotFoundException
   */
  private static Person deserializePerson() throws FileNotFoundException,
      IOException, ClassNotFoundException {
    ObjectInputStream in = new ObjectInputStream(new FileInputStream(
        new File("E:\\person.txt")));
    Person person = (Person) in.readObject();
    System.out.println("反序列化成功!");
    return person;
  }
}

 运行发现,会出现如下错误:
Exception in thread "main" java.io.InvalidClassException: com.pegasus.serializable.Person; local class incompatible: stream classdesc serialVersionUID = 2521373692768252888, local class serialVersionUID = -6354757228515182324 

意思是,文件流中的class和修改过后的class,不兼容了,处于安全机制考虑,程序抛出错误,而且拒绝载入。如果我们真的需要在序列化后添加一个字段或者方法,应该怎么办?其实也很简单,只需自己去指定serialVersionUID即可。在上面的例子中,没有给Person类指定serialVersionUID,那么java编译器会自动给这个class生成一个serialVersionUID,只要对这个文件添加一个空格,得到的UID都会不同,这个编号是唯一的。所以,添加一个字段后,由于没有显示指定serialVersionUID,编译器又为我们生成一个UID,当然和前面保存在文件中的哪一个不一样,于是出现两个版本号不一致的错误。因此,只要自己指定serialVersionUID,就可在序列化以后,去添加一个字段,或者方法,而不会影响后期的反序列化,反序列后的对象还会多了方法和属性。

下面将Person类中指定serialVersionUID,重新执行序列化操作,将Person对象序列化到本地硬盘的Person.txt文件存储,然后修改Person类,之后再次反序列化测试,将会发现程序就没有异常了。

四、transient

当使用Serializable接口实现序列化操作时,如果一个对象中的某个属性不希望被序列化的话,则可以使用transient关键字进行声明。如下面的示例:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Customer implements Serializable{
  private static final long serialVersionUID = -4020382581484304699L;
  private String name; 
  private transient String address; // 此属性不被序列化
 
  public Customer(String name, String address) {
    this.name = name;
    this.address = address;
  }
 
  @Override
  public String toString() {
    return "Customer [name=" + name + ", address=" + address + "]";
  }
}

序列化、反序列化Customer,代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class TestCustomer {
  public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
    serializeCustomer();
    deserializeCustomer();
  }
 
  private static void deserializeCustomer() throws FileNotFoundException, IOException, ClassNotFoundException {
    ObjectInputStream in = new ObjectInputStream(new FileInputStream(
        new File("E:\\customer.txt")));
    Customer customer = (Customer) in.readObject();
    System.out.println(customer);
    in.close();
  }
 
  private static void serializeCustomer() throws FileNotFoundException, IOException {
    ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(
        new File("E:\\customer.txt")));
    out.writeObject(new Customer("pegasus", "甘肃"));
    System.out.println("序列化成功!");
    out.close();
  }
}

结果如下:

Java对象的序列化与反序列化详解

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

原文链接:http://blog.csdn.net/qq_25583079/article/details/48624425

相关文章

热门资讯

2020微信伤感网名听哭了 让对方看到心疼的伤感网名大全
2020微信伤感网名听哭了 让对方看到心疼的伤感网名大全 2019-12-26
Intellij idea2020永久破解,亲测可用!!!
Intellij idea2020永久破解,亲测可用!!! 2020-07-29
歪歪漫画vip账号共享2020_yy漫画免费账号密码共享
歪歪漫画vip账号共享2020_yy漫画免费账号密码共享 2020-04-07
电视剧《琉璃》全集在线观看 琉璃美人煞1-59集免费观看地址
电视剧《琉璃》全集在线观看 琉璃美人煞1-59集免费观看地址 2020-08-12
最新idea2020注册码永久激活(激活到2100年)
最新idea2020注册码永久激活(激活到2100年) 2020-07-29
返回顶部