众所周知,java.io.InputStream是不可序列化的,但是如何序列化一个带有InputStream的类呢?可以通过将流转换成字节数组来实现,这里利用序列化的机制来实现。
在java.io.Serializable这个标记接口的API中有这样的描述:
在序列化和反序列化过程中需要特殊处理的类必须使用下列准确签名来实现特殊方法:
private void writeObject(java.io.ObjectOutputStream out) throws IOException
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException;
writeObject 方法负责写入特定类的对象的状态,以便相应的 readObject 方法可以还原它。通过调用
out.defaultWriteObject 可以调用保存 Object 的字段的默认机制。该方法本身不需要涉及属于其超类或子类的状态。状态是通过使用
writeObject 方法或使用 DataOutput 支持的用于基本数据类型的方法将各个字段写入 ObjectOutputStream 来保存的。
下面就用这个机制来实现:
import java.io.*;
public class Test {
public static void main(String... arguments) throws Exception {
if(arguments.length != 1) {
System.out.println("Usage:java Test [w|r]");
return;
}
String option = arguments[0];
if("w".equalsIgnoreCase(option)) {
FileOutputStream fos = null; //序列化后的文件
ObjectOutputStream oos = null; //序列化后的文件
FileInputStream fis = null; //读取的文件流
try {
fos = new FileOutputStream("a.dat");
oos = new ObjectOutputStream(fos);
fis = new FileInputStream("刘雁 - 断桥伞.mp3");
Document doc = new Document("断桥伞.mp3", fis);
oos.writeObject(doc);
} finally {
if(oos != null) {
oos.close();
}
if(fos != null) {
fos.close();
}
if(fis != null) {
fis.close();
}
}
} else if("r".equalsIgnoreCase(option)) {
FileInputStream fis = null;
ObjectInputStream ois = null;
try {
fis = new FileInputStream("a.dat");
ois = new ObjectInputStream(fis);
Document doc = (Document) ois.readObject();
doc.saveFile();
} finally {
if(ois != null) {
ois.close();
}
if(fis != null) {
fis.close();
}
}
} else {
System.out.println("Usage:java Test [w|r]");
}
}
}
class Document implements Serializable {
private String fileName;
//InputStream 不能被序列化
private transient InputStream inputStream;
public Document(String fileName, InputStream inputStream) {
if(fileName == null || fileName.trim().length() == 0 || inputStream == null) {
throw new IllegalArgumentException("fileName:" + fileName + " | inputStream:" + inputStream);
}
this.fileName = fileName;
this.inputStream = inputStream;
}
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
System.out.println("调用了ReadObject");
ois.defaultReadObject();//读取可反序列化的内容
//读取流
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int ret = -1;
while((ret = ois.read(buffer, 0, 1024)) != -1) {
baos.write(buffer, 0, ret);
}
byte[] data = baos.toByteArray();
inputStream = new ByteArrayInputStream(data);
}
private void writeObject(ObjectOutputStream oos) throws IOException {
System.out.println("调用了writeObject");
oos.defaultWriteObject();//将可以序列化的内容写出到流
//接下来将inputStream也写出
byte[] buffer = new byte[1024];
int ret = -1;
while((ret = inputStream.read(buffer, 0, 1024)) != -1) {
oos.write(buffer, 0, ret);
}
oos.flush();
}
public void saveFile() throws IOException, FileNotFoundException {
FileOutputStream fos = null;
try {
fos = new FileOutputStream(fileName);
byte[] buffer = new byte[1024];
int ret = -1;
while((ret = inputStream.read(buffer, 0, 1024)) != -1) {
fos.write(buffer, 0, ret);
}
} finally {
if(fos != null) {
fos.close();
}
}
}
}
在Test.class文件的同一目录下放入一个mp3文件,我用的是:刘雁 - 断桥伞.mp3
Document类是一个可序列化类,包括两个字段,输入流和一个文件名fileName,可以通过调用该类对象的saveFile方法将该输入流中的数据保存以fileName为名称的文件。
执行java Test w的时候,将一个Document类实例序列化后写入文件,包括“刘雁 - 断桥伞.mp3”的数据。
执行java Test r,将从之前写入的序列化文件中读取Document对象,然后调用其上的saveFile方法,保存数据。
通常,我们很少需要在Serializable类中添加readObject和writeObject方法,这里为了写入inputStream字段的数据,添加了这两个方法,这两个方法的签名必须同上述描述中的一样,否则不会在序列化或反序列化对象时调用,所以在测试时,总是打印一句话以确保这两个方法确实在序列化或反序列化时调用了
将inputStream的数据写入序列化文件很简单,从inputStream读取字节,然后写入objectOutputStream。
从序列化文件读取数据亦然,在读取了可序列化字段后,然后从objectInputStream中读取字节。
分享到:
相关推荐
Java数据压缩与传输实例,可以学习一下实例化套按字、得到文件输入流、压缩输入流、文件输出流、实例化缓冲区、写入数据到文件、关闭输入流、关闭套接字关闭输出流、输出错误信息等Java编程小技巧。 Java数组倒置...
Java数据压缩与传输实例,可以学习一下实例化套按字、得到文件输入流、压缩输入流、文件输出流、实例化缓冲区、写入数据到文件、关闭输入流、关闭套接字关闭输出流、输出错误信息等Java编程小技巧。 Java数组倒置...
Java数据压缩与传输实例,可以学习一下实例化套按字、得到文件输入流、压缩输入流、文件输出流、实例化缓冲区、写入数据到文件、关闭输入流、关闭套接字关闭输出流、输出错误信息等Java编程小技巧。 Java数组倒置...
Java数据压缩与传输实例,可以学习一下实例化套按字、得到文件输入流、压缩输入流、文件输出流、实例化缓冲区、写入数据到文件、关闭输入流、关闭套接字关闭输出流、输出错误信息等Java编程小技巧。 Java数组倒置...
Java数据压缩与传输实例,可以学习一下实例化套按字、得到文件输入流、压缩输入流、文件输出流、实例化缓冲区、写入数据到文件、关闭输入流、关闭套接字关闭输出流、输出错误信息等Java编程小技巧。 Java数组倒置...
Java数据压缩与传输实例 1个目标文件 摘要:Java源码,文件操作,数据压缩,文件传输 Java数据压缩与传输实例,可以学习一下实例化套按字、得到文件输入流、压缩输入流、文件输出流、实例化缓冲区、写入数据到文件、...
Java数据压缩与传输实例 1个目标文件 摘要:Java源码,文件操作,数据压缩,文件传输 Java数据压缩与传输实例,可以学习一下实例化套按字、得到文件输入流、压缩输入流、文件输出流、实例化缓冲区、写入数据到文件、...
Java数据压缩与传输实例,可以学习一下实例化套按字、得到文件输入流、压缩输入流、文件输出流、实例化缓冲区、写入数据到文件、关闭输入流、关闭套接字关闭输出流、输出错误信息等Java编程小技巧。 Java数组倒置...
13.1 通过Socket数据报传输消息 464 13.2 从Web抓取文档 466 13.3 过滤FTP站点列表 467 13.4 通过SNTP协议从服务器获取时间 468 13.5 发送HTML邮件 469 13.6 在MIME消息中绑入文件 471 13.7 拆解一个分段MIME...
1.1.5 Console类——控制台中的输入流、输出流和错误流 6 1.1.6 Convert类——类型转换 8 1.1.7 常量——值不改变的量 9 1.1.8 Dispose方法——释放资源 10 1.1.9 迭代器——相同类型的值的有序序列的一段代码 10 ...
//将字符编码为一个字节序列 byte[] md5data = md5.ComputeHash(data); //计算data字节数组的哈希值 md5.Clear(); //清空MD5 对象 string str = ""; //定义一个变量,用来记录加密后的密码 for (int i = 0; i ; i++...
中,如果不关心a[]的哪一个分量会被写入,这段代码就没有问题,i也的确会增加1,对吗? 38 3.11 人们总是说i=i++的行为是未定义的。可我刚刚在一个ANSI编译器上尝试过,其结果正如我所期望的。 38 3.12 我不...
第1章 声明和初始化 基本类型 1.1 我该如何决定使用哪种整数类型? 1.2 为什么不精确定义标准类型的大小? 1.3 因为C语言没有精确定义类型的大小,所以我一般都用typedef定义int16和int32。然后根据实际的...
``序列点" 是什么? o 4.8 那么, 对于 a[i] = i++; 我们不知道 a[] 的哪一个分量会被改写,但 i 的确会增加 1, 对吗? o 4.9 ++i 和 i++ 有什么区别? o 4.10 如果我不使用表达式的值, 我应该用 ++i 或 i++ 来自增...