Java 面试必问:文件读取如何保证读完?未写完就被读怎么解决?

问题背景

  • 在 Java 文件操作的面试中,经常会问到两个核心问题:
  • 代码层面,如何判断一个文件已经完全读完?

业务层面,如何避免文件还在写入就被读取,读到不完整内容?

本文从基础判断到生产可用方案,一次性总结清楚

一、Java 中如何判断文件已读完?

不同 IO 方式,判断结束的标志不同,记住这一套即可:

1. 字节流 InputStream
read() 或 read(byte[]) 返回 -1 表示到达文件末尾。
int len;
byte[] buffer = new byte[1024];
while ((len = inputStream.read(buffer)) != -1) {
    // 处理数据
}

---------------------------
1.字节流的 read() 方法返回 -1 时,表示文件读取到末尾(读完)。
import java.io.FileInputStream;
import java.io.IOException;

public class ReadFileEndCheck {
    public static void main(String[] args) {
        try (FileInputStream fis = new FileInputStream("test.txt")) {
            int byteData;
            // 核心:read()返回-1时,说明文件读完
            while ((byteData = fis.read()) != -1) {
                // 处理读取到的字节(比如转字符)
                System.out.print((char) byteData);
            }
            System.out.println("\n文件已完全读取");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
---------------------------
2.批量读取(更高效):read(byte[] buffer) 返回读取到的字节数,返回 -1 时表示读完:
try (FileInputStream fis = new FileInputStream("test.txt")) {
    byte[] buffer = new byte[1024];
    int len;
    // len为-1时,文件读完
    while ((len = fis.read(buffer)) != -1) {
        // 处理buffer中前len个字节
        System.out.print(new String(buffer, 0, len));
    }
} catch (IOException e) {
    e.printStackTrace();
}
2. 字符流 BufferedReader
readLine() 返回 null 表示读完。
String line;
while ((line = br.readLine()) != null) {
    // 处理行数据
}
--------------------
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class BufferedReaderEndCheck {
    public static void main(String[] args) {
        try (BufferedReader br = new BufferedReader(new FileReader("test.txt"))) {
            String line;
            // 核心:readLine()返回null时,文件读完
            while ((line = br.readLine()) != null) {
                System.out.println(line);
            }
            System.out.println("文件已完全读取");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
3. NIO FileChannel
channel.read(ByteBuffer) 返回 -1 表示读完。
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class NIOFileRead {
    public static void main(String[] args) {
        try (RandomAccessFile raf = new RandomAccessFile("test.txt", "r");
             FileChannel channel = raf.getChannel()) {
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            int bytesRead;
            // 返回-1时读完
            while ((bytesRead = channel.read(buffer)) != -1) {
                buffer.flip(); // 切换为读模式
                while (buffer.hasRemaining()) {
                    System.out.print((char) buffer.get());
                }
                buffer.clear(); // 清空缓冲区
            }
            System.out.println("\n文件已完全读取");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

二、核心问题:文件还没写完,就被读取了怎么办?

这类问题本质是:写读竞争 → 读到半拉文件。
常见场景:大文件上传、日志采集、跨进程文件传输。
下面是 4 种标准解决方案,按生产推荐度排序。

三、方案 1:临时文件 + 重命名(最推荐、最稳定)

思路
- 写入方先写 .tmp / .temp 临时文件
- 完全写完后,原子重命名为正式文件名
- 读取方只处理正式文件

四、方案 2:约定结束标记(简单通用)

思路
- 读写双方约定一个结束标记,例如:###EOF###
  读取方只有读到该标记,才认为文件完整。
优点
  实现简单
  跨语言、跨平台
缺点
  写入进程异常退出时,可能不会写结束符。

五、方案 3:判断文件大小 / 修改时间稳定

思路
  记录当前文件大小
  等待 500ms ~ 1s
  再次获取大小,不变则认为写入完成
long size1 = file.length();
Thread.sleep(1000);
long size2 = file.length();
if (size1 == size2) {
    // 认为写入完成
}
优点
  无需改造写入端
  适合第三方文件采集
缺点
  写入暂停时可能误判

六、方案 4:NIO 文件锁 FileLock

思路
  写入时加独占锁
  读取时尝试加共享锁,加锁成功表示可读取
FileLock lock = channel.tryLock(0, Long.MAX_VALUE, true);
if (lock != null) {
    // 文件未被占用,可以安全读取
    lock.release();
}
优点
  JVM 标准机制
  适合多进程、多线程并发
缺点
  不同操作系统行为略有差异

七、面试标准回答(可直接背诵)

1. 判断文件读完:
  字节流返回 -1,字符流 BufferedReader readLine 返回 null。
2. 防止未写完就读:
  本质解决写读竞争问题,常用四种方案:
    优先使用:临时文件 + 重命名,最稳定、生产最常用
    简单方案:约定结束标记
    无侵入方案:判断文件大小稳定
    标准并发方案:NIO FileLock 文件锁
posted @ 2026-02-24 17:20  zzusjw  阅读(12)  评论(0)    收藏  举报