java系列化 替代_transient以及Externalizable的一种替代方法(使用Serializable)

transient(瞬时)关键字

当我们队序列化进行控制时,可能某个特定子对象不想让Java的序列化机制自动保存与恢复。如果子对象表示的是我们不希望将其序列化的敏感信息(如密码),通常就会面临这种情况。即使对象中的这些信息是private属性,一经序列化处理,人们就可以通过读取文件或者拦截网络传输的方式来访问它。

在Java对象序列化(二)——Externalizable中我们通过将类实现为Externalizable可以实现类的部分序列化。本文提供了另一种可以实现部分序列化的方式,即通过关键字——transient(瞬时)。如果我们正在操作的是一个Serializable对象,那么所有序列化操作就会自动进行,为了能够给予控制,我们可以用transient关键字逐个字段的关闭序列化,它的意思“不烦恼您老保存或者恢复数据——我会自己处理的,谢谢。”

由于Externalizable对象在默认情况下不保存它的任何字段(即任何字段都不进行序列化处理),所以transient关键字只能和Serializable对象一起使用。

Externalizable的替代方法

如果不是特别坚持实现Externalizable接口,那么我们可以通过实现Serializable接口,并添加名为writeObject()和readObject()方法(注意:这里用的是添加而不是“覆盖”或者“实现”,因为这两个方法不是基类Object也不是接口Serializable中的方法)。这样一旦对象被序列化或者反序列还原,就会自动地分别调用者两个方法。也就是说,只要我们提供了这两个方法,就会使用它们而不是默认的序列化机制。

但是一定要注意,这两个方法的必须具有准确的方法特征签名,必须严格按照如下形式

private void writeObject(ObjectOutputStream stream) throws IOException{

//TODO

}

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

//TODO

}

这两个方法看似简单,但是其实包含了一些比较有意思的处理。首先,这个方式不是Serializable接口或者基类中的一部分,所以必须在类内部自己实现。其次,注意到这个方式其实是private类型。也就是说这两个方法仅能被这个类的其他成员调用,但是我们看到,其实我们没有在这个类的其他的方法中调用这两个方法。那么到底是谁调用这两个方法呢?其实,实际上我们并没有从这个类的其他方法中调用它们,而是ObjectOutputStream和ObjectInputStream对象的writeObject和readObject()方法分别调用者两个方法(通过过反射机制来访问类的私有方法)。

在调用ObjectOutputStream.writeObject()时,会检查所传递的Serializable对象,利用反射来搜索是否有writeObject()方法。如果有,就会跳过正常的序列化过程,转而调用这个它的writeObject()方法,readObject方法处理方式也一样。

这里面有一个技巧,在类的writeObject()内部,可以通过ObjectOutputStream.defaultWriteObject()来执行默认的writeObject()(非transient字段由这个方法保存),同样的,在类readObject内部,可以通过ObjectInputStream.defalutReadObject()来执行默认的readObject()方法。

下面这段代码就显示上述的技巧

package test.serializable;

import java.io.ByteArrayInputStream;

import java.io.ByteArrayOutputStream;

import java.io.IOException;

import java.io.ObjectInputStream;

import java.io.ObjectOutputStream;

import java.io.Serializable;

public class SeriCtrol implements Serializable {

private static final long serialVersionUID = -4994939941552821559L;

private String a;

private String b;

private transient String c;

public SeriCtrol(String a,String b,String c) {

this.a = "非瞬时默认实现:" + a;

this.b = "非瞬时非默认实现:"+ b;

this.c = "瞬时实现:" + c;

}

private void writeObject(ObjectOutputStream stream) throws IOException{

stream.defaultWriteObject();

stream.writeObject(b);

stream.writeObject(c);

}

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

stream.defaultReadObject();

stream.readObject();

b= "null";

c = (String)stream.readObject();

}

public String toString() {

return a + b + c;

}

public static void main(String[] args) throws IOException, ClassNotFoundException {

SeriCtrol sCtrol = new SeriCtrol("test1","test2","test3");

System.out.println("序列化之前");

System.out.println(sCtrol);

ByteArrayOutputStream out = new ByteArrayOutputStream();

ObjectOutputStream oos  = new ObjectOutputStream(out);

oos.writeObject(sCtrol);

System.out.println("反序列化操作之后");

ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());

ObjectInputStream ois = new ObjectInputStream(in);

SeriCtrol src = (SeriCtrol) ois.readObject();

System.out.println(src);

}

}

运行结果如下:

序列化之前

非瞬时默认实现:test1非瞬时非默认实现:test2瞬时实现:test3

反序列化操作之后

非瞬时默认实现:test1null瞬时实现:test3

从结果中可以看到,字段a没做什么处理,正常序列化,b在readObject()时,将其设置为null,相当于没有序列化了,而瞬时的c,我们可以手动的将其序列化。

注意,个人在操作的过程发现,writeObject()和readObject()方法不需要成对出现,但是这时候readObject()要小心,如果没有writeObject()方法,在readObject()方法就不能再调用ObjectInputStream的readObject()方法。

想想老刘在处理超链接废弃属性时处理,在readObject()方法中,将废弃的属性置为null.

在“Thinking in Java”中提到如果想序列化static静态字段,必须自己手动去实现(p585),回头有时间,写个程序验证下。

0b1331709591d260c1c78e86d0c51c18.png

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值