Java序列化机制拾遗

对象序列化的概念加入到语言中是为了支持两种主要特性:一是Java的远程方法调用(RMI),使存活于其他计算机上的对象使用起来就像是存活在本机上一样。当向远程对象发送消息时,需要通过对象序列化来传输参数和返回值。二是,对于JavaBean,对象的序列化也是必需的。使用一个Bean时,一般情况下是在设计阶段对他的状态信息进行配置,这种状态信息必须保存下来,并在程序启动时进行后期恢复;这种具体工作就是由对象序列化完成的。

对象序列化机制允许把内存中的Java对象转换为平台无关的二进制流,其他程序一旦获得了这种二进制流就可以将二进制

流恢复成原来的Java对象。序列化机制使得对象可以脱离程序的运行而独立存在。对象的序列化是指将Java对象写入IO流中,与此对应的,对象的反序列化则指从IO流中恢复该Java对象。

Java序列化可以实现对象的深复制,但是对象对应的类的各个成员必须是可序列化的,否则会抛出异常。

只有实现了Serializable接口或者Externalizable接口的类才是可序列化的。

1、对象的序列化以及反序列化

对象的序列化的步骤:

(1)、创建一个ObjectOutputStream, 这个输出流是一个处理流,所以必须建立在其他节点流的基础上。

ObjectOutputStream oos=newObjectOutputStream(new FileOutputStream(“object.txt”));

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

Person p=new Person();
oos.writeObject(p);

反序列化的步骤:

(1)、创建一个ObjectInputStream输入流,这个输入流也是一个处理流,所以也要建立在其他节点流的基础之上

ObjectInputStream ois=newObjectInputStream(new FileInputStream(“object.txt”));

(2)、调用ObjectInputStream对象的readObject()方法读取输入流中的对象,该方法返回一个Object类型的Java对象,如果程序知道该Java对象的类型,则可以将该对象强制类型转换成其真实的类型。

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

注意:1、反序列化读取的仅仅是Java对象的数据,而不是Java类,因此采用反序列化恢复Java对象时,必须提供该Java对象所属类的class文件,否则将会引发ClassNotFoundException异常。

2、对于实现Serializable接口的序列化类,在反序列化的时候不会调用其构造器;但是对于实现Externalizable接口的序列化类来说,反序列化的时候则会在调用readExternal(ObjectInput in)方法之前,会调用该类的无参构造器,因此,对于实现Externalizable接口的序列化类来说,必须要有无参构造器。

2、对象引用的序列化

如果某个类的Field类型不是基本类型或String类型,而是另一个引用类型,那么这个引用类必须是可序列化的,否则拥有该类型的Field的类也是不可序列化的。

       Java序列化机制采用的序列化算法:

(1) 所有保存到磁盘中的对象都有一个序列化编号;

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

(3) 如果某个对象已经序列化过,程序将只是直接输出一个序列化编号,而不是再次重新序列化该对象。

这种算法会引起一个问题:当程序使用writeObject()方法后改变了该对象的Field值,再次调用writeObject()方法,改变的Field值也不会被输出的。

3、自定义序列化

为了某些特殊的需求,比如序列化的时候防止某些敏感字段一同序列化,可以采取自定义序列化的方式。对于敏感字段,比较简单的方式是使用transient关键字修饰相应的Field。

使用transient关键字修饰Field虽然简单,但被transient修饰的Field将被完全隔离在序列化机制之外,导致在反序列化恢复Java对象时无法取得该Field值。Java还提供了一种自定义序列化机制,通过这种自定义序列化机制可以让程序控制如何序列化各Field,甚至完全不序列化某些Field。

在序列化和反序列化过程中需要特殊处理的类(这个类应该是实现了Serializable的类,而不是实现了Externalizable接口的类)应该提供如下特殊签名的方法,这些特殊的方法用以实现自定义序列化。

private void writeObject(java.io.ObjectOutputStream out)throws IOException
private void readObject(java.io.ObjectInputStream in)throws IOException,ClassNotFoundException
private void readObjectNoData()throws ObjectStreamException

writeObject()方法负责写入特定类的实例状态,默认情况下会调用out.defaultWriteObject来保存Java对象的各Field

readObject()方法负责从流中读取并恢复对象Field,默认会调用in.defaultReadObject来恢复Java对象的非静态和非瞬态Field。

当序列化流不完整时,readObjectNoData方法可以用来正确地初始化反序列化的对象

4、更彻底的序列化机制

还有一种更彻底的自定义机制,如果需要实现序列化某个对象时替换该对象,则应为序列化类提供如下特殊方法:

ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException;

系统在序列化某个对象之前,会先调用该对象的writeReplace和writeObject两个方法,系统总是先调用被序列化对象的writeReplace方法,如果该方法返回另一个对象,系统将再次调用另一个对象的writeReplace方法,直到不再返回另一个对象为止,程序最后将调用该对象的writeObject方法来保存该对象的状态。

序列化机制里有一个特殊的方法,它可以实现保护性复制整个对象。
ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;
这个方法会紧接着readObject()之后被调用,该方法的返回值将会代替原来反序列化的对象,而原来反序列化的对象将会被立即丢弃。在序列化单例类尤其有用。
import java.io.*;
class SingleTon implements Serializable{
	private static final SingleTon instance=new SingleTon();
	private SingleTon(){}
	public static SingleTon getInstance(){
		return instance;
	}
	private Object readResolve() throws ObjectStreamException{
		return instance;
	}
}
public class SingleTonSerializeTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		try(ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("object.txt"));
				ObjectInputStream ois=new ObjectInputStream(new FileInputStream("object.txt"))){
			SingleTon single1=SingleTon.getInstance();
			oos.writeObject(single1);
			SingleTon single=(SingleTon) ois.readObject();
			System.out.println(single1==SingleTon.getInstance());
			System.out.println(single==single1);
			System.out.println(single==SingleTon.getInstance());
		}catch(IOException | ClassNotFoundException e){
			e.printStackTrace();
		}
	}

}

