`

对象的序列化 Externalizable Serializable

    博客分类:
  • java
阅读更多

对象的序列化是非常有趣的,因为利用它可以实现“有限持久化”。请记住“持久化”意味着对象的“生存时间”并不取决于程序是否正在执行——它存在或“生存”于程序的每一次调用之间。通过序列化一个对象,将其写入磁盘,以后在程序重新调用时重新恢复那个对象,就能圆满实现一种“持久”效果。之所以称其为“有限”,是因为不能用某种“persistent”(持久)关键字简单地地定义一个对象,并让系统自动照看其他所有细节问题(尽管将来可能成为现实)。相反,必须在自己的程序中明确地序列化和组装对象。

 

为序列化一个对象,首先要创建某些OutputStream对象,然后将其封装到ObjectOutputStream对象内。此时,只需调用writeObject()即可完成对象的序列化,并将其发送给OutputStream。相反的过程是将一个InputStream封装到ObjectInputStream内,然后调用readObject()。

 

 

语言里增加了对象序列化的概念后,可提供对两种主要特性的支持。RMI和JavaBean。

 

 

 

 

10.9.2 序列化的控制

1.特殊的安全问题

2.某一个子对象完全不必序列化

 

此时,通过实现Externalizable接口,用它代替Serializable接口,便可控制序列化的具体过程。这个Externalizable接口扩展了Serializable,并增添了两个方法:writeExternal()和readExternal()。在序列化和重新装配的过程中,会自动调用这两个方法,以便我们执行一些特殊操作。

public class Blips {

	/**
	 * @param args
	 * @throws IOException 
	 * @throws IOException
	 * @throws ClassNotFoundException 
	 * @throws FileNotFoundException
	 * @throws ClassNotFoundException 
	 */
	public static void main(String[] args) throws IOException, ClassNotFoundException  {
			ObjectOutputStream objectOutputStream = new ObjectOutputStream(
					new FileOutputStream("c:/Blips.txt"));
			objectOutputStream.writeObject(new Blip1());
			objectOutputStream.writeObject(new Blip2());
			objectOutputStream.close();
			System.out.println("1111111111111111111");
			
			ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("c:/Blips.txt"));
			objectInputStream.readObject();
			objectInputStream.readObject();
			objectInputStream.close();
			System.out.println("222222222222222222222");
	}

}

class Blip1 implements Externalizable {

	public Blip1() {
		System.out.println("Blip1.Blip1()");
	}

	@Override
	public void readExternal(ObjectInput in) throws IOException,
			ClassNotFoundException {
		System.out.println("Blip1.readExternal()");
	}

	@Override
	public void writeExternal(ObjectOutput out) throws IOException {
		System.out.println("Blip1.writeExternal()");
	}

}

class Blip2 implements Externalizable {

	Blip2() {
		System.out.println("Blip2.Blip2()");
	}

	@Override
	public void readExternal(ObjectInput in) throws IOException,
			ClassNotFoundException {
		System.out.println("Blip2.readExternal()");
	}

	@Override
	public void writeExternal(ObjectOutput out) throws IOException {
		System.out.println("Blip2.writeExternal()");
	}

}

 

恢复b1后,会调用Blip1默认构建器。这与恢复一个Serializable(可序列化)对象不同。在后者的情况下,对象完全以它保存下来的二进制位为基础恢复,不存在构建器调用。而对一个Externalizable对象,所有普通的默认构建行为都会发生(包括在字段定义时的初始化),而且会调用readExternal()。必须注意这一事实——特别注意所有默认的构建行为都会进行——否则很难在自己的Externalizable对象中产生正确的行为。

 

 

以下代码演示了保持和恢复一个Externalizable的过程:

要有默认的public构造函数,实现特定接口。

但是Serilizable可以不用默认构造函数。

 

若从一个Externalizable对象继承,通常需要调用writeExternal()和readExternal()的基础类版本,以便正确地保存和恢复基础类组件。
所以为了让一切正常运作起来,千万不可仅在writeExternal()方法执行期间写入对象的重要数据(没有默认的行为可用来为一个Externalizable对象写入所有成员对象)的,而是必须在readExternal()方法中也恢复那些数据。初次操作时可能会有些不习惯,因为Externalizable对象的默认构建行为使其看起来似乎正在进行某种存储与恢复操作。但实情并非如此。

 

 

class Blip3 implements Externalizable {
	int i;
	String s; // No initialization

	public Blip3() {
		System.out.println("Blip3 Constructor");
		// s, i not initialized
	}

	public Blip3(String x, int a) {
		System.out.println("Blip3(String x, int a)");
		s = x;
		i = a;
		// s & i initialized only in non-default
		// constructor.
	}

	public String toString() {
		return s + i;
	}

	public void writeExternal(ObjectOutput out) throws IOException {
		System.out.println("Blip3.writeExternal");
		// You must do this:
		out.writeObject(s);
		out.writeInt(i);
	}

	public void readExternal(ObjectInput in) throws IOException,
			ClassNotFoundException {
		System.out.println("Blip3.readExternal");
		// You must do this:
		s = (String) in.readObject();
		i = in.readInt();
	}

	public static void main(String[] args) {
		System.out.println("Constructing objects:");
		Blip3 b3 = new Blip3("A String ", 47);
		System.out.println(b3.toString());
		try {
			ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream(
					"Blip3.out"));
			System.out.println("Saving object:");
			o.writeObject(b3);
			o.close();
			// Now get it back:
			ObjectInputStream in = new ObjectInputStream(new FileInputStream(
					"Blip3.out"));
			System.out.println("Recovering b3:");
			b3 = (Blip3) in.readObject();
			System.out.println(b3.toString());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

  

 

 

控制序列化过程时,可能有一个特定的子对象不愿让Java的序列化机制自动保存与恢复。一般地,若那个子对象包含了不想序列化的敏感信息(如密码),就会面临这种情况。即使那种信息在对象中具有“private”(私有)属性,但一旦经序列化处理,人们就可以通过读取一个文件,或者拦截网络传输得到它。


由于Externalizable对象默认时不保存它的任何字段,所以transient关键字只能伴随Serializable使用。

 

2. Externalizable的替代方法
我们可以实现Serializable接口,并添加(注意是“添加”,而非“覆盖”或者“实现”)名为writeObject()和readObject()的方法。一旦对象被序列化或者重新装配,就会分别调用那两个方法。也就是说,只要提供了这两个方法,就会优先使用它们,而不考虑默认的序列化机制。
这些方法必须含有下列准确的签名:

 

我们传递给它的Serializable对象似乎会被检查是否实现了自己的writeObject()。若答案是肯定的是,便会跳过常规的序列化过程,并调用writeObject()。readObject()也会遇到同样的情况。
还存在另一个问题。在我们的writeObject()内部,可以调用defaultWriteObject(),从而决定采取默认的writeObject()行动。类似地,在readObject()内部,可以调用defaultReadObject()。

 

private void 
  writeObject(ObjectOutputStream stream)
    throws IOException;

private void 
  readObject(ObjectInputStream stream)
    throws IOException, ClassNotFoundException
public class SerialCtl implements Serializable {
	String a;
	transient String b;

	public SerialCtl(String aa, String bb) {
		a = "Not Transient: " + aa;
		b = "Transient: " + bb;
	}

	public String toString() {
		return a + "\n" + b;
	}

	private void writeObject(ObjectOutputStream stream) throws IOException {
		stream.defaultWriteObject();
		stream.writeObject(b);
	}

	private void readObject(ObjectInputStream stream) throws IOException,
			ClassNotFoundException {
		stream.defaultReadObject();
		b = (String) stream.readObject();
	}

	public static void main(String[] args) {
		SerialCtl sc = new SerialCtl("Test1", "Test2");
		System.out.println("Before:\n" + sc);
		ByteArrayOutputStream buf = new ByteArrayOutputStream();
		try {
			ObjectOutputStream o = new ObjectOutputStream(buf);
			o.writeObject(sc);
			// Now get it back:
			ObjectInputStream in = new ObjectInputStream(
					new ByteArrayInputStream(buf.toByteArray()));
			SerialCtl sc2 = (SerialCtl) in.readObject();
			System.out.println("After:\n" + sc2);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

 

 

 

3. 版本问题
有时候可能想改变一个可序列化的类的版本(比如原始类的对象可能保存在数据库中)。尽管这种做法得到了支持,但一般只应在非常特殊的情况下才用它。此外,它要求操作者对背后的原理有一个比较深的认识,而我们在这里还不想达到这种深度。JDK 1.1的HTML文档对这一主题进行了非常全面的论述(可从Sun公司下载,但可能也成了Java开发包联机文档的一部分)。

 

 

10.9.3 利用“持久性”

如果两个对象都有指向第三个对象的句柄,该如何对这两个对象序列化呢?如果从两个对象序列化后的状态恢复它们,第三个对象的句柄只会出现在一个对象身上吗?如果将这两个对象序列化成独立的文件,然后在代码的不同部分重新装配它们,又会得到什么结果呢?

 

同一个流中相同的对象在持久化时表现为一个对象,read时占据同一个内存位置。

不同流中相同对象在持久化时没有办法知道对方,read时占据不同的内存位置。

 

若想保存系统状态,最安全的做法是当作一种“微观”操作序列化。如果序列化了某些东西,再去做其他一些工作,再来序列化更多的东西,以此类推,那么最终将无法安全地保存系统状态。相反,应将构成系统状态的所有对象都置入单个集合内,并在一次操作里完成那个集合的写入。这样一来,同样只需一次方法调用,即可成功恢复之。

 

只需简单地序列化Class对象,就能实现static字段的保存。

尽管类Class是“可以序列化的”,但却不能按我们希望的工作。所以假如想序列化static值,必须亲自动手。

使用objectOutputStream.writeObject(Circle.class);直接持久化类是不能持久化static变量的。必须使用以下代码

Line.serializeStaticState(objectOutputStream)。

class Line extends Shape2 implements Serializable {
	private static int color = RED;

	public static void serializeStaticState(ObjectOutputStream os)
			throws IOException {
		os.writeInt(color);
	}

	public static void deserializeStaticState(ObjectInputStream os)
			throws IOException {
		color = os.readInt();
	}

}

 


 

分享到:
评论

相关推荐

    JAVA序列化Serializable及Externalizable区别详解

    主要介绍了JAVA序列化Serializable及Externalizable区别详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

    Java 串行化(序列化)Serializable/Externalizable

    NULL 博文链接:https://zhycaf.iteye.com/blog/982092

    深入探索Java对象的序列化

    Serializable有一个子接口Externalizable,实现Externalizable接口的类可以自行控制对象序列化荷反序列化过程。 一般来说,没有必要自己实现序列化接口,直接交给Java虚拟机是上策。 实现了序列化接口的类,如果...

    Java 对象序列化详解以及实例实现和源码下载

    Java中的序列化机制有两种实现方式: 一种是实现Serializable接口 另一种是实现Externalizable接口 区别: 实现Serializable接口 1 系统自动储存必要的信息 2 Java内建支持,易于实现,只需实现该接口即可,无须任何...

    Java中的序列化与反序列化.pdf

    Serializable接口和Externalizable接口实现序列化和反序列化

    Java高级程序设计实战教程第五章-Java序列化机制.pptx

    其中包括以下接口和类: java.io.Serializable java.io.Externalizable ObjectOutput ObjectInput ObjectOutputStream ObjectInputStream Java高级程序设计实战教程第五章-Java序列化机制全文共15页,当前为第7页。...

    java序列化和反序列化,面试必备

    最近阅读Serializable接口和Externalizable接口的源码,并结合了一些资料,对面试过程中与序列化相关的内容做了一些总结。 一、序列化、反序列化、使用场景、意义。 序列化:将对象写入IO流中; 反序列化:从IO流中...

    xml的序列化与验证

    1、xml序列化的Java3种实现方法 1)Serializable和Externalizable接口Xstream框架2)Simple框架 3)Apache的AXIOM框架 2、XML验证文档的生成工具 trang.jar 3、利用XSD文件的XML3种验证方法 1)Dom4j的SAXValidator ...

    groovy-io:与JSON格式之间完美的Groovy序列化。 此外,还支持JSON的精美打印(与jsonEditorOnline样式匹配)

    groovy-io消除了使用ObjectInputStream / ObjectOutputStream序列化对象的需要,而使用了JSON格式。 有第三个可选类( JsonObject ),请参见下面的“非类型化用法”。 groovy-io不需要类实现Serializable或...

    JDK_API_1_6

    Externalizable Externalizable 实例类的唯一特性是可以被写入序列化流中,该类负责保存和恢复实例内容。 FileFilter 用于抽象路径名的过滤器。 FilenameFilter 实现此接口的类实例可用于过滤器文件名。 ...

Global site tag (gtag.js) - Google Analytics