二进制文件不能直接用 String 来读取和写入,因为 String 是基于字符的,会涉及到字符编码(如 UTF-8, GBK),这会破坏二进制数据的完整性,必须使用字节流来操作。

Java 提供了 InputStream 和 OutputStream 两个抽象基类以及它们的具体实现类来处理二进制数据,现代 Java 开发更推荐使用 NIO (New I/O) 中的 FileChannel 和 ByteBuffer,它们在性能上通常更优。
下面我将详细介绍几种打开和操作二进制文件的方法,从基础到现代实践。
使用 InputStream 和 OutputStream (经典方式)
这是最传统、最基础的方法,适用于所有 Java 版本。
读取二进制文件 (FileInputStream)
使用 FileInputStream 将文件中的字节读入一个字节数组。

示例代码:读取一个图片文件并打印其字节大小
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
public class ReadBinaryFile {
public static void main(String[] args) {
// 1. 指定要读取的文件路径
File file = new File("path/to/your/image.jpg"); // 请替换为你的文件路径
byte[] fileContent = new byte[(int) file.length()]; // 创建一个与文件大小相同的字节数组
// 使用 try-with-resources 语句,确保流在使用后被自动关闭
try (FileInputStream fis = new FileInputStream(file)) {
// 2. 读取文件内容到字节数组中
int bytesRead = fis.read(fileContent);
// 3. 验证是否读取成功
if (bytesRead != -1) {
System.out.println("成功读取文件,共读取 " + bytesRead + " 个字节。");
// 注意:直接打印字节数组会得到内存地址,而不是文件内容
// System.out.println(fileContent); // 不要这样做!
// 如果你需要处理这些字节,
// - 发送到网络
// - 写入另一个文件
// - 解析特定格式的数据
// 你可以遍历 fileContent 数组
System.out.println("文件的前10个字节是:");
for (int i = 0; i < 10 && i < fileContent.length; i++) {
System.out.print(fileContent[i] + " ");
}
}
} catch (IOException e) {
System.err.println("读取文件时发生错误: " + e.getMessage());
e.printStackTrace();
}
}
}
写入二进制文件 (FileOutputStream)
使用 FileOutputStream 将一个字节数组写入到文件中。
示例代码:将一个字节数组写入到新文件中
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
public class WriteBinaryFile {
public static void main(String[] args) {
// 1. 准备要写入的二进制数据
String data = "这是一个测试字符串,将被转换为二进制数据。";
byte[] dataToWrite = data.getBytes(); // 将字符串转换为字节数组 (使用平台默认编码)
// 2. 指定要写入的文件路径
File outputFile = new File("path/to/your/output.dat"); // 请替换为你想保存的路径
// 使用 try-with-resources 语句
try (FileOutputStream fos = new FileOutputStream(outputFile)) {
// 3. 写入字节数组到文件
fos.write(dataToWrite);
System.out.println("数据成功写入文件: " + outputFile.getAbsolutePath());
} catch (IOException e) {
System.err.println("写入文件时发生错误: " + e.getMessage());
e.printStackTrace();
}
}
}
使用 BufferedInputStream 和 BufferedOutputStream (优化性能)
当处理大文件时,直接使用 FileInputStream 每次可能只读取一个字节,这会导致大量的磁盘 I/O 操作,效率很低,使用缓冲流可以解决这个问题。

