java对象序列化作用_Java学习之——理解对象序列化

Java的对象序列化就是把对象写入到输出流中,用来存储或传输;反序列化就是从输入流中读取对象。简单的来说是指将那些实现了Serializable接口的对象转换成一个字节序列,并能够在以后将这个字节序列完全恢复为原来的对象。

要序列化一个对象首先要创造某些OutputStream对象(如FileOutputStream、ByteArrayOutputStream等),然后将其封装在一个ObjectOutputStream对象中,在调用writeObject()方法即可序列化一个对象;而反序列化的过程需要创造InputStream对象(如FileInputstream、ByteArrayInputStream等),然后将其封装在ObjectInputStream中,在调用readObject()即可。注意 对象的序列化是基于字节的不能使用基于字符的流。

实现对象的序列化有两种方式:(1)实现Serializable接口 (2)实现Externalible接口

一、序列化

1.第一种方式——实现Serializable接口。该接口仅为标记接口,不包含任何方法定义,表示实现了该接口的类可以被序列化,且实现该接口的类的所有子类都可以被序列化。而且实现了该接口的类默认为自动序列化,即对象中的所有字段都将被可序列化。但是有时候我们需要对某些字段不进行自动序列化(因为序列化会曝光信息),这时候我们就需要transient关键字(该关键字只能和Serializable对象一起使用)。被该关键字修饰的字段在自动序列化的过程中将不会被序列化。除了默认的自动序列化,我们也可以手动控制序列化过程,手动序列化甚至可以实现将被transient关键字修饰的字段序列化。要想实现手动序列化需要在实现了Serializable接口的类中添加两个私有的方法writeObject()和readObject(),在这两个方法中控制字段的序列化。

private void writeObject(ObjectOutputStream oos)throws IOException{

oos.defaultWriteObject();

oos.writeObject(password);

}第一行代码是序列化所有非transient字段,必须是该方法的第一个操作

第二行代码是序列化transient字段

private void readObject(ObjectInputStream ois)throws IOException,ClassNotFoundException{

ois.defaultReadObject();

password = (String)ois.readObject();

}

第一行代码是反序列化所有非transient和非静态字段,必须是该方法的第一个操作

第二行代码是反序列化transient字段

这两个方法会在序列化、反序列化的过程 中被自动调用。且不能关闭流,否则会导致序列化操作失败。

2.第二种方式——实现Externalible接口。实现该接口需要定义一个默认的构造函数,否则将会抛出java.io.InvalidClassException异常。此外还需要定义两个方法writeExternal()和readExternal()来控制序列化字段。

public void writeExternal(ObjectOutput out)throws IOException{

out.writeObject(name);

out.writeObject(password);

}

public void readExternal(ObjectIntput in)throws IOException,ClassNotFoundException{

name = in.readObject();

password = in.readObject();

}如果通过序列化将一个对象持久化写入到文件中应当注意以下情况:

(1)我们知道 FileOutputStream类有一个带有两个参数的重载Constructor——FileOutputStream(String,boolean)。若第二个参数为true且String代表的文件存在,那么将把新的内容写到原来文件的末尾而非重写这个文件,这里我们不能用这个版本的构造函数,也就是说我们必须重写这个文件,否则在读取这个文件反序列化的过程中就会抛出异常,导致只有我们第一次写到这个文件中的对象可以被反序列化,之后程序就会出错。

(2)若一个类的字段有引用对象,那么在序列化该类的时候不仅该类要实现Serializable接口,这个引用类型也要实现Serializable接口。但有时我们并不需要对这个引用类型进行序列化,此时就需要使用transient关键字来修饰该引用类型保证在序列化的过程中跳过该引用类型。

(3)注意序列化操作保存的是对象的状态,静态字段是类的状态而不是对象的状态,所以不能序列化。

(4) 序列化前后对象的地址不同了,但是内容是一样的,而且对象中包含的引用也相同。换句话说,通过序列化操作,我们可以实现对任何可Serializable对象的”深度复制(deep copy)"——这意味着我们复制的是整个对象网,而不仅仅是基本对象及其引用。对于同一流的对象,他们的地址是相同,说明他们是同一个对象,但是与其他流的对象地址却不相同。也就说,只要将对象序列化到单一流中,就可以恢复出与我们写出时一样的对象网,而且只要在同一流中,对象都是同一个。

(5)如果父类没有实现Serializable接口,但其子类实现 了此接口,那么 这个子类是可以序列化的,但是在反序列化的过程 中会调用 父类 的无参构造函数,所以在其直接父类(注意是直接父类)中必须有一个无参的构造函数。

(6)序列化输出过程跟踪写入流的对象,试图将同一个对象写入流时,不会导致该对象被复制,而只是将一个句柄写入流,该句柄指向流中相同对象的第一个对象出现的位置。为了避免这种情况,方法是在writeObject()之前调用out.reset()方法,这个方法的作用是清除流中保存的写入对象的记录。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值