杰瑞科技汇

Java二进制文件如何正确打开?

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

Java二进制文件如何正确打开?-图1
(图片来源网络,侵删)

Java 提供了 InputStreamOutputStream 两个抽象基类以及它们的具体实现类来处理二进制数据,现代 Java 开发更推荐使用 NIO (New I/O) 中的 FileChannelByteBuffer,它们在性能上通常更优。

下面我将详细介绍几种打开和操作二进制文件的方法,从基础到现代实践。


使用 InputStreamOutputStream (经典方式)

这是最传统、最基础的方法,适用于所有 Java 版本。

读取二进制文件 (FileInputStream)

使用 FileInputStream 将文件中的字节读入一个字节数组。

Java二进制文件如何正确打开?-图2
(图片来源网络,侵删)

示例代码:读取一个图片文件并打印其字节大小

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();
        }
    }
}

使用 BufferedInputStreamBufferedOutputStream (优化性能)

当处理大文件时,直接使用 FileInputStream 每次可能只读取一个字节,这会导致大量的磁盘 I/O 操作,效率很低,使用缓冲流可以解决这个问题。

Java二进制文件如何正确打开?-图3
(图片来源网络,侵删)

缓冲流会在内存中创建一个缓冲区(默认通常是 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();
        }
    }
}

关键点:

  • BufferedInputStreamBufferedOutputStream 是对字节流的包装,它们本身不直接与文件交互。
  • 必须提供 InputStreamOutputStream 的构造参数。
  • 缓冲区大小(如 8192)可以根据实际情况调整,8KB 是一个不错的默认值。

使用 NIO (FileChannelByteBuffer) (现代高性能方式)

从 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())需要学习。

给初学者的建议:

  1. 如果只是读取一个配置文件(即使是二进制格式),且文件不大,用 FileInputStream 就足够了。
  2. 对于任何需要进行实际文件 I/O 操作的场景,特别是复制、移动文件,强烈推荐使用 BufferedInputStreamBufferedOutputStream 这是性能和易用性之间最好的平衡点。
  3. 如果你在处理 GB 级别的大文件,或者在做性能要求极高的服务器开发,那么应该学习和使用 NIO。

请务必记住使用 try-with-resources 语句,它可以自动关闭实现了 AutoCloseable 接口的资源(如所有 I/O 流),避免资源泄漏,这是现代 Java 编程的最佳实践。

分享:
扫描分享到社交APP
上一篇
下一篇