RandomAccessFile、FileInputStream、MappedByteBuffer、FileChannel 区别及应用场景

RandomAccessFile、FileInputStream、MappedByteBuffer、FileChannel 比较

这些类都是Java中用于文件I/O操作的类,但各有特点和适用场景。下面我将详细介绍它们的区别、使用场景以及相关类。

主要区别

类/接口 特点 线程安全 性能 功能丰富度
RandomAccessFile 可随机读写,功能全面但API较老
FileInputStream 只能顺序读取,简单易用
MappedByteBuffer 内存映射文件,高性能随机访问
FileChannel NIO的核心通道,支持多种操作(传输、锁定、内存映射等),功能强大且灵活

详细说明及使用场景

1. RandomAccessFile

特点

  • 可读可写,支持随机访问(通过seek()方法)
  • 支持基本数据类型(如readInt(), writeDouble()等)
  • 基于文件指针操作

使用场景

  • 需要同时读写文件的场景
  • 需要随机访问文件的场景(如数据库实现)
  • 需要操作基本数据类型的场景

示例
https://www.cnblogs.com/vipsoft/p/16252698.html

断点续传(上传)Java版: https://www.cnblogs.com/vipsoft/p/15951660.html

java 复制代码
RandomAccessFile raf = new RandomAccessFile("file.txt", "rw");
raf.seek(100);  // 移动到第100字节
raf.writeInt(123);
raf.close();

2. FileInputStream

特点

  • 只能顺序读取,不能写入
  • 简单的字节流读取
  • 通常与BufferedInputStream配合使用提高性能

使用场景

  • 只需顺序读取文件的简单场景
  • 读取小文件
  • 与其他装饰器流配合使用(如BufferedInputStream)

示例

java 复制代码
FileInputStream fis = new FileInputStream("file.txt");
byte[] buffer = new byte[1024];
int bytesRead = fis.read(buffer);
fis.close();

3. MappedByteBuffer

特点

  • 通过内存映射文件提供高性能访问
  • 直接操作内存,避免了用户空间和内核空间的数据拷贝
  • 适合大文件操作
  • 非线程安全

使用场景

  • 需要高性能随机访问大文件
  • 内存数据库实现
  • 文件共享场景

示例
https://www.cnblogs.com/vipsoft/p/16533152.html
https://www.cnblogs.com/vipsoft/p/16458161.html

java 复制代码
RandomAccessFile file = new RandomAccessFile("largefile.dat", "rw");
FileChannel channel = file.getChannel();
MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, channel.size());

// 直接操作buffer
buffer.put(0, (byte)123);
buffer.force();  // 强制刷新到磁盘

4. FileChannel

特点

  • NIO的核心文件操作类
  • 支持多种高级功能:文件锁定、内存映射、分散/聚集I/O、文件间传输等
  • 通常比传统I/O性能更好
  • 线程安全

使用场景

  • 需要高性能文件操作的场景
  • 需要文件锁定的场景
  • 大文件传输(transferTo/transferFrom)
  • 与Selector配合实现非阻塞I/O(虽然文件通道不能完全非阻塞)

示例

java 复制代码
RandomAccessFile file = new RandomAccessFile("file.txt", "rw");
FileChannel channel = file.getChannel();

// 文件锁定
FileLock lock = channel.lock();

// 文件传输
FileChannel destChannel = new FileOutputStream("dest.txt").getChannel();
channel.transferTo(0, channel.size(), destChannel);

channel.close();

相关联的重要类

  1. BufferedInputStream/BufferedOutputStream:提供缓冲功能,提高I/O性能
  2. BufferedReader/BufferedWriter:提供字符缓冲功能,支持按行读写
  3. FileOutputStream:对应FileInputStream的写入类
  4. ByteBuffer :NIO中的缓冲区类,与Channel配合使用 https://www.cnblogs.com/vipsoft/p/16547142.html
  5. Selector:NIO多路复用器,用于非阻塞I/O(虽然文件通道不完全支持)
  6. Files (Java 7+ NIO.2):提供很多实用静态方法操作文件
  7. Path/Paths (Java 7+ NIO.2):现代文件路径操作类
  8. AsynchronousFileChannel (Java 7+):异步文件通道
  9. FileLock :文件锁定功能: https://www.cnblogs.com/vipsoft/p/16540562.html

性能比较

一般来说性能排序(从高到低):

  1. MappedByteBuffer (内存映射文件)
  2. FileChannel (特别是使用transferTo/transferFrom时)
  3. BufferedInputStream/BufferedOutputStream (有缓冲)
  4. RandomAccessFile
  5. 原始FileInputStream/FileOutputStream (无缓冲)

选择建议

  • 简单读取:FileInputStream + BufferedInputStream
  • 需要随机访问:RandomAccessFile 或 FileChannel + MappedByteBuffer
  • 高性能需求:FileChannel + MappedByteBuffer
  • 大文件传输:FileChannel的transferTo/transferFrom
  • 现代Java开发:优先考虑NIO.2 (Java 7+)的Files和Path API

