在 Java 中,对象转化成字节流的过程通常称为序列化。序列化后的字节流包含对象的字段值以及一些其他信息,用于对象恢复或在网络上传输。Java 中可以使用 ObjectOutputStream 类进行对象序列化,并使用 ObjectInputStream 进行反序列化。
字节流的内容
Java 对象序列化后的字节流并不是直接的对象数据,而是包含了许多元数据。这些元数据用于帮助 Java 反序列化对象,包括:
- 类的描述信息(类名、类的序列化版本等)
- 类字段的类型和字段名
- 字段的数据值
- 对象的引用关系(如嵌套对象)
序列化后的字节流通常比对象本身要大一些,因为它包含了恢复对象所需的元数据。
字节流的结构
当 Java 对象通过 ObjectOutputStream 被序列化时,它的字节流大致会包含以下几个部分:
- 流头信息:Java 序列化流的标记,包括流格式版本号,用于标识数据是序列化对象。
- 类描述:被序列化对象的类信息,包括类名和
serialVersionUID(用于反序列化时的一致性检查)。 - 字段信息:对象所有字段的名称、类型和值。
- 嵌套对象:如果对象中有引用其他对象,序列化流会嵌套这些对象的信息。
字节流示例
假设有一个简单的 Person 类,通过序列化可以生成其对应的字节流。
import java.io.Serializable;
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
然后可以将 Person 对象序列化为字节流:
import java.io.*;
public class SerializeExample {
public static void main(String[] args) {
Person person = new Person("Alice", 25);
try (FileOutputStream fileOut = new FileOutputStream("person.ser");
ObjectOutputStream out = new ObjectOutputStream(fileOut)) {
out.writeObject(person);
System.out.println("Serialized data is saved in person.ser");
} catch (IOException i) {
i.printStackTrace();
}
}
}
字节流示例(十六进制表示)
序列化后的文件 person.ser 中会包含类似以下内容的字节流(以十六进制表示):
AC ED 00 05 73 72 00 06 50 65 72 73 6F 6E E7 83 C4 20 49 2A 1F 03 00 02 4C 00 04 6E 61 6D 65 74 00 12 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B 49 00 03 61 67 65 78 70 74 00 05 41 6C 69 63 65 78 70 1A
字节流内容解释
AC ED 00 05:序列化标记,表示 Java 的序列化格式。73 72:标记一个新类的描述。00 06 50 65 72 73 6F 6E:类名 "Person"。E7 83 C4 20 49 2A 1F:serialVersionUID的部分。03 00 02:对象字段数为 2。4C 00 04 6E 61 6D 65:字段name。74 00 12 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B:name字段的类型。49 00 03 61 67 65:字段age。78 70:表示对象结束。
不同的 Java 序列化对象会产生不同的字节流,但都包含类似的标识符和结构,以便反序列化时能解析回对象。
思考题:字节流只能十六进制表示吗?
字节流不仅可以用十六进制表示,还可以使用其他形式表示,例如二进制、十进制、Base64,甚至是 字符表示(在可读的情况下)。下面是常用的几种表示方式:
1. 二进制表示
- 字节流中的每个字节可以用8位二进制来表示。对于开发调试来说,二进制表示能让我们看到具体的位信息,但不便于直接阅读,因为二进制长度很长,通常只在低级编程或需要逐位分析时使用。
示例:10101100 11101101 00000000 00000101
2. 十六进制表示
- 十六进制表示是最常见的表示方法,每个字节用两个十六进制字符表示,简洁且易于阅读。常见的文件编辑器(如十六进制编辑器)和调试工具通常都以这种形式显示字节流。
示例:AC ED 00 05
3. 十进制表示
- 每个字节都可以用十进制表示。虽然我们熟悉十进制数字,但在字节流表示上它并不常用,因为读起来较不直观。十进制表示一般用在数学运算、校验和计算等场景中。
示例:172 237 0 5
4. Base64 表示
- Base64 是一种常用的编码方式,常用于在文本中传输二进制数据(如 JSON、XML 等),也广泛应用于网络协议和图像编码。Base64 将三字节数据编码为四个字符,是一种有效的二进制到文本的转换方式。
示例:rO0ABQ==
5. 字符表示
- 如果字节流可以转换为可读的 ASCII 字符(例如字母、数字、常用符号等),则可以用字符表示。但是字符表示非常依赖数据内容,当字节流中的数据不属于 ASCII 范围时,会出现乱码。字符表示常用于纯文本内容的字节流。
示例:可能得到一些文本表示,如:Hello。但对于非文本二进制数据,直接字符转换会导致乱码,无法正确表示。
总结
不同的表示方式有各自的用途,实际使用中可以根据需要选择合适的表示形式。例如:
- 调试:使用十六进制或二进制。
- 网络传输:使用 Base64(尤其是文本协议)。
- 显示给用户:使用字符表示(仅限可显示的文本数据)。