控制序列化就是有选择的序列化对象,而不是把对象的所以内容都序列化,控制对象序列化和反序列方法,可以在序列化过程中储存其他非this对象包含的数据 。java.io.Externalizable 。继承自了java.io.Serializable 。
java.io.Externalizable的所有实现者必须提供读取和写出的实现。
java.io.Externalizable接口(自己实现):
voidreadExternal(ObjectInput in) 反序列时被调用
void writeExternal(ObjectOutput out) 序列化时被调用
例子:
import java.io.*;
import java.util.*; //本程序通过实现Externalizable接口控制对象序列化和反序列
public class UserInfo implements Externalizable {
public String userName;
public String userPass;
public int userAge;
public UserInfo(){
}
public UserInfo(String username,String userpass,int userage){
this.userName=username;
this.userPass=userpass;
this.userAge=userage;
}
//当序列化对象时,该方法自动调用
public void writeExternal(ObjectOutput out) throws IOException{
System.out.println("现在执行序列化方法");
//可以在序列化时写非自身的变量
Date d=new Date();
out.writeObject(d);
//只序列化userName,userPass变量
out.writeObject(userName);
out.writeObject(userPass);
}
//当反序列化对象时,该方法自动调用
public void readExternal(ObjectInput in) throws IOException,ClassNotFoundException{
System.out.println("现在执行反序列化方法");
Date d=(Date)in.readObject();
System.out.println(d);
this.userName=(String)in.readObject();
this.userPass=(String)in.readObject();
}
public String toString(){
return "用户名: "+this.userName+";密码:"+this.userPass+";年龄:"+this.userAge;
}
}
import java.io.*; import java.util.*; //本程序通过实现Externalizable接口控制对象序列化和反序列
public class UserInfo implements Externalizable {
public String userName;
public String userPass;
public int userAge;
public UserInfo(){ }
public UserInfo(String username,String userpass,int userage){
this.userName=username;
this.userPass=userpass;
this.userAge=userage;
}
//当序列化对象时,该方法自动调用
public void writeExternal(ObjectOutput out) throws IOException{
System.out.println("现在执行序列化方法"); //可以在序列化时写非自身的变量
Date d=new Date();
out.writeObject(d);
//只序列化userName,userPass变量
out.writeObject(userName);
out.writeObject(userPass);
}
//当反序列化对象时,该方法自动调用
public void readExternal(ObjectInput in) throws IOException,ClassNotFoundException{
System.out.println("现在执行反序列化方法");
Date d=(Date)in.readObject();
System.out.println(d);
this.userName=(String)in.readObject();
this.userPass=(String)in.readObject();
}
public String toString(){
return "用户名: "+this.userName+";密码:"+this.userPass+ ";年龄:"+this.userAge;
}
}
改写测试类 Java代码
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.Date;
import java.lang.management.*;
public class Test {
//序列化对象到文件
public static void serialize(String fileName){
try{
//创建一个对象输出流,讲对象输出到文件
ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream(fileName));
UserInfo user=new UserInfo("renyanwei","888888",20);
out.writeObject(user); //序列化一个会员对象
out.close();
}catch (Exception x){
System.out.println(x.toString());
}
}
//从文件反序列化到对象
public static void deserialize(String fileName){
try{
//创建一个对象输入流,从文件读取对象
ObjectInputStream in=new ObjectInputStream(new FileInputStream(fileName));
//读取UserInfo对象并调用它的toString()方法
UserInfo user=(UserInfo)(in.readObject());
System.out.println(user.toString());
in.close();
}catch (Exception x){
System.out.println(x.toString());
}
}
public static void main(String[] args) {
serialize("D://test.txt");
System.out.println("序列化完毕");
deserialize("D://test.txt");
System.out.println("反序列化完毕");
}
}
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.Date;
import java.lang.management.*;
public class Test {
//序列化对象到文件
public static void serialize(String fileName){
try {
//创建一个对象输出流,讲对象输出到文件
ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream(fileName));
UserInfo user=new UserInfo("renyanwei","888888",20);
out.writeObject(user);
//序列化一个会员对象
out.close();
} catch (Exception x) {
System.out.println(x.toString());
}
}
//从文件反序列化到对象
public static void deserialize(String fileName){
try {
//创建一个对象输入流,从文件读取对象
ObjectInputStream in=new ObjectInputStream(new FileInputStream(fileName));
//读取UserInfo对象并调用它的toString()方法
UserInfo user=(UserInfo)(in.readObject());
System.out.println(user.toString()); in.close();
} catch (Exception x) {
System.out.println(x.toString());
}
}
public static void main(String[] args) {
serialize("D://test.txt");
System.out.println("序列化完毕");
deserialize("D://test.txt");
System.out.println("反序列化完毕");
}
}
执行结果
现在执行序列化方法
序列化完毕
现在执行反序列化方法
Thu Oct 23 22:18:12 CST 2008
用户名: renyanwei;密码:888888;年龄:0
反序列化完毕
总结:
首先,我们在序列化UserInfo对象的时候,由于这个类实现了Externalizable 接口,在writeExternal()方法里定义了哪些属性可以序列化,哪些不可以序列化,所以,对象在经过这里就把规定能被序列化的序列化保存文件,不能序列化的不处理,然后在反序列的时候自动调用readExternal()方法,根据序列顺序挨个读取进行反序列,并自动封装成对象返回,然后在测试类接收,就完成了反序列通过Serializable接口是java内部已经封装好了,而
序列化会自动存储必要的信息,用以反序列化被存储的实例,而外部化则只保存被存储的类的标识。当你通过java.io.Serializable接口序列化一个对象时,有关类的信息,比如它的属性和这些属性的类型,都与实例数据一起被存储起来。在选择走Externalizable这条路时,Java 只存储有关每个被存储类型的非常少的信息。
每个接口的优点和缺点
Serializable接口
? 优点:内建支持
? 优点:易于实现
? 缺点:占用空间过大
? 缺点:由于额外的开销导致速度变比较慢
Externalizable接口
? 优点:开销较少(程序员决定存储什么)
? 优点:可能的速度提升
? 缺点:虚拟机不提供任何帮助,也就是说所有的工作都落到了开发人员的肩上。
在两者之间如何选择要根据应用程序的需求来定。Serializable通常是最简单的解决方案,但是它可能会导致出现不可接受的性能问题或空间问题;在出现这些问题的情况下,Externalizable可能是一条可行之路。
要记住一点,如果一个类是可外部化的(Externalizable),那么Externalizable方法将被用于序列化类的实例,即使这个类型提供了Serializable方法:
private void writeObject()
private void readObject()
Externalizable 实例
被Serializable接口声明的类的对象的内容都将被序列化,如果现在用户希望自己指定序列化的内容,则可以让一个类实现Externalizable接口,此接口定义如下:
- public
interface Externalizable extends Serializable { -
public void writeExternal(ObjectOutput out) throws IOException ; -
public void readExternal(ObjectInput in) throws IOException, - ClassNot
FoundException ; - }
Externalizable接口是Serializable接口的子接口,在此接口中定义了两个方法,这两个方法的作用如下。
writeExternal(ObjectOutput out):在此方法中指定要保存的属性信息,对象序列化时调用。
readExternal(ObjectInput in):在此方法中读取被保存的信息,对象反序列化时调用。
这两个方法的参数类型是ObjectOutput和ObjectInput,两个接口的定义如下。
ObjectOutput接口定义:
- public
interface ObjectOutput extends DataOutput
ObjectInput接口定义:
- public
interface ObjectInput extends DataInput
可以发现以上两个接口分别继承DataOutput和DataInput,这样在这两个方法中就可以像DataOutputStream和DataInputStream那样直接输出和读取各种类型的数据。
如果一个类要使用Externalizable实现序列化时,在此类中必须存在一个无参构造方法,因为在反序列化时会默认调用无参构造实例化对象,如果没有此无参构造,则运行时将会出现异常,这一点的实现机制与Serializable接口是不同的。
范例:修改Person类并实现Externalizable接口
- package
org.lxh.demo12.serdemo; - import
java.io.Externalizable; - import
java.io.IOException; - import
java.io.ObjectInput; - import
java.io.ObjectOutput; - public
class Person implements Externalizable {// 此类的对象可以被序列化 -
private String name; // 声明name属性 -
private int age; // 声明age属性 -
public Person(){} // 必须定义无参构造 -
public Person(String name, int age) { // 通过构造方法设置属性内容 -
this.name = name; -
this.age = age; -
} -
public String toString() { // 覆写toString()方法 -
return "姓名:" + this.name + ";年龄:" + this.age; -
} -
// 覆写此方法,根据需要读取内容,反序列化时使用 -
public void readExternal(ObjectInput in) throws IOException, -
ClassNotFoundException { -
this.name = (String)in.readObject() ; // 读取姓名属性 -
this.age = in.readInt() ; // 读取年龄 -
} -
// 覆写此方法,根据需要可以保存属性或具体内容,序列化时使用 -
public void writeExternal(ObjectOutput out) throws IOException { -
out.writeObject(this.name) ; // 保存姓名属性 -
out.writeInt(this.age) ; // 保存年龄属性 -
} - }
以上程序中的Person类实现了Externalizable接口,这样用户就可以在类中有选择地保存需要的属性或者其他的具体数据。在本程序中,为了与之前的程序统一,将全部属性保存下来。
范例:序列化和反序列化Person对象
- package
org.lxh.demo12.serdemo; - import
java.io.File; - import
java.io.FileInputStream; - import
java.io.FileOutputStream; - import
java.io.InputStream; - import
java.io.ObjectInputStream; - import
java.io.ObjectOutputStream; - import
java.io.OutputStream; - public
class SerDemo03 { -
public static void main(String[] args) throws Exception { -
ser(); // 序列化 -
dser(); // 反序列化 -
} -
public static void ser() throws Exception { // 序列化操作 -
File f = new File("D:" + File.separator + "test.txt"); -
ObjectOutputStream oos = null; -
OutputStream out = new FileOutputStream(f); // 文件输出流 -
oos = new ObjectOutputStream(out); // 为对象输出流实例化 -
oos.writeObject(new Person("张三", 30)); // 保存对象到文件 -
oos.close(); // 关闭输出 -
} -
public static void dser() throws Exception { // 反序列化操作 -
File f = new File("D:" + File.separator + "test.txt"); -
ObjectInputStream ois = null; -
InputStream input = new FileInputStream(f); // 文件输出流 -
ois = new ObjectInputStream(input); // 为对象输出流实例化 -
Object obj = ois.readObject(); // 读取对象 -
ois.close(); // 关闭输出 -
System.out.println(obj); -
} - }
从以上代码中可以发现,使用Externalizable接口实现序列化明显要比使用Serializable接口实现序列化麻烦得多,除此之外,两者的实现还有不同,如表12-27所示。
表12-27
序 | 区 | Serializable | Externalizable |
1 | 实现复杂度 | 实现简单,Java对其 有内建支持 | 实现复杂, 由开发人员自己完成 |
2 | 执行效率 | 所有对象由Java统一保存, 性能较低 | 开发人员决定哪个对象保存, 可能造成速度提升 |
3 | 保存信息 | 保存时占用空间大 | 部分存储, 可能造成空间减少 |
在一般的开发中,因为Serializable接口的使用较为方便,所以出现较多