众所周知,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编程中,Socket通信是实现网络间进程通信的一种方式,而对象序列化则是将Java对象转换为字节流,以便在网络中传输或存储的重要技术。本教程将深入讲解如何结合Socket通信和对象序列化来实现在不同系统之间传递...
- **服务器端**:创建ServerSocket监听客户端连接,接收到连接后,通过`ObjectOutputStream`将对象写入Socket的输出流。 - **客户端**:通过Socket与服务器建立连接,创建`ObjectInputStream`从Socket的输入流读取...
4. **发送序列化对象**:通过已建立的TCP连接,将序列化后的字节流写入Socket的输出流。 5. **接收和反序列化**:在另一端,从Socket的输入流读取字节流,然后使用相应的反序列化方法恢复为对象。 6. **关闭连接**...
- **基本概念**:序列化是将对象转换为字节流,以便它可以被存储(如写入文件或数据库)或在网络上传输。反序列化则是相反的过程,将字节流恢复为原来的对象。 - **在C#中的实现**:C#提供了`System.Runtime....
在对象序列化场景中,`ObjectOutputStream`和`ObjectInputStream`用于将对象写入或从文件读取。这些类继承自`OutputStream`和`InputStream`,专门用于处理对象的序列化和反序列化。 #### 结论 对象序列化、网络...
前者用于写入序列化对象到输出流,后者用于从输入流读取并反序列化对象。 在本实例中,`SerializationClient`和`Server`分别代表客户端和服务器端。客户端通常负责发起请求,而服务器端则响应这些请求。以下是一些...
在本例中,我们将讨论如何通过Socket进行对象的序列化和反序列化。 首先,让我们看一个简单的Java对象`User`,它有一个`name`属性和对应的getter、setter方法。在尝试通过Socket进行对象传输时,我们首先创建了一个...
3. 通过客户端Socket创建输入流,用于接收数据,`InputStream in = clientSocket.getInputStream();` 4. 读取输入流中的数据并保存为文件,可能需要使用`FileOutputStream`。 5. 关闭相关的输入流和Socket连接。 在...
如果涉及到网络传输,可以使用`Socket`类结合`ObjectInputStream`和`ObjectOutputStream`进行对象的序列化和反序列化。 3. **C#中的文件传输流** C#提供了相似的机制,如`System.IO.FileStream`类,配合`...
前者通过`writeObject()`方法将对象写入输出流,后者通过`readObject()`方法从输入流中读取对象。 需要注意的是,只有实现了`Serializable`或`Externalizable`接口的类才能被序列化。`Externalizable`接口允许对象...
3. **对象流**:`ObjectOutputStream`用于序列化Java对象,可以将对象写入文件或网络。 4. **网络流**:`SocketOutputStream`和`ServerSocketOutputStream`用于网络数据的发送。 ### 流的连接与转换 Java I/O系统...
为了序列化一个对象,我们需要在类定义上添加`implements Serializable`,然后可以使用`ObjectOutputStream`将对象写入输出流,用`ObjectInputStream`从输入流读取。以下是一个简单的示例: ```java // 定义一个可...
总之,Java Socket网络传输的序列化机制是通过将对象转换为字节流在网络中传输,然后在目标机器上恢复为原始对象。这种方式简化了网络通信,但也需要注意数据兼容性、安全性和性能优化等问题。在实际应用中,可以...
1. 序列化(Serialization):在Java中,对象序列化是将对象的状态转换为字节序列的过程,以便存储或在网络上传输。要使一个类支持序列化,只需要在类定义上添加`implements Serializable`即可。序列化时,Java会...
然后,通过`ObjectInputStream`读取Socket的输入流,反序列化接收到的字节数流,恢复成原来的对象。 以下是一个简单的示例,展示了如何在服务器端发送对象和客户端接收对象的基本步骤: 服务器端: ```java ...
- Java的对象序列化用于将对象转换为字节流,以便在网络中传输。在聊天应用中,可能会将消息对象序列化后发送到服务器,服务器再反序列化恢复成对象。 6. **文件I/O操作**: - 文件的上传和下载涉及Java的File类...
首先,Java的IO流是数据传输的基础,分为输入流(InputStream)和输出流(OutputStream)。它们提供了一种在不同数据源之间读写数据的方式,如硬盘、内存、网络等。IO流按照处理数据的不同类型,又分为字节流(处理...
在实际开发中,我们还会接触到网络流(如`Socket`和`ServerSocket`)以及对象流(如`ObjectInputStream`和`ObjectOutputStream`),它们分别用于网络数据传输和序列化对象的存储与恢复。 总之,Java的输入输出流是...
4. **文件上传**:在客户端,FileClient首先打开本地文件,然后通过DataOutputStream将文件内容写入Socket的输出流,从而发送到服务器。 5. **文件下载**:在服务器端,FileServer接收到客户端的文件数据后,会通过...
3. **Socket数据传输**:在Java中,我们可以使用Socket对象的getOutputStream()和getInputStream()方法来获取输出流和输入流,然后通过这些流进行文件数据的发送和接收。为了防止阻塞,通常会使用ObjectOutputStream...