哈喽,哈喽,大家好~ 我是你们的老朋友:保护小周ღ
今天给大家带来的是 【Java IO流】对象与字节流的序列化和反序列化,首先了解, 本次主题有啥实际应用, 学习 ByteArrayOutputStream / ByteArrayInputStream 字节数组流, ObjectOutputStream / ObjectInputStream 对象与字节流的序列化和反序列化 , 前者作为当期流的输出目的, 和数据来源, 一起来看看叭~
本期收录于博主的专栏:JavaSE_保护小周ღ的博客-CSDN博客
适用于编程初学者,感兴趣的朋友们可以订阅,查看其它 “JavaSE基础知识”。
更多精彩敬请期待:保护小周ღ *★,°*:.☆( ̄▽ ̄)/$:*.°★* ‘
对象与字节流的序列化和反序列化在 Java 开发中具有重要作用,主要用于将对象状态转换为字节流以便存储或传输。以下是它们的一些作用介绍:
作用
-
持久化存储:
- 将对象的状态保存到文件或数据库,以便在系统重启时恢复。
- 例如,将用户设置、游戏进度等信息保存到磁盘。
-
网络传输:
- 在分布式系统或者微服务架构中,通过网络发送对象,以实现远程通信。
- 例如,使用 RMI(远程方法调用)或者 gRPC 进行服务间调用时,需要传输对象。
-
深拷贝:
- 创建对象的深拷贝,避免引用类型字段造成的共享问题。
- 例如,在需要保护原始对象不被修改时,可以通过序列化和反序列化来复制对象。
-
缓存:
- 将对象序列化后存储在内存中,减少重复计算,提高性能。
- 例如,使用 Redis 等缓存工具时,可以序列化对象以便快速读取
一、处理字节数组的流类
ByteArrayOutputStream
和 ByteArrayInputStream
是 Java 中用于处理字节数组的流类,通常用于输入和输出操作。
1.1 ByteArrayOutputStream
功能
ByteArrayOutputStream
允许你将数据写入一个字节数组中,而不是直接写入文件或其他输出流。- 这个类提供了一个可动态扩展的字节数组,可以随时获取当前写入的数据。
用法
- 创建一个
ByteArrayOutputStream
流对象。 - 使用
write()
方法将字节写入到流中。 - 调用
toByteArray()
方法获取写入的字节数组。
public class ByteArrayOutputStreamExample {
public static void main(String[] args) {
// 这种将流对象写在 try 中的写法, 这种结构确保在操作完成后,相关的资源(如流)会被自动关闭,从而避免内存泄漏。
try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
String data = "Hello, World!";
// getBytes() 方法就是将字符串转换为字节数组, 写入流对象
bos.write(data.getBytes());
// 获取字节数组
byte[] byteArray = bos.toByteArray();
System.out.println("Written bytes: " + new String(byteArray));
} catch (Exception e) {
e.printStackTrace();
}
}
}
1.2 ByteArrayInputStream
功能
ByteArrayInputStream
允许你从一个字节数组中读取数据。- 可以用来在内存中模拟输入流操作,方便进行数据处理。
用法
- 创建一个
ByteArrayInputStream
对象,并传入一个字节数组。 - 使用
read()
方法读取字节,或者使用read(byte[] b)
方法将数据读取到字节数组中。
public class ByteArrayInputStreamExample {
public static void main(String[] args) {
byte[] inputData = "Hello, World!".getBytes();
try (ByteArrayInputStream bis = new ByteArrayInputStream(inputData)) {
int data;
int count = 0;
while ((data = bis.read()) != -1) {
count++;
System.out.print((char) data); // 转换为字符并打印
}
System.out.println();
// 说明一次读取四个字节, 但是也需要读取 13 次, Hello, World! 13个字符
System.out.println(count);
} catch (Exception e) {
e.printStackTrace();
}
}
}
二、序列化与反序列化的流类
ObjectOutputStream
和 ObjectInputStream
是 Java 中用于对象的序列化与反序列化的流类。这两个类使得将对象写入流并从流中读取对象变得非常简单。
注意:若是想一个类可具备序列化和反序列化的能力,需要该类实现 Serializable 接口。
- 只序列化可序列化的成员变量。如果某个字段不需要序列化,可以使用
transient
关键字修饰该字段。- 如果类的结构发生变化(如修改了字段),可能会导致反序列化失败,这时可以使用
serialVersionUID
来版本控制。
2.1 ObjectOutputStream
功能
ObjectOutputStream
可用于将 Java 对象序列化为字节流,以便存储或通过网络传输。- 它将对象的状态(包括对象的属性)转换为字节序列。
用法
- 创建一个
FileOutputStream(文件流)
以指定输出目的地(如文件)。 - 创建一个
ByteArrayOutputStream(字节数组)
就可以输出到字节数组啦。 - 使用
ObjectOutputStream
将对象写入到流中。 - 调用
writeObject()
方法将对象序列化。
public class Person implements Serializable { //一定要实现Serializable 接口才可以序列化和反序列化
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + "}";
}
}
public class ObjectOutputStreamExample {
public static void main(String[] args) {
try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); // 序列化后往哪个流里面写, 也可以是 FileOutputSteam
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream)) { // 这种写法可以自动关闭资源
Person person = new Person("Alice", 30);
objectOutputStream.writeObject(person); // 序列化对象
System.out.println("Object serialized: " + person);
// 存储在字节数据流中, 以字节数组的形式输出
System.out.println(Arrays.toString(byteArrayOutputStream.toByteArray()));
} catch (Exception e) {
e.printStackTrace();
}
}
}
2.2 ObjectInputStream
功能
ObjectInputStream
用于从字节流中反序列化对象,即读取序列化后的对象数据并恢复成 Java 对象。
用法
- 创建一个
FileInputStream
(文件流) 指向序列化对象的文件。 - 创建一个
ByteArrayInputStream(字节数组)
指向序列化对象的字节数组流。 - 使用
ObjectInputStream
从流中读取对象。 - 调用
readObject()
方法获取对象。
public class ObjectInputStreamExample {
public static void main(String[] args) {
// 用于存储 Person 对象序列化后字节
byte[] personBytes = new byte[0];
// 1. 先将 Person 对象序列化为字节数组
try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream)) { // 这种写法可以自动关闭资源
Person person = new Person("Alice", 30);
objectOutputStream.writeObject(person); // 序列化对象
System.out.println("Object serialized: " + person);
// 存储在字节数据流中, 以字节数组的形式输出
byte[] data = byteArrayOutputStream.toByteArray();
personBytes = new byte[data.length];
personBytes = byteArrayOutputStream.toByteArray();
System.out.println(Arrays.toString(personBytes));
} catch (Exception e) {
e.printStackTrace();
}
// 2. 将字节数组personBytes, 反序列化为 person 对象
try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(personBytes); // 从哪个字节数组中读取
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream)) {
Person person = (Person) objectInputStream.readObject();// 反序列化对象
System.out.println("----------------------------------");
System.out.println("Object deserialized: " + person);
} catch (Exception e) {
e.printStackTrace();
}
}
}
总结
- 序列化:使用
ObjectOutputStream
将对象转化为字节流,可以存储或传输。- 反序列化:使用
ObjectInputStream
从字节流中恢复对象状态。这两个类主要用于持久化对象,确保对象状态可以在不同的程序运行间保存和恢复。注意,参与序列化的对象必须实现
Serializable
接口。且序列化/ 反序列化的字节流也可以使用 FileOutputStream 和 FileInputStream (文件流)来作为输出目的地和恢复的源头.
三、封装工具类
根据上述所讲, 我们可以将一个对象序列化为字节流, 也可以将字节流序列化为对象. 那我们就可以在开发中针对上述方法进行封装使用.
/**
* Created with IntelliJ IDEA.
* Description: 二进制序列化工具
* Author: 保护小周
* Date: 2024-08-21
* Time: 23:22
*/
public class BinaryTool {
/**
* 把一个对象序列化成一个字节数组
* @param object
* @return
*/
public static byte[] toBytes(Object object) throws IOException {
// 这个流对象相当于一个变长的字节数组.
// 就可以把 object 序列化的数据给逐渐的写入到 byteArrayOutputStream 中, 再统一转成 byte[]
try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream)) {
// 此处的 writeObject 就会把该对象进行序列化, 生成的二进制字节数据, 就会写入到
// ObjectOutputStream 中.
// 由于 ObjectOutputStream 又是关联到了 ByteArrayOutputStream, 最终结果就写入到 ByteArrayOutputStream 里了
objectOutputStream.writeObject(object);
}
// 这个操作就是把 byteArrayOutputStream 中持有的二进制数据取出来, 转成 byte[]
return byteArrayOutputStream.toByteArray();
}
}
/**
* 把一个字节数组, 反序列化成一个对象
* @param data
* @return
*/
public static Object fromBytes(byte[] data) throws IOException, ClassNotFoundException {
Object object = null;
// 这个流对象相当于一个边长的字节数组
// 就可以把object 序列化的数据给逐渐的写入到 byteArrayOutStream.
try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(data)) {
try (ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream)) {
// 此处的 readObject 就是从 data 这个 byte[] 中读取数据并进行反序列化.
object = objectInputStream.readObject();
}
}
return object;
}
}
实际应用效果展示: 非常的实用呀~
好了,到这里,【Java IO流】对象与字节流的序列化和反序列化 博主已经分享完了,阐述较为基础, 希望对大家有所帮助,如有不妥之处欢迎批评指正。
感谢每一位观看本篇文章的朋友,更多精彩敬请期待:保护小周ღ *★,°*:.☆( ̄▽ ̄)/$:*.°★*
遇见你,所有的星星都落在我的头上……