java序列化算法透析_Java序列化——transient关键字和Externalizable接口

提到Java序列化,相信大家都不陌生。我们在序列化的时候,需要将被序列化的类实现Serializable接口,这样的类在序列化时,会默认将所有的字段都序列化。那么当我们在序列化Java对象时,如果不希望对象中某些字段被序列化(如密码字段),怎么实现呢?看一个例子:

import java.io.Serializable;

import java.util.Date;

public class LoginInfo implements Serializable {

private static final long serialVersionUID = 8364988832581114038L;

private String userName;

private transient String password;//Note this key word "transient"

private Date loginDate;

//Default Public Constructor

public LoginInfo() {

System.out.println("LoginInfo Constructor");

}

//Non-Default constructor

public LoginInfo(String username, String password) {

this.userName = username;

this.password = password;

loginDate = new Date();

}

public String toString() {

return "UserName=" + userName + ", Password="

+ password + ", LoginDate=" + loginDate;

}

}

测试类:

import java.io.FileInputStream;

import java.io.FileOutputStream;

import java.io.ObjectInputStream;

import java.io.ObjectOutputStream;

public class Test {

static String fileName = "C:/x.file";

public static void main(String[] args) throws Exception {

LoginInfo info = new LoginInfo("name", "123");

System.out.println(info);

//Write

System.out.println("Serialize object");

ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(fileName));

oos.writeObject(info);

oos.close();

//Read

System.out.println("Deserialize object.");

ObjectInputStream ois = new ObjectInputStream(new FileInputStream(fileName));

LoginInfo info2 = (LoginInfo)ois.readObject();

ois.close();

System.out.println(info2);

}

}

执行结果:

UserName=name, Password=123, LoginDate=Wed Nov 04 16:41:49 CST 2015

Serialize object

Deserialize object.

UserName=name, Password=null, LoginDate=Wed Nov 04 16:41:49 CST 2015

另一种可以达到此目的的方法可能就比较少用了,那就是——不实现Serializable而实现Externalizable接口。这个Externalizable接口有两个方法,分别表示在序列化的时候需要序列化哪些字段和反序列化的时候能够反序列化哪些字段:

void writeExternal(ObjectOutput out) throws IOException;

void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;

于是就有了下面的代码:

import java.io.Externalizable;

import java.io.IOException;

import java.io.ObjectInput;

import java.io.ObjectOutput;

import java.util.Date;

public class LoginInfo2 implements Externalizable {

private static final long serialVersionUID = 8364988832581114038L;

private String userName;

private String password;

private Date loginDate;

//Default Public Constructor

public LoginInfo2() {

System.out.println("LoginInfo Constructor");

}

//Non-Default constructor

public LoginInfo2(String username, String password) {

this.userName = username;

this.password = password;

loginDate = new Date();

}

public String toString() {

return "UserName=" + userName + ", Password="

+ password + ", LoginDate=" + loginDate;

}

@Override

public void writeExternal(ObjectOutput out) throws IOException {

System.out.println("Externalizable.writeExternal(ObjectOutput out) is called");

out.writeObject(loginDate);

out.writeUTF(userName);

}

@Override

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

System.out.println("Externalizable.readExternal(ObjectInput in) is called");

loginDate = (Date)in.readObject();

userName = (String)in.readUTF();

}

}

测试类除了类名使用LoginInfo2以外,其他保持不变。下面是执行结果:

UserName=name, Password=123, LoginDate=Wed Nov 04 16:36:39 CST 2015

Serialize object

Externalizable.writeExternal(ObjectOutput out) is called

Deserialize object.

LoginInfo Constructor //-------------------------Note this line

Externalizable.readExternal(ObjectInput in) is called

UserName=name, Password=null, LoginDate=Wed Nov 04 16:36:39 CST 2015

可以看到,反序列化后的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类的无参构造器注释掉,就会产生错误了):

UserName=name, Password=123, LoginDate=Wed Nov 04 17:03:24 CST 2015

Serialize object

Deserialize object.

Exception in thread "main" java.io.InvalidClassException: LoginInfo2; no valid constructor

at java.io.ObjectStreamClass$ExceptionInfo.newInvalidClassException(ObjectStreamClass.java:150)

at java.io.ObjectStreamClass.checkDeserialize(ObjectStreamClass.java:768)

at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1772)

at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1350)

at java.io.ObjectInputStream.readObject(ObjectInputStream.java:370)

at Test2.main(Test2.java:19)

那么,如果把Externalizable接口和transient关键字一起用,会是什么效果呢?我们在LoginInfo2中的password加上关键字transient,再修改writeExternal()和readExternal()方法:

@Override

public void writeExternal(ObjectOutput out) throws IOException {

out.writeObject(loginDate);

out.writeUTF(userName);

out.writeUTF(password);//强行将transient修饰的password属性也序列化进去

}

@Override

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

loginDate = (Date)in.readObject();

userName = (String)in.readUTF();

password = (String)in.readUTF();//反序列化password字段

}

执行结果:

UserName=name, Password=123, LoginDate=Wed Nov 04 16:58:27 CST 2015

Serialize object

Deserialize object.

LoginInfo Constructor

UserName=name, Password=123, LoginDate=Wed Nov 04 16:58:27 CST 2015

从结果中可以看到,尽管在password字段上使用了transient关键字,但是这还是没能阻止被序列化。因为不是以Serializable方式去序列化和反序列化的。也就是说:transient关键字只能与Serializable接口搭配使用。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值