RandomAccessFile 是 Java I/O 體系中獨特的存在:它不屬於流體系,直接繼承 Object,實現 DataInput/DataOutput 接口,支持隨機定位讀寫。本文系統講解核心 API、典型應用場景、記憶體映射文件進階用法,並給出多線程斷點下載、文件插入數據的生產級代碼範例。
"r"(只讀)、"rw"(讀寫)seek(long pos) 移動文件指針、getFilePointer() 獲取當前位置、length() 文件長度、skipBytes(int n) 跳過字節readInt()、writeDouble()、readUTF() 等public class RAFBasicDemo {
public static void main(String[] args) throws IOException {
// 寫入基本類型數據
try (RandomAccessFile raf = new RandomAccessFile("data.dat", "rw")) {
for (int i = 0; i < 10; i++) raf.writeDouble(i * 1.414);
}
// 隨機修改第 6 個 double(索引 5,偏移 5*8=40 字節)
try (RandomAccessFile raf = new RandomAccessFile("data.dat", "rw")) {
raf.seek(40);
raf.writeDouble(47.0001);
}
// 讀取驗證
try (RandomAccessFile raf = new RandomAccessFile("data.dat", "r")) {
for (int i = 0; i < 10; i++) {
System.out.println("Value " + i + ": " + raf.readDouble());
}
}
}
}
通過 FileChannel.map() 將文件區域直接映射到虛擬記憶體,MappedByteBuffer 繼承 ByteBuffer,操作即記憶體操作,OS 負責頁面調度。
public class LargeMappedFileDemo {
static final int SIZE = 0x8000000; // 128 MB
public static void main(String[] args) throws Exception {
try (FileChannel fc = new RandomAccessFile("big.dat", "rw").getChannel()) {
MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_WRITE, 0, SIZE);
// 寫入
for (int i = 0; i < SIZE; i++) buffer.put((byte) 'x');
System.out.println("寫入完成");
// 讀取中間 6 字節
for (int i = SIZE/2; i < SIZE/2 + 6; i++)
System.out.print((char) buffer.get(i));
}
}
}
優勢:無需一次性載入整個文件,虛擬記憶體按需分頁加載,支持 TB 級文件(64 位 JVM)。
public static void insertAt(long pos, String content, String filePath) throws IOException {
try (RandomAccessFile raf = new RandomAccessFile(filePath, "rw")) {
if (pos < 0 || pos > raf.length()) throw new IllegalArgumentException("位置非法");
byte[] data = content.getBytes(StandardCharsets.UTF_8);
long newLength = raf.length() + data.length;
raf.setLength(newLength); // 擴展文件
// 從後向前移動數據,騰出空間
for (long i = newLength - 1; i >= pos + data.length; i--) {
raf.seek(i - data.length);
byte b = raf.readByte();
raf.seek(i);
raf.writeByte(b);
}
// 寫入新數據
raf.seek(pos);
raf.write(data);
}
}
public class MultiThreadDownloader {
public static void download(String url, File target, int threadCount) throws Exception {
// 1. 預分配文件空間
try (RandomAccessFile raf = new RandomAccessFile(target, "rw")) {
raf.setLength(getContentLength(url));
}
long partSize = target.length() / threadCount;
CountDownLatch latch = new CountDownLatch(threadCount);
for (int i = 0; i < threadCount; i++) {
long start = i * partSize;
long end = (i == threadCount - 1) ? target.length() : (i + 1) * partSize - 1;
new Thread(() -> {
try (RandomAccessFile raf = new RandomAccessFile(target, "rw");
InputStream in = new URL(url).openStream()) {
in.skip(start);
raf.seek(start);
byte[] buf = new byte[8192];
int len;
long downloaded = 0;
while (downloaded < end - start + 1 && (len = in.read(buf)) != -1) {
int toWrite = (int) Math.min(len, end - start + 1 - downloaded);
raf.write(buf, 0, toWrite);
downloaded += toWrite;
}
} catch (IOException e) {
e.printStackTrace();
} finally {
latch.countDown();
}
}).start();
}
latch.await(); // 等待所有線程完成
System.out.println("下載完成:" + target.getAbsolutePath());
}
private static long getContentLength(String urlStr) throws IOException {
HttpURLConnection conn = (HttpURLConnection) new URL(urlStr).openConnection();
conn.setRequestMethod("HEAD");
return conn.getContentLengthLong();
}
}
StandardCharsets.UTF_8,避免平台預設編碼不一致RandomAccessFile、FileChannel 及時關閉buffer.force() 或 raf.getFD().sync()RandomAccessFile 賦予 Java 類 C 語言 fseek 的隨機訪問能力,配合 NIO 記憶體映射,是構建高性能文件處理、下載工具、嵌入式存儲引擎的基石。掌握 seek、map、setLength 核心操作,能解決 90% 的非順序文件訪問需求。