Java DataInput/DataOutput入门实训
1. 实训目标
- 理解
DataInput和DataOutput接口的作用和设计理念。 - 掌握
DataInputStream和DataOutputStream这两个核心实现类的使用。 - 学会使用
DataInput/DataOutput进行基本数据类型的读写操作。 - 理解数据持久化和网络传输中序列化的基本概念。
2. 核心概念
DataInput 和 DataOutput 是 Java I/O 中的一对接口,位于 java.io 包,核心作用是定义**“结构化数据的读写规范”——即如何读取/写入 Java 基本数据类型(如 int、float、boolean)和字符串,且保证数据的跨平台兼容性**。
1. 为什么需要这两个接口?
底层字节流(如 InputStream/OutputStream)只能读写 byte 数组,无法直接操作基本数据类型:
- 比如要写一个
int(4 字节)到文件,直接用OutputStream需手动把int拆成 4 个byte; - 不同操作系统的字节序(大端/小端)不同,手动拆分可能导致跨平台读取乱码。
DataInput/DataOutput 接口统一了这些操作:
- 提供专门方法直接读写
int、float、String等; - 规定了统一的字节序(大端序),确保不同平台(Windows/Linux/Mac)写入的数据能正确读取。
2. 核心定义与常用方法
DataInput 接口(读取结构化数据)
定义“从数据源读取基本类型和字符串”的规范,常用方法:
| 方法 | 作用 | 备注 |
|---|---|---|
readBoolean() |
读取 1 字节,返回 boolean |
0→false,非 0→true |
readByte() |
读取 1 字节,返回 byte |
范围:-128~127 |
readInt() |
读取 4 字节,返回 int |
按大端序解析 |
readFloat() |
读取 4 字节,返回 float |
遵循 IEEE 754 标准 |
readUTF() |
读取 UTF-8 编码的字符串 | 先读 2 字节表示字符串长度,再读内容 |
readFully(byte[] b) |
读取指定长度的字节到数组 b |
必须读满数组长度,否则阻塞 |
DataOutput 接口(写入结构化数据)
定义“向目的地写入基本类型和字符串”的规范,常用方法(与 DataInput 对应):
| 方法 | 作用 | 备注 |
|---|---|---|
writeBoolean(boolean v) |
写入 1 字节表示 boolean |
false→0,true→1 |
writeByte(int v) |
写入 1 字节(取 v 的低 8 位) |
兼容 byte 和 int 输入 |
writeInt(int v) |
写入 4 字节表示 int |
按大端序写入 |
writeFloat(float v) |
写入 4 字节表示 float |
遵循 IEEE 754 标准 |
writeUTF(String s) |
写入 UTF-8 编码的字符串 | 先写 2 字节表示字符串长度,再写内 |
注意:DataInput / DataOutput 是接口,不能直接实例化。我们通常使用它们的实现类,最常用的就是 DataInputStream 和 DataOutputStream。
3. 常用实现类
DataOutputStream
- 作用: 装饰一个
OutputStream,为其增加写入基本数据类型的能力。 - 构造方法:
public DataOutputStream(OutputStream out) - 示例:
1
2FileOutputStream fileOut = new FileOutputStream("data.bin");
DataOutputStream dataOut = new DataOutputStream(fileOut);
DataInputStream
作用: 装饰一个
InputStream,为其增加读取基本数据类型的能力。构造方法:
public DataInputStream(InputStream in)示例:
1
2FileInputStream fileIn = new FileInputStream("data.bin");
DataInputStream dataIn = new DataInputStream(fileIn);
重要原则:使用 DataOutputStream 写入的数据,必须使用 DataInputStream 按照完全相同的顺序来读取,否则会导致数据解析错误或 EOFException (文件结束异常)。
4. 实践案例:学生信息序列化与反序列化
在这个案例中,你将创建一个程序,它能将一个学生对象的信息(学号、姓名、年龄、身高、是否毕业)写入到一个二进制文件中(序列化),然后再从该文件中读取出来并打印到控制台(反序列化)。
步骤 1: 创建 Student 类
首先,我们创建一个简单的 Student 类来封装数据。
1 | // Student.java |
步骤 2: 编写主程序 DataIODemo.java
这个程序将包含两个主要功能:
writeStudentData(): 将一个Student对象写入文件。readStudentData(): 从文件中读取数据并重建一个Student对象。
1 | import java.io.*; |
测试
1 | // 1. 创建一个学生对象 |
步骤 3: 运行与分析
- 将上述两个 Java 文件放在同一个目录下。
- 编译并运行
DataIODemo。1
2javac Student.java DataIODemo.java
java DataIODemo - 你将看到类似以下的输出:
1
2
3
4准备写入的数据: Student{id=101, name='张三', age=22, height=1.75, isGraduated=false}
数据写入成功!
从文件中读取的数据: Student{id=101, name='张三', age=22, height=1.75, isGraduated=false} - 同时,在你的项目目录下会生成一个名为
student_data.bin的二进制文件。你可以用文本编辑器打开它,但内容会是乱码,因为它不是文本文件而是二进制数据。
代码关键点解析:
- 写入顺序:
writeInt->writeUTF->writeInt->writeDouble->writeBoolean。 - 读取顺序:
readInt->readUTF->readInt->readDouble->readBoolean。 - 顺序必须严格一致:这是使用
DataInput/DataOutput的黄金法则。如果你先写了一个int,然后读的时候却先用readDouble,程序会试图从流的起始位置解析 8 个字节作为double,这必然会得到错误的值,并且后续的所有读取都会错位。 - 与 Java 序列化的区别: 本实训中手动读写字段的方式也可以称为一种“序列化”。Java 还提供了更强大的
Serializable接口,它能自动序列化对象的所有字段。DataInput/DataOutput给了你更精细的控制,但代码量也更大。
5. 扩展与思考
- 练习: 修改
Student类,增加一个String address(地址) 字段。 - 探索: 了解 Java 的
ObjectOutputStream和ObjectInputStream,并比较它们与DataOutputStream/DataInputStream的异同和适用场景。
All articles on this blog are licensed under CC BY-NC-SA 4.0 unless otherwise stated.