Java 7引入的NIO.2 (java.nio.file包)提供了更现代的API,对于新项目推荐优先考虑使用这些新API。

在 Java 中,你可以使用 RandomAccessFileFileInputStream 来从指定偏移量(offset)读取指定长度的字节数据。以下是两种实现方式:


方法 1:使用 RandomAccessFile(推荐)

RandomAccessFile 可以直接跳转到文件的指定位置进行读取,适合随机访问文件。

java 复制代码
import java.io.IOException;
import java.io.RandomAccessFile;

public class ReadFileFromOffset {
    public static void main(String[] args) {
        String filePath = "your_file.bin"; // 替换为你的文件路径
        int offset = 10;                  // 起始偏移量
        int length = 2;                   // 要读取的字节数

        try (RandomAccessFile raf = new RandomAccessFile(filePath, "r")) {
            // 跳转到指定偏移量
            raf.seek(offset);

            // 读取指定长度的字节
            byte[] buffer = new byte[length];
            int bytesRead = raf.read(buffer);

            if (bytesRead != length) {
                System.err.println("未能读取足够字节,可能已到文件末尾");
            } else {
                System.out.println("读取的字节数据: " + bytesToHex(buffer));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // 辅助方法:将字节数组转为十六进制字符串(方便查看)
    private static String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02X ", b));
        }
        return sb.toString();
    }
}

方法 2:使用 FileInputStream

FileInputStream 也可以读取指定偏移量的数据,但需要手动跳过前面的字节。

java 复制代码
import java.io.FileInputStream;
import java.io.IOException;

public class ReadFileFromOffsetWithStream {
    public static void main(String[] args) {
        String filePath = "your_file.bin"; // 替换为你的文件路径
        int offset = 10;                  // 起始偏移量
        int length = 2;                   // 要读取的字节数

        try (FileInputStream fis = new FileInputStream(filePath)) {
            // 跳过 offset 之前的字节
            long skipped = fis.skip(offset);
            if (skipped != offset) {
                System.err.println("无法跳过足够字节,可能文件太小");
                return;
            }

            // 读取指定长度的字节
            byte[] buffer = new byte[length];
            int bytesRead = fis.read(buffer);

            if (bytesRead != length) {
                System.err.println("未能读取足够字节,可能已到文件末尾");
            } else {
                System.out.println("读取的字节数据: " + bytesToHex(buffer));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // 辅助方法:将字节数组转为十六进制字符串
    private static String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02X ", b));
        }
        return sb.toString();
    }
}

关键点说明

  1. RandomAccessFile vs FileInputStream

    • RandomAccessFile 更适合随机访问(如频繁跳转偏移量)。
    • FileInputStream 适用于顺序读取,但也可以手动 skip() 到指定位置。
  2. seek(offset)skip(offset)

    • RandomAccessFile.seek(offset) 直接定位到 offset
    • FileInputStream.skip(offset) 跳过 offset 字节,但可能受流限制(如网络流可能无法跳过)。
  3. 字节读取

    • read(byte[]) 返回实际读取的字节数,可能小于请求的长度(如文件末尾)。
  4. 异常处理

    • 需处理 IOException(如文件不存在或权限问题)。
  5. 字节转十六进制

    • 示例代码提供了 bytesToHex() 方法方便查看字节数据(如 1A 2B)。

输出示例

如果文件 your_file.binoffset=10 处的 2 个字节是 0x410x42(即 ASCII 字符 AB),输出:

复制代码
读取的字节数据: 41 42

适用场景

  • 读取二进制文件(如 .bin.dat)。
  • 解析文件头、特定偏移量的数据(如文件格式解析)。
  • 网络协议解析(如从固定位置读取字段)。

如果需要更复杂的文件操作(如内存映射),可以考虑 java.nioFileChannelMappedByteBuffer

相关推荐
VipSoft6 天前
原码、反码、补码
文件处理
唐叔在学习18 天前
【Python入门】文件读取全攻略:5种常用格式(csv/excel/word/ppt/pdf)一键搞定 | 附完整代码示例
python·数据分析·办公自动化·文件处理
背太阳的牧羊人1 个月前
使用 PyMuPDF(fitz)库打开 PDF 文件,并且是从内存中的字节流(BytesIO)读取 PDF 内容
数据库·pdf·文件处理·pymupdf·fitz
算法小白(真小白)1 年前
信号处理之(文件批处理+小波分解+波形图的生成)
python·学习·机器学习·信号处理·文件处理·小波分解
程序员安然1 年前
【昕宝爸爸小模块】什么是POI,为什么它会导致内存溢出?
java·开发语言·文件处理
avi91112 年前
分享一个Python 写的监控日志log txt文档 的代码
开发语言·python·文件处理·时间戳
全栈若城2 年前
【linux命令讲解大全】001. bzcat和cat命令的使用指南及示例
linux·文件处理·linux命令·压缩解压缩·命令行工具·文本操作
java李杨勇2 年前
Java获取指定文件夹下目录下所有视频并复制到另一个地方
java·文件处理
他们叫我技术总监2 年前
kettle开发-Day40-AI分流之case/switch
大数据·数据库·kettle·文件处理