缓冲流会在内存中创建一个缓冲区(默认通常是 8KB),每次读取时,它会尽可能多地将数据从磁盘读入缓冲区,然后程序从缓冲区中读取数据,从而大大减少磁盘 I/O 次数。
示例代码:使用缓冲流复制一个视频文件
import java.io.*;
public class CopyBinaryFileWithBuffer {
public static void main(String[] args) {
File sourceFile = new File("path/to/your/source_video.mp4");
File destFile = new File("path/to/your/dest_video_copy.mp4");
// 使用 try-with-resources,确保所有流都被正确关闭
try (InputStream bis = new BufferedInputStream(new FileInputStream(sourceFile));
OutputStream bos = new BufferedOutputStream(new FileOutputStream(destFile))) {
byte[] buffer = new byte[8192]; // 8KB 的缓冲区
int bytesRead;
// 循环读取,直到文件末尾 (read() 返回 -1 表示结束)
while ((bytesRead = bis.read(buffer)) != -1) {
// 将从缓冲区读取到的数据写入目标文件
bos.write(buffer, 0, bytesRead);
}
System.out.println("文件复制成功!");
} catch (IOException e) {
System.err.println("复制文件时发生错误: " + e.getMessage());
e.printStackTrace();
}
}
}
关键点:
BufferedInputStream和BufferedOutputStream是对字节流的包装,它们本身不直接与文件交互。- 必须提供
InputStream或OutputStream的构造参数。 - 缓冲区大小(如
8192)可以根据实际情况调整,8KB 是一个不错的默认值。
使用 NIO (FileChannel 和 ByteBuffer) (现代高性能方式)
从 Java 1.4 开始引入的 NIO (New I/O) 提供了另一种更强大、更高效的文件操作方式,尤其适合大文件传输。
FileChannel 可以看作是文件的一个通道,ByteBuffer 则是数据的容器,NIO 的一个巨大优势是“零拷贝”,在数据传输时,可以减少内核空间和用户空间之间的数据拷贝,从而提高性能。
示例代码:使用 NIO 复制文件
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class CopyBinaryFileWithNIO {
public static void main(String[] args) {
File sourceFile = new File("path/to/your/source_video.mp4");
File destFile = new File("path/to/your/dest_video_nio_copy.mp4");
try (
// 使用 RandomAccessFile 来获取 FileChannel
RandomAccessFile source = new RandomAccessFile(sourceFile, "r");
RandomAccessFile destination = new RandomAccessFile(destFile, "rw");
FileChannel sourceChannel = source.getChannel();
FileChannel destChannel = destination.getChannel()
) {
// 分配一个直接缓冲区
ByteBuffer buffer = ByteBuffer.allocateDirect(8192); // 8KB
// 从源通道读取数据到缓冲区
while (sourceChannel.read(buffer) != -1) {
// 切换缓冲区的模式,从写模式切换到读模式
buffer.flip();
// 将缓冲区中的数据写入目标通道
destChannel.write(buffer);
// 清空缓冲区,为下一次读取做准备
buffer.clear();
}
System.out.println("NIO 文件复制成功!");
} catch (IOException e) {
System.err.println("NIO 复制文件时发生错误: " + e.getMessage());
e.printStackTrace();
}
}
}
NIO 的优势:
- 性能更高:特别是在大文件传输时,得益于“零拷贝”和更高效的 I/O 模型。
- 更灵活:
ByteBuffer可以以字节、字符、整数等多种方式查看其内容,功能更强大。 - 支持非阻塞 I/O:这是 NIO 的另一个核心特性,但对于简单的文件复制,阻塞模式就足够了。
总结与选择
| 方法 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
FileInputStream/OutputStream |
简单、小文件的读写。 | 代码简单直观,易于理解。 | 大文件性能差,频繁 I/O。 |
BufferedInputStream/OutputStream |
推荐用于大多数文件操作,特别是大文件。 | 通过缓冲显著提升 I/O 性能。 | 代码比基础流稍复杂一点点。 |
NIO (FileChannel) |
高性能需求,如超大文件复制、服务器端文件处理。 | 性能最优,功能强大,支持非阻塞 I/O。 | 代码更复杂,概念(如 ByteBuffer.flip())需要学习。 |
给初学者的建议:
- 如果只是读取一个配置文件(即使是二进制格式),且文件不大,用
FileInputStream就足够了。 - 对于任何需要进行实际文件 I/O 操作的场景,特别是复制、移动文件,强烈推荐使用
BufferedInputStream和BufferedOutputStream。 这是性能和易用性之间最好的平衡点。 - 如果你在处理 GB 级别的大文件,或者在做性能要求极高的服务器开发,那么应该学习和使用 NIO。
请务必记住使用 try-with-resources 语句,它可以自动关闭实现了 AutoCloseable 接口的资源(如所有 I/O 流),避免资源泄漏,这是现代 Java 编程的最佳实践。
