提到Java序列化,相信大家都不陌生。我们在序列化的时候,需要将被序列化的类实现Serializable接口,这样的类在序列化时,会默认将所有的字段都序列化。那么当我们在序列化Java对象时,如果不希望对象中某些字段被序列化(如密码字段),怎么实现呢?看一个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
|
测试类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
执行结果:
1 2 3 4 |
|
另一种可以达到此目的的方法可能就比较少用了,那就是——不实现Serializable而实现Externalizable接口。这个Externalizable接口有两个方法,分别表示在序列化的时候需要序列化哪些字段和反序列化的时候能够反序列化哪些字段:
1 2 |
|
于是就有了下面的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
|
测试类除了类名使用LoginInfo2以外,其他保持不变。下面是执行结果:
1 2 3 4 5 6 7 |
|
可以看到,反序列化后的Password一项依然为null。
需要注意的是:对于恢复Serializable对象,对象完全以它存储的二进制为基础来构造,而不调用构造器。而对于一个Externalizable对象,public的无参构造器将会被调用(因此你可以看到上面的测试结果中有LoginInfoConstructor这一行),之后再调用readExternal()方法。在Externalizable接口文档中,也给出了相关描述:
When an Externalizable object is reconstructed, an instance is created using the public no-arg constructor, then the readExternal method called. Serializable objects are restored by reading them from an ObjectInputStream.
如果没有发现public的无参构造器,那么将会报错。(把LoginInfo2类的无参构造器注释掉,就会产生错误了):
1 2 3 4 5 6 7 8 9 10 |
|
那么,如果把Externalizable接口和transient关键字一起用,会是什么效果呢?我们在LoginInfo2中的password加上关键字transient,再修改writeExternal()和readExternal()方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
执行结果:
1 2 3 4 5 |
|
从结果中可以看到,尽管在password字段上使用了transient关键字,但是这还是没能阻止被序列化。因为不是以Serializable方式去序列化和反序列化的。也就是说:transient关键字只能与Serializable接口搭配使用。