本篇文章主要介紹RandomAccessFile,該類是IO流體系中功能最豐富的文件內(nèi)容訪問類,既可以讀取文件內(nèi)容,也可以向文件輸出數(shù)據(jù)。
一、RandomAccessFile簡介
RandomAccessFile既可以讀取文件內(nèi)容,也可以向文件輸出數(shù)據(jù)。同時(shí),RandomAccessFile支持“隨機(jī)訪問”的方式,程序快可以直接跳轉(zhuǎn)到文件的任意地方來讀寫數(shù)據(jù)。
由于RandomAccessFile可以自由訪問文件的任意位置,所以如果需要訪問文件的部分內(nèi)容,而不是把文件從頭讀到尾,使用RandomAccessFile將是更好的選擇。
與OutputStream、Writer等輸出流不同的是,RandomAccessFile允許自由定義文件記錄指針,RandomAccessFile可以不從開始的地方開始輸出,因此RandomAccessFile可以向已存在的文件后追加內(nèi)容。如果程序需要向已存在的文件后追加內(nèi)容,則應(yīng)該使用RandomAccessFile。
RandomAccessFile的方法雖然多,但它有一個最大的局限,就是只能讀寫文件,不能讀寫其他IO節(jié)點(diǎn)。
RandomAccessFile的一個重要使用場景就是網(wǎng)絡(luò)請求中的多線程下載及斷點(diǎn)續(xù)傳。
二、RandomAccessFile中的方法
1.RandomAccessFile的構(gòu)造函數(shù)
RandomAccessFile類有兩個構(gòu)造函數(shù),其實(shí)這兩個構(gòu)造函數(shù)基本相同,只不過是指定文件的形式不同——一個需要使用String參數(shù)來指定文件名,一個使用File參數(shù)來指定文件本身。除此之外,創(chuàng)建RandomAccessFile對象時(shí)還需要指定一個mode參數(shù),該參數(shù)指定RandomAccessFile的訪問模式,一共有4種模式。
**"r" : ** 以只讀方式打開。調(diào)用結(jié)果對象的任何 write 方法都將導(dǎo)致拋出 IOException。
"rw": 打開以便讀取和寫入。
"rws": 打開以便讀取和寫入。相對于 "rw","rws" 還要求對“文件的內(nèi)容”或“元數(shù)據(jù)”的每個更新都同步寫入到基礎(chǔ)存儲設(shè)備。
"rwd" : 打開以便讀取和寫入,相對于 "rw","rwd" 還要求對“文件的內(nèi)容”的每個更新都同步寫入到基礎(chǔ)存儲設(shè)備。
2.RandomAccessFile的重要方法
RandomAccessFile既可以讀文件,也可以寫文件,所以類似于InputStream的read()方法,以及類似于OutputStream的write()方法,RandomAccessFile都具備。除此之外,RandomAccessFile具備兩個特有的方法,來支持其隨機(jī)訪問的特性。
RandomAccessFile對象包含了一個記錄指針,用以標(biāo)識當(dāng)前讀寫處的位置,當(dāng)程序新創(chuàng)建一個RandomAccessFile對象時(shí),該對象的文件指針記錄位于文件頭(也就是0處),當(dāng)讀/寫了n個字節(jié)后,文件記錄指針將會后移n個字節(jié)。除此之外,RandomAccessFile還可以自由移動該記錄指針。下面就是RandomAccessFile具有的兩個特殊方法,來操作記錄指針,實(shí)現(xiàn)隨機(jī)訪問:
long getFilePointer( ):返回文件記錄指針的當(dāng)前位置
void seek(long pos ):將文件指針定位到pos位置
三、RandomAccessFile的使用
利用RandomAccessFile實(shí)現(xiàn)文件的多線程下載,即多線程下載一個文件時(shí),將文件分成幾塊,每塊用不同的線程進(jìn)行下載。下面是一個利用多線程在寫文件時(shí)的例子,其中預(yù)先分配文件所需要的空間,然后在所分配的空間中進(jìn)行分塊,然后寫入:
/**
* 測試?yán)枚嗑€程進(jìn)行文件的寫操作
*/
public class Test {
public static void main(String[] args) throws Exception {
// 預(yù)分配文件所占的磁盤空間,磁盤中會創(chuàng)建一個指定大小的文件
RandomAccessFile raf = new RandomAccessFile("D://abc.txt", "rw");
raf.setLength(1024*1024); // 預(yù)分配 1M 的文件空間
raf.close();
// 所要寫入的文件內(nèi)容
String s1 = "第一個字符串";
String s2 = "第二個字符串";
String s3 = "第三個字符串";
String s4 = "第四個字符串";
String s5 = "第五個字符串";
// 利用多線程同時(shí)寫入一個文件
new FileWriteThread(1024*1,s1.getBytes()).start(); // 從文件的1024字節(jié)之后開始寫入數(shù)據(jù)
new FileWriteThread(1024*2,s2.getBytes()).start(); // 從文件的2048字節(jié)之后開始寫入數(shù)據(jù)
new FileWriteThread(1024*3,s3.getBytes()).start(); // 從文件的3072字節(jié)之后開始寫入數(shù)據(jù)
new FileWriteThread(1024*4,s4.getBytes()).start(); // 從文件的4096字節(jié)之后開始寫入數(shù)據(jù)
new FileWriteThread(1024*5,s5.getBytes()).start(); // 從文件的5120字節(jié)之后開始寫入數(shù)據(jù)
}
// 利用線程在文件的指定位置寫入指定數(shù)據(jù)
static class FileWriteThread extends Thread{
private int skip;
private byte[] content;
public FileWriteThread(int skip,byte[] content){
this.skip = skip;
this.content = content;
}
public void run(){
RandomAccessFile raf = null;
try {
raf = new RandomAccessFile("D://abc.txt", "rw");
raf.seek(skip);
raf.write(content);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
raf.close();
} catch (Exception e) {
}
}
}
}
}
當(dāng)RandomAccessFile向指定文件中插入內(nèi)容時(shí),將會覆蓋掉原有內(nèi)容。如果不想覆蓋掉,則需要將原有內(nèi)容先讀取出來,然后先把插入內(nèi)容插入后再把原有內(nèi)容追加到插入內(nèi)容后。