JDK的新功能序列化接口(Serializableinterface)简化了对象持久化(Persistence)的实现以下介绍如何通过SMTPEmail将对象传送给另一个用户
摘要一些应用程序需要以一种非实时的方式(例如旅行指南错误报告(bugreport) 时间表(timesheet)等)和其余用户共享对象Java语言开发工具包(JDK)版提供了一 个重要的功能:javaioSerializable接口该技术能让你知道如何序列化一个对象然 后用email传给其它用户
对象持久化和用户间对象共享是许多商业解决方案的基础例如一个公司可以用从本公司网址启动的Applet来完成一个时间表的制作同样该公司也可以提供象具有开支报告旅行指 南错误报告(bugreport)等功能的Applet在这些情况下从Applet的使用者获得的数据需要和负责薪水付款旅行房间预订的人们共享执行这些职能的人们可能分布在不同的 城市和国家可能工作在不同的时区不能希望每个工作人员都能象猫头鹰一样在晚上工作以填写这样的表格相同的信息也不应该重新输入因此能够存储并且把这些相关对象传 送到商业应用中是这些applet有别于其它applet的优势
目前已经有许多方法能实现对象的持久化例如使用对象数据库和磁盘文件同样的也有许多办法可以共享对象例如将数据写入一个套接字或者实现一个符合CORBASOM的模 型以上这几种方案均有自己的优点当你设计你的商业解决方案时需要认真地考虑这些方案但是还有一种开销不大但可靠的方法它使用Internet和Intranet用户能够获取的 技术服务在世界范围传送对象的拷贝它就是简单邮件传输协议SMTP
用Email发送Java对象
存储和保存对象的一个简单方法是将对象序列化而后用Email将它发送给别的用户这种 方法有以下优点:
发送的计算机或NC(网络计算机)无需硬盘空间
使用现有的系统传送排队发送对象
允许用户使用最喜欢的邮件客户程序来接受邮件
提供简单的机制将同一对象的拷贝分发给许多人
这种方法也有不足之处:
邮件的传送可能因为Email主机的关机而被较长时间地延迟所有的主机都可能出现这 种情况Email服务器的错误恢复优先级通常比数据库服务器低
邮件的传送不能得到保证在你的Email服务器通知你邮件没有发出时你不得不重新 发送邮件
Email服务器和POP客户程序的功能不足以处理大量交易信息
这些不足和你使用的应用程序有关对于很多商业解决方案这些不足并不重要作为一个设计人员你工作的一部分就是在全面考虑价格性能和需求的情况下确定系统的最佳整体结构
使用Java传送对象的四个步骤:
Applet必须依次以下面所列出的四个步骤传送Java对象:
序列化有关对象
发送时选择Base编码方式对序列化对象编码(RFC)
与一个SMTP服务器连接
将该对象传送到这个SMTP服务器
下面将介绍如何用Email发送一个假设的臭虫报告到公司的质量保证部门
将对象序列化
JDK提供的一个奇妙的机制javaioSerializable接口能够序列化并且重建对象 这个接口能使用存储对象(writeObject())和恢复对象(readObject())方法函数在很多 情况下使用这个接口很方便只需实现并且调用这两个方法函数
以下的代码定义了一个简单的BugReport对象它实现了最简单的序列化接口
import javaIo*;
public class BugReport implements Serializable {
private Float m_SoftwareVersion; // version number from HelpAbout eg
private String m_ErrorDescription; // Description of error
private int m_Severity; // =System unusable =Minor Aesthetic defect
public BugReport (Float SoftwareVersion String ErrorDescription int Severity) {
m_SoftwareVersion = SoftwareVersion;
m_ErrorDesctiption = ErrorDescription;
m_Severity = Severity;
}
public BugReport () {} // for reconstituting serialized objects
public void save (OutputStream os)
throws IOException {
try {
ObjectOutputStream o = new ObjectOutputStream(os);
owriteObject(this);
oflush();
}
catch (IOException e) {throw e;}
}
public BugReport restore (InputStream is)
throws IOException ClassNotFoundException {
BugReport RestoredBugReport = null;
try {
ObjectInputStream o = new ObjectInputStream(is);
RestoredBugReport = (BugReport)oreadObject();
}
catch (IOException e) {throw e;}
catch (ClassNotFoundException e) {throw e;}
return RestoredBugReport;
}
}
使用import语句引入I/O包包括序列化接口
定义类中的成员变量并指出该类实现了序列化接口
提供一个简单的构造函数
一个空的构造函数这个构造函数在重建序列化对象时使用见以下的例子
定义一个方法函数它把对象写入一个已经打开了的ObjectOutputStream这个方 法函数首先创建一个ObjectOutputStream对象然后调用writeObject方法函数最后在 函数返回前显式清空输出缓沖区
定义一个方法函数它从一个打开了的InputStream中读入一个BugReport对象注 意如果输入流中下一个对象和正在读入对象的类型不一致时readObject()将会抛出一 个异常
使用BugReport对象相当简单譬如我们想要创建一个新的BugReport对象并且把它存入 一个文件我们会用到以下代码:
import javaio*;
BugReport bug = new BugReport( Crashes when spell checker invoked );
FileOUtputStream os = new FileOutputStream(MyBugtest);
bugsave(os);
很简单对吗?当然一旦对象已经被序列化没有人能阻止你继续操纵对象的状态上一 个例子中包涵了一个在被写入磁盘时已经存在对象的拷贝因此你必须要十分谨慎以防 在对对象做出所有的修改之后没有序列化对象从而丢失了对象的状态修改信息
以下是怎样恢复一个对象的拷贝:
import javaio*
FileInputStream fis = new FileInputStream(MyBugtest);
BugReport bug = new BugReport()restore(fis);
这更简单!是不是Java的功能越来越强大了?
现在我们修改第二个例子的第行使对象被写入一个字节数组而不是一个文件:
import javaio*
BugReport bug = new BugReport( Crashes when spell checker invoked );
字 节ArrayOutputStream os = new 字 节ArrayOutputStream();
bugsave(os);
好了我们已经构造了一个对象并且学会把它序列化后放入一个字节OutputStream然 后我们将把这个字节OutputStream转化为一个Base编码的字符串
Base编码
目前的Internet Email标准简单邮件传递协议(SMTP)在RFC中宣布对于我们来说 RFC对邮件的内容规定了两条重要但不难实现的限制
邮件的内容必须全部为比特的美国ASCII码
每一行的长度不能超过的字符
因此为了通过SMTP用Email进行传送内存的序列化对象必须转化为和以上相容的格式
RFC提供了一个可行的方案它定义了邮件的内容部分使之能包涵多种形式的数 据这种标准就是目前众所周知的MIME
按照RFC编码过程为:输入是个比特输出是个字节个比特输入组从左至右 由个比特的输入组形成这个比特被看成个连续的比特组而每个比特输入组被翻 译为Base码表中的一个数字
这意味着如果我们有下面的个字节的输入xCxFxFF它将会被转化为如下 的Base的编码:xxFxFxF
图Base编码实例
Base编码似乎有点神秘但实现它的代码却非常简单在下面的程序中我们可以看到 这一点在这个例子中我们创建了一个新类Codecs现在Codecs有两个方法函数:一 个用来对字符数组编码一个用来对String类编码对String类编码的方法函数简单地调 用String类的getBytes()函数然后对返回的结果字符数组进行编码我们将增加从Base解 码至原先格式的方法函数
public class Codecs {
private Codecs() {} // do not instantiate this class
public final static String baseEncode(String strInput) {
if (strInput == null) return null;
byte byteData[] = new byte[strInputlength()];
strInputgetBytes( strInputlength() byteData );
return new String(baseEncode(byteData) );
}
public final static byte[] baseEncode(byte[] byteData) {
if (byteData == null) return null;
int iSrcIdx; // index into source (byteData)
int iDestIdx; // index into destination (byteDest)
byte byteDest[] = new byte[((byteDatalength+)/)*];
for