java对象序列化技术

Java中对象的序列化(serialization)允许把采用Serializable接口的任何对象转换成字节流序列;同时它也允许把字节流序列转换回对象本身。其机制不依赖于操作系统,也就是说,你可以通过网络传递该对象,并在网络连线的另一端把它们恢复。

使用sterilization,你可以很容易实现所谓的“lightweight persistence”(轻量级持久化),让对象的生命周期超越程序的生命周期。Serializtion机制被引入到Java语言中来有两个原因:(1)JavaBeans机制用到了serialization,及(2)远程方法调用(RMI)允许你自动使用位于网络另一端的对象,就如同使用本地对象一般。

Serializable 接口

很容易把任何采用Serializable接口的对象进行序列化。这个接口不包含任何方法,所以它对于编译器和Java 虚拟机(JVM)而言不过是一个标记,向它们声明自己是可以被序列化的。自从序列化机制引入到这种语言之后,许多标准Java类(所有的原始类型封装类,如Integer类和Double类,所有的容器类,和Class类)都重新进行了修改以支持序列化。

为了序列化一个对象,你需要用到输出流OutputStream,OutputStream必需被放入特殊的称作ObjectOutputStream的序列化流中。然后,你只需要调用方法writeObject()来序列化对象并发送到输出流中。为了反序列化一个对象,你需要把InputStream转化进ObjectInputStream中,然后调用readObject()方法。和往常一样,你需要得到一个对象的引用,所以你也得构造所需类型的对象的class cast。

序列化机制可以正确处理对象包含的所有引用。所有含有实时引用的连接对象也会被序列化,就如同所有连接对象的所有连接对象,以此类推。这有时被叫作对象网络。清单A向你显示了如何反序列化一个对象。

清单A

import java.io.*;

public class Data implements Serializable {
public static void main(String[] args)
throws ClassNotFoundException, IOException {
Data d = new Data();
ObjectOutputStream out = new ObjectOutputStream(
new FileOutputStream("data.out"));
out.writeObject("Data storage");
out.writeObject(d);
out.close();

ObjectInputStream in = new ObjectInputStream(
new FileInputStream("data.out"));
String s = (String)in.readObject();
Data d2 = (Data)in.readObject();
}
}

在你创建了一个ObjectOutputStream(基于另外一个输出流)之后,writeObject()方法把对象写入该流。注意我把字符串对象写入了流,之后又轻松地对它进行了反序列化操作。JVM会看管好对象长度,所以不要担心对象的尺寸和结构。你可以把对象写入任何流;例如,RMI把对象写入到网络流中。同时需要注意,反序列化过程中不调用任何方法,包括构造器函数。

寻找类

让我们假设你把一个对象传进了另一台主机。那么它是否真的能够根据从网络接收到的信息,在那里重构而使用呢?能,完全可以,但只有当JVM知道该对象的类之后才行。

清单B中,我从文件中读取序列化了的对象,然后试图通过一个类的getClass()方法得到它的类。如果返回的类不包括在classpath之中,那么函数将抛出ClassNotFoundException例外。

清单B

import java.io.*;

public class GetAlienClass {
public static void main(String[] args)
throws IOException, ClassNotFoundException {
ObjectInputStream in = new ObjectInputStream(
new FileInputStream("alien.obj"));
Object obj = in.readObject();

// Here you will get ClassNotFoundException
System.out.println(obj.getClass());
}
}

 

Externalizable 接口

对于序列化对象,有时候你会有特殊要求。例如,你的对象可能有一些安全敏感的部分,就像密码,对这些内容你不希望保留并传送到其他地方。或者,在保存主要对象中所引用的另一个对象显得毫无用处时,因为在恢复之后它的数值将没有利用价值。

你在控制序列化的过程中,可以不采用Serializable接口,取而代之,你可以采用Externalizable接口。这个接口扩充了原来的Serializable接口,加入了writeExternal() 和 readExternal()方法。这两个方法在对象的序列化和反序列化中自动被调用,通过它们,你可以控制整个过程。

在serialization 和 externalization之间有一个极大的不同:对于externalization,当你序列化一个Externalizable对象时,有一个默认的构造函数会被自动调用;只有在那之后才会轮到readExternal()函数被调用。清单 C向你显示了如何使用externalization。

清单 C

import java.io.*;
import java.util.*;

class Data implements Externalizable {
inti;
String s;
public Data() {
System.out.println("Data default constructor");
}
public Data(String x, int a) {
System.out.println("Second constructor");
s = x; i = a;
}
public String toString() {
return s + i;
}
public void writeExternal(ObjectOutput out)
throws IOException
public void readExternal(ObjectInput in) {
s = (String)in.readObject();
i = in.readInt();
}
public static void main(String[] args)
throws IOException, ClassNotFoundException {
Data d = new Data("String value",1514);
System.out.println(d);
ObjectOutputStream o = new ObjectOutputStream(
New FileOutputStream("data.out"));
o.writeObject(d);
o.close();

// Now deserialize
ObjectInputStream in = new ObjectInputStream(
new FileInputStream("data.out"));
d = (Data)in.readObject();
}
}

如果你从采用Externalizable接口的类继承了一些类,那么为了正确的保存和恢复这个对象,你必须在序列化或反序列化时调用writeExternal()和readExternal()方法。

transient”(临时)关键词

如果你不想保存或还原每一个成员变量(例如,保存在字符串对象中的密码),冠以“private”(私有)修饰字将无济于事。你可以采用Externalizable接口,这已在上一段阐述。这种情况下不会自动写入任何东西,并且你可以控制整个过程。

可是,serializable对象要方便许多,因为一切都会自动进行序列化。这时,你可以藉transient修饰字禁止成员变量对象的序列化。它会告诉JVM:“请不要保存和恢复这个字段;有其他人会照管好这个字段的。”清单D显示了其大体情况。

清单 D

import java.io.*;
import java.util.*;

class LoginCredentials implements Serializable {

private String username;
private transient String password;

LoginCredentials(String name, String password) {
username = name;
this.password = password;
}

public static void main(String[] args)
throws IOException, ClassNotFoundException {
LoginCredentials = new LoginCredentials("peter","mikhalenko");
}
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值