java序列化流_Java IO流详解(七)——对象流(序列化与反序列化)

对象流的主要用作是对Java对象的序列化和反序列化的操作。在Java IO流中提供了两个对象流:ObjectInputStream和ObjectOutputStream,这两个类都属于字节流。其中ObjectOutputStream将Java对象以字节序列的形式写出到文件,实现对象的永久存储,它继承自OutputStream。ObjectInputStream是将之前使用ObjectOutputStream序列化的字节序列恢复为Java对象,它继承自InputStream。

序列化与反序列化

序列化 : 把Java对象转换成字节序列的过程。

反序列化:把序列化成字节序列的数据恢复为Java对象的过程。

为什么需要序列化?

①、把对象的字节序列永久地保存到硬盘上:对于一个存在JVM中的对象来说,其内部的状态只是保存在内存中。当JVM退出之后,内存资源也就被释放,Java对象的内部状态也就丢失了。而在很多情况下,对象内部状态是需要被持久化的,将运行中的对象状态保存下来(最直接的方式就是保存到文件系统中),在需要的时候可以还原,即使是在Java虚拟机退出的情况下。

②、在网络上传送对象的字节序列:对象序列化机制是Java内建的一种对象持久化方式,可以很容易实现在JVM中的活动对象与字节数组(流)之间进行转换,使用得Java对象可以被存储,可以被网络传输,在网络的一端将对象序列化成字节流,经过网络传输到网络的另一端,可以从字节流重新还原为Java虚拟机中的运行状态中的对象。

1、ObjectOutputStream类

ObjectOutputStream代表对象输出流,即序列化,将Java对象以字节序列的形式写出到文件,实现对象的永久存储。

它的writeObject(Object obj)方法可对指定的obj参数对象进行序列化。

首先需要明确的一点是:一个对象要想序列化,该对象必须要实现Serializable接口,否则会抛出NotSerializableException异常。

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

package com.thr;

import java.io.Serializable;

/**

* @author Administrator

* @date 2020-02-28

* @desc Person对象

*/

public class Person implements Serializable {

private int id;

private String name;

private int age;

public Person() {

}

public Person(int id, String name, int age) {

this.id = id;

this.name = name;

this.age = age;

}

//getter、setter、toString方法省略(自己测试需要加上)

}

序列化操作:

package com.thr;

import java.io.*;

/**

* @author Administrator

* @date 2020-02-28

* @desc 使用ObjectOutputStream序列化对象

*/

public class ObjectOutputStreamTest {

public static void main(String[] args) {

//定义对象流

ObjectOutputStream oos = null;

try {

//创建对象流

oos = new ObjectOutputStream(new FileOutputStream("D:\\IO\\person.txt"));

//序列化对象

oos.writeObject(new Person(10001,"张三",20));

oos.writeObject(new Person(10002,"李四",21));

//刷新缓冲区

oos.flush();

System.out.println("序列化成功...");

} catch (IOException e) {

e.printStackTrace();

}finally {

//释放资源

if (oos!=null){

try {

oos.close();

} catch (IOException e) {

e.printStackTrace();

}

}

}

}

}

序列化之后打开的文件我们是看不懂的,因为它是字节序列文件,只有计算机懂。所以接下来需要将它反序列化成我们能看懂的。

2、ObjectInputStream类

ObjectOutputStream代表对象输入流,即反序列化,将之前使用ObjectOutputStream序列化的字节序列恢复为Java对象。

它的readObject()方法读取指定目录下的序列化对象。

反序列化操作:

package com.thr;

import java.io.*;

/**

* @author Administrator

* @date 2020-02-28

* @desc 使用ObjectInputStream反序列化对象

*/

public class ObjectInputStreamTest {

public static void main(String[] args) {

//定义对象流

ObjectInputStream ois = null;

try {

//创建对象输入流对象

ois = new ObjectInputStream(new FileInputStream("D:\\IO\\person.txt"));

//反序列化对象

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

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

System.out.println(person1);

System.out.println(person2);

System.out.println("反序列化成功...");

} catch (IOException e) {

e.printStackTrace();

} catch (ClassNotFoundException e) {

e.printStackTrace();

} finally {

if (ois!=null){

try {

ois.close();

} catch (IOException e) {

e.printStackTrace();

}

}

}

}

}

在使用ObjectInputStream反序列化时需要注意一点:

①、在完成序列化操作后,如果对序列化对象进行了修改,比如增加某个字段,那么我们再进行反序列化就会抛出InvalidClassException异常,这种情况叫不兼容问题。

解决的方法是:在对象中手动添加一个 serialVersionUID 字段,用来声明一个序列化版本号,之后再怎么添加属性也能进行反序列化,凡是实现Serializable接口的类都应该有一个表示序列化版本标识符的静态变量。

public class Person implements Serializable {

//序列化版本号

private static final long serialVersionUID = 5687485987455L;

private int id;

private String name;

private int age;

//getter、setter、toString、构造方法省略(自己测试需要加上)

}

注意:ObjectInputStream和ObjectOutputStream不能序列化transient修饰的成员变量。

Transient 关键字

transient修饰符仅适用于变量,不适用于方法和类。在序列化时,如果我们不想序列化特定变量以满足安全约束,那么我们应该将该变量声明为transient。执行序列化时,JVM会忽略transient变量的原始值并将默认值保存到文件中。因此,transient意味着不要序列化。

假如Person类中的age属性不需要序列化,在age属性上添加transient关键字。private transient int age;

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

package com.thr;

import java.io.*;

/**

* @author Administrator

* @date 2020-02-29

* @desc transient的使用

*/

public class Test {

public static void main(String[] args) {

serialization(new Person(10001, "赵六", 20));

deserialization();

}

//序列化

public static void serialization(Person person){

ObjectOutputStream oos = null;

try {

//创建输出对象流

oos = new ObjectOutputStream(new FileOutputStream("D:\\IO\\object.txt"));

//序列化对象

oos.writeObject(person);

oos.flush();

System.out.println("序列化成功...");

} catch (IOException e) {

e.printStackTrace();

} finally {

//释放资源

try {

if (oos!=null){

oos.close();

}

} catch (IOException e) {

e.printStackTrace();

}

}

}

//反序列化

public static void deserialization (){

ObjectInputStream ois = null;

try {

//创建输入对象流

ois = new ObjectInputStream(new FileInputStream("D:\\IO\\object.txt"));

//反序列化对象

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

System.out.println(person);

System.out.println("反序列化成功...");

} catch (IOException e) {

e.printStackTrace();

} catch (ClassNotFoundException e) {

e.printStackTrace();

} finally {

try {

ois.close();

} catch (IOException e) {

e.printStackTrace();

}

}

}

}

transient的使用示例

transient的使用示例

运行的结果为:Person{id=10001, name='赵六', age=0},可以发现,尽管age属性没有序列化,但是它是有默认值的。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值