5、实现了Externalizable接口的序列化实现方式

另一种自定义序列化机制,为实现Externalizable接口,这个接口里有两个方法:

(1) void readExternal(ObjectInput in):需要序列化的类实现readExternal()方法来实现反序列化。该方法调用DataInput(ObjectInput的父接口)的方法readXxx()来恢复基本类型的Field值,调用ObjectInput的readObject()方法来恢复引用类型的Field值。

(2) void writeExternal(ObjectOutput out):需要序列化的类实现writeExternal()方法来保存对象的状态。该方法调用DataInput(ObjectInput的父接口)的方法writeXxx()来保存基本类型的Field值,调用ObjectOutput的writeObject()方法来保存引用类型的Field值。

6、static和transient修饰的Field的序列化问题
static和transient修饰的Field不会被序列化,如果想要实现static Field的序列化,则需要使用如下的方法:

public static void serializeStaticState(ObjectOutputStream os) throws IOException;
public static void deserializeStaticState(ObjectInputStream os) throws IOException;


本文整理自李刚老师编著的《疯狂Java》以及参考《Thinking in Java》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java高级程序设计 第5章 Java序列化机制 5.1 应用场景 5.2 相关知识5.3 实施过程 5.4 拓展知识5.5 拓展训练 5.6 课后小结5.7 课后习题 5.8 上机实训 Java高级程序设计实战教程第五章-Java序列化机制全文共15页,当前为第1页。 5.1 应用场景 在分布式环境下,当进行远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传输。发送方需要把这个Java对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为Java对象。 序列化是一种将对象以一连串的字节描述的过程,用于解决在对对象流进行读写操作时所引发的问题。序列化可以将对象的状态写在流里进行网络传输,或者保存到文件、数据库等系统中,并在需要时把该流读取出来重新构造一个相同的对象。 Java高级程序设计实战教程第五章-Java序列化机制全文共15页,当前为第2页。 5.2 相关知识 5.2.1 序列化的概念 5.2.2 序列化应用 5.2.3 序列化的几种方式 5.2.4 对象实现机制 Java高级程序设计实战教程第五章-Java序列化机制全文共15页,当前为第3页。 5.2.1 序列化的概念 将在内存中的各种对象的状态(也就是实例变量,不是方法)保存在磁盘中或者在网络中进行传输,并且可以把保存的对象状态再读出来。 将一个Java对象写入IO流;与此对应的,则是从IO流中恢复一个Java对象。 Java提供这种保存对象状态的机制,就是序列化。 对象序列化Java编程中的必备武器 Java高级程序设计实战教程第五章-Java序列化机制全文共15页,当前为第4页。 5.2.2 序列化应用 当你想把内存中的对象状态保存到一个文件中或者数据库中时候; 当你想用套接字在网络上传送对象的时候; 当你想通过RMI传输对象的时候。 Java高级程序设计实战教程第五章-Java序列化机制全文共15页,当前为第5页。 5.2.3 序列化的几种方式 在Java中socket传输数据时,数据类型往往比较难选择。可能要考虑带宽、跨语言、版本的兼容等问题。比较常见的做法有两种: 一是把对象包装成JSON字符串传输, 二是采用Java对象的序列化和反序列化。 随着Google工具protoBuf的开源,protobuf也是个不错的选择。 *提示:对JSON,Object Serialize,ProtoBuf 做个对比。 Java高级程序设计实战教程第五章-Java序列化机制全文共15页,当前为第6页。 5.2.4 对象实现机制 为了方便开发人员将Java对象进行序列化及反序列化Java提供了一套方便的API来支持。其中包括以下接口和类: java.io.Serializable java.io.Externalizable ObjectOutput ObjectInput ObjectOutputStream ObjectInputStream Java高级程序设计实战教程第五章-Java序列化机制全文共15页,当前为第7页。 5.2 相关知识 5.2.1 序列化的概念 5.2.2 序列化应用 5.2.3 序列化的几种方式 5.2.4 对象实现机制 Java高级程序设计实战教程第五章-Java序列化机制全文共15页,当前为第8页。 5.3 实施过程 5.3.1 任务一 使用serializable序列化实体对象 5.3.2 任务二 反序列化将Person对象从磁盘上读出 Java高级程序设计实战教程第五章-Java序列化机制全文共15页,当前为第9页。 5.3.1 任务一 使用serializable序列化实体对象 实现Serializable接口非常简单,只要让Jjava实现Serializable接口即可,无需实现任何方法。 一个类一旦实现了Serializable接口,那么该类的对象就是可序列化的。实现类的对象的序列化可以使用ObjectOutputStream,实现步骤如下: 创建ObjectOutputStream对象; 调用ObjectOutputStream的writeObject方法输出对象。 Java高级程序设计实战教程第五章-Java序列化机制全文共15页,当前为第10页。 5.3.2 任务二 反序列化将Person对象从磁盘上读出 任务需求: 反序列化将Employee对象从磁盘上读出并修改员工信息,然后再写入到文件中。 分析: 相应的反序列化需要使用的类是ObjectInputStream,反序列化步骤如下: 创建ObjectInputStream对象; 使用ObjectInputStream的readObject方法取出对象。 Java高级程序设计实战教程第五章-Java序列化机制全文

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值