RandomAccessFile隨機訪問文件
RandomAccessFile是Object的子類。一般IO流都是成對出現的,但是RandomAccessFile和打印流、序列流一樣都是單獨存在的。
特點:
1. 和打印流序列流不同的是RandomAccessFile既可以讀也可以寫。
2. RandomAccessFile的內部其實是一個byte類型的數組,數組可以延長,該數組存在一個指向數組的索引,稱之爲文件指針。
3. 可以通過getFilePointer方法獲取指針的位置,通過seek方法設置指針的位置。
4. 它可以對數組進行讀和寫,數組是byte類型的,所以RandomAccessFile類其實是對字節輸出流和字節輸入流進行了封裝
5. RandomAccessFile對象的源和目的只能是文件。
寫
RandomAccessFile raf = new RandomAccessFile("E:\\raf.txt","rw");
raf.write("張三".getBytes());
raf.writeInt(97);
raf.write("李四".getBytes());
raf.write(98);
raf.close();
輸出結果:
張三 a李四b
這裏是a和b是因爲記事本把寫進去的97和98解析了,不管怎麼解析,最底層的97和98是不變的,所以這裏沒有問題。而如果我們修改代碼
raf.write(609);
輸出結果卻也是a。因爲int類型是4個字節,它只寫最低位的字節。如果我們想寫原字節,就要用writeInt方法, 所以a前面有3個空格。
並且如果使用RandomAccessFile創建文件,文件不存在,則創建,文件存在,則不創建。這和一般的輸出流不一樣,其他的輸出流都是不管文件存在不存在都會創建。如果文件存在,繼續寫入數據,就會把原數據覆蓋。
讀
RandomAccessFile raf = new RandomAccessFile("E:\\raf.txt","rw");
byte[] buf = new byte[4];
raf.read(buf);
String name = new String(buf);
int age = raf.readInt();
System.out.println("name="+name);
System.out.println("age="+age);
raf.close();
輸出結果:
name=張三
age=97
在讀age的時候我們不能拿數組讀,讀完轉成String然後打印出來,因爲age應該是一個int類型的值,不能用String類型。所以需要一個專門讀int類型值的方法readInt(),讀完就是97了。
操作指針讀寫
設置指針:seek
獲取指針:getFilePointer
因爲內部其實是一個數組,所以指針位置從0開始。
RandomAccessFile raf = new RandomAccessFile("E:\\raf.txt","rw");
raf.seek(8);
raf.write("王五".getBytes());
raf.writeInt(99);
System.out.println("指針位置在"+raf.getFilePointer()) ;
raf.close();
輸出結果:
指針位置在16
//記事本中內容:張三 a王五c,覆蓋掉了李四,但是張三依然保留。
PipedInputStream和PipedOutputStream管道流
管道流的主要作用就是可以進行兩個線程之間的通訊,一個線程作爲管道輸出流,一個線程作爲管道輸入流。它不同於其他的輸入輸出流,管道輸出流只能連接到管道輸入流。不建議對這兩個對象使用單線程,因爲這兩個輸入輸出流是相互關聯的。如果恰好先執行的是輸入流,而輸出流還沒有往外寫數據,read方法是一個阻塞式方法,這時候就會死鎖。
public static void main(String[] args) throws IOException {
PipedInputStream input = new PipedInputStream();
PipedOutputStream output = new PipedOutputStream();
input.connect(output);
new Thread(new Input(input)).start();
new Thread(new Output(output)).start();
}
//管道輸入流線程
class Input implements Runnable{
private PipedInputStream in;
Input(PipedInputStream in){
this.in = in;
}
public void run(){
try {
byte[] buf = new byte[1024];
int len = in.read(buf);
String s = new String(buf,0,len);
System.out.println("s="+s);
in.close();
} catch (Exception e) {
// TODO: handle exception
}
}
}
//管道輸出流線程
class Output implements Runnable{
private PipedOutputStream out;
Output(PipedOutputStream out){
this.out = out;
}
public void run(){
try {
Thread.sleep(5000);
out.write("hi,管道來了!".getBytes());
} catch (Exception e) {
// TODO: handle exception
}
}
}
輸出結果:
hi,管道來了!
DateInputStream和DateOutputStream
DataInputStream和DataOutputStream都是Java中輸入輸出流的裝飾類。數據輸出流允許應用程序以適當方式將基本 Java 數據類型寫入輸出流中。然後,應用程序可以使用數據輸入流將數據讀入。
這兩個流對象也要對應着使用。
這兩個流對象中的writeInt、readInt等方法其實在RandomAccessFile類和ObjectInputStream、ObjectOutputStream類中也都存在,但是這些方法的使用場景不同,一個是專門用來隨機訪問文件的,一個是專門用來對對象序列化的。
如果只想操作Java的基本數據類型,就可以使用DateInputStream和DateOutputStream。
DataOutputStream dos = new DataOutputStream(new FileOutputStream("G:\\datastreamdemo.txt"));
dos.writeUTF("你好");
dos.close();
DataInputStream dis = new DataInputStream(new FileInputStream("G:\\datastreamdemo.txt"));
String str = dis.readUTF();
System.out.println(str);
dis.close();
輸出結果:
你好
ByteArrayInputStream和ByteArrayOutputStream
這兩個是操作字節數組的流對象,他們的源和目的都是內存,所以不需要調用系統的底層資源,不需要關流。
ByteArrayOutputStream:
此類實現了一個輸出流,其中的數據被寫入一個 byte 數組。緩衝區會隨着數據的不斷寫入而自動增長。可使用 toByteArray() 和 toString() 獲取數據。
ByteArrayInputStream :
包含一個內部緩衝區,該緩衝區包含從流中讀取的字節。內部計數器跟蹤 read 方法要提供的下一個字節。
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bos.write("abcd".getBytes());
byte[] buf = bos.toByteArray();
ByteArrayInputStream bis = new ByteArrayInputStream(buf);
int ch = 0;
while((ch = bis.read())!=-1){
System.out.println(ch);
}
輸出結果:
97
98
99
100