java如何反序列化对象_Java序列化和反序列化

1. Java序列化和反序列化(What)

Java序列化(Serialize)是指将一个Java对象写入IO流中;

Java反序列化(Deserialize)指的是从IO流中回复IO对象。

2. 序列化的意义(Why)

序列化机制可以将Java对象转换为数据流用来保存在磁盘上或者通过网络传输。这使得对象可以脱离程序独立存在。

3. 如何进行序列化(How)

为了使对象支持序列化机制,需要让它的类变成可序列化的(serializable)。通过实现两个接口之一实现:

Serializable

Externalnalizable

3.1 序列化的步骤

实现了Serializable接口的类,可以通过两个步骤序列化该对象:

创建建立在其他节点流上的ObjectOutputStream

//创建ObjectOutputStream输出流

ObjectOutputStream oos = new ObejctOutputStream(new FileOutputStream("object.txt"));

调用ObjectOutputStream对象的writeObject()方法输出可序列化对象

//将一个Person对象输出到输出流中

oos.writeObject(per);

3.2 序列化的代码

定义一个Person类,实现了Serializable接口,标识该类的对象是可序列化的。

public class Person implements java.io.Serializable {

private String name;

private int age;

// 这里没有无参构造器

public Person(String name, int age){

this.name = name;

this.age = age;

}

// name和age的setter和getter方法

...

}

将Person对象写入硬盘

import java.io.*;

public class Test{

public static void main(String[] args){

try{

// 创建ObjectOutputStream输出流

ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("Object.txt"));

Person per = new Person("Junzerg", 20);

// 将per对象写入输出流

oos.writeObject(per);

}

catch (IOException ex){

ex.printStackTrace();

}

}

}

3.3 序列化结果

AC ED 00 05 73 72 00 06 50 65 72 73 6F 6E 2A 98

15 B9 5C 2E C1 6C 02 00 02 49 00 03 61 67 65 4C

00 04 6E 61 6D 65 74 00 12 4C 6A 61 76 61 2F 6C

61 6E 67 2F 53 74 72 69 6E 67 3B 78 70 00 00 00

14 74 00 07 4A 75 6E 7A 65 72 67

共75字节。

3.4 序列化的字节数

把Person类的age属性设置为Long,重新序列化,结果为:

AC ED 00 05 73 72 00 06 50 65 72 73 6F 6E BE CF

78 98 E0 A3 0B E9 02 00 02 4A 00 03 61 67 65 4C

00 04 6E 61 6D 65 74 00 12 4C 6A 61 76 61 2F 6C

61 6E 67 2F 53 74 72 69 6E 67 3B 78 70 00 00 00

00 00 00 00 14 74 00 07 4A 75 6E 7A 65 72 67

共79字节。

4. 反序列化

4.1 反序列化的步骤

创建一个建立在其他节点流基础上的ObjectInputStream输入流

// 出啊构建一个ObjectInputStream输入流

ObjectInputStream ois = new ObjectInputStream(new FileInputStream("Object.txt"));

调用ObjectInputStream对象的readObject方法读取流中的对象,该方法返回一个Java对象,再强制转换为真实类型。

//从输入流中读取一个Java对象并将其强制类型转换成Person类

Person p = (Person) ois.readObject();

4.2 反序列化的代码

从前文创建的Object.txt中读取Person类

import java.io.*;

public class ReadObject{

public static void main(String[] args){

try{

// 创建一个ObjectInputStream输入流

ObjectInputStream ois =

new ObjectInputStream(new FileInputStream("Object.txt"));

// 从输入流中读取一个Java对象,转换为Person类

Person p = (Person)ois.readObject();

System.out.println("name: " + p.getName()

+ "\n age: " + p.getAge());

}

catch(Exception ex){

ex.printStackTrace();

}

}

}

4.3 反序列化的结果

name: Junzerg

age: 20

5 对象引用的序列化和序列化算法

5.1 对象引用的序列化

如果要序列化的类的某个成员变量是一个非String类型的引用类型,那么这个引用类型必须是可序列化的。

例如有一个Teacher类持有Person类的引用

public class Teacher implements Seializable{

private String name;

Private Person student;

public Teacher(String name, Person student){

this.name = name;

this.student = student;

}

// 省略name和student的stter和getter方法

...

}

为了在反序列化Teacher对象时正确恢复,Person类必须也是可序列化的,否则Teacher不可序列化

5.2 多个实例变量引用同一个引用对象的特殊情况

当两个Teacher对象引用同一个Person对象的时候:

Person per = new Person("Junzerg", 20);

Teacher t1 = new Teacher("Miss Li", per);

Teacher t2 = new Teacher("Mr Wu", per);

在程序依次序列化三个对象的过程中,看起来似乎会向输出流中输出三个Person对象。

这时当程序从输入流中反序列化这些对象时,就会得到三个Person对象,这样这样t1和t2引用的就不是同一个Person对象了。

5.3 Java序列化算法

为了避免5.2中出现的错误,Java的序列化算法如下:

所有保存在磁盘中的对象都有一个序列化编号

当程序试图序列化一个对象时,程序会先检查该对象是否已经被序列化过,只有改对象从未(在本次虚拟机中)被序列化过,系统才会将给对象转换成字节序列并出输出。

如果某个对象已经被序列化过,程序将直接出书一个序列化编号,而不是重新序列化该对象。

6. 自定义序列化

6.1 递归序列化

当对某个对象及进行序列化时,系统自动把该对象的所有实例变量依次进行序列化,如果某个实例变量引用另一个对象,则被引用的变量也会被序列化,这种情况被称为递归序列化。

6.2 transient关键字

在递归序列化的过程中,可能遇到不想被序列化或者不能被序列化的变量。这时可以使用transient关键字在序列化时忽略该变量,避免引发java.io.NotSerializableException异常。

6.3 transient关键字的使用

有带有transient关键字修饰的变量的Person类

public class Person implements java.io.Serializable {

private String name;

private transient int age;

// 这里没有无参构造器

public Person(String name, int age){

this.name = name;

this.age = age;

}

// name和age的setter和getter方法

...

}

注意:transient关键字只能用于修饰实例变量,不可修饰Java程序中的其他部分。

Person对象的序列化和反序列化。

public class TransientTest{

public static void main(String[] args){

try{

// 创建ObjectOutputStream输出流

ObjectOutputStream oos = new ObjectOutputStream(

new FileOutputStream("transient.txt"));

//创建ObjectInputStream输入流

ObjectInputStream ois = new ObjectInputStream(

new FileInputStream("transient.txt"));

Person per = new Person("Junzerg", 20);

// 将per对象序列化输出

oos.writeObject(per);

Person p = (Person) ois.readObject();

System.out.println(p.getAge());

}

catch (Exception ex){

ex.printStackTrace();

}

}

}

输出结果

3.1 控制台输出:

0

3.2 transient.txt内容为:

AC ED 00 05 73 72 00 06 50 65 72 73 6F 6E DB F9

DD 8C 83 99 C5 2E 02 00 01 4C 00 04 6E 61 6D 65

74 00 12 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74

72 69 6E 67 3B 78 70 74 00 07 4A 75 6E 7A 65 72

67

大小为65字节。

可以看到per实例中的age变量并没有序列化。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值