簡介
小師妹又對F師兄提了一大堆奇奇怪怪的需求,要格式化輸出,要特定的編碼輸出,要自己定位輸出,什么?還要閱后即焚?大家看F師兄怎么一一接招吧。
字符輸出和字節輸出
小師妹:F師兄,上次你的IO講到了一半,文件讀取是基本上講完了,但是文件的寫入還沒有講,什么時候給小師妹我再科普科普?
小師妹:F師兄,你知道我這個人一直以來都是勤奮好學的典范,是老師們眼中的好學生,同學們心中的好榜樣,父母身邊乖巧的好孩子。在我永攀科學高峰的時候,居然發現還有一半的知識沒有獲取,真是讓我扼腕嘆息,F師兄,快快把知識傳給我吧。
小師妹你的請求,師兄我自當盡力辦到,但是我怎么記得上次講IO文件讀取已經過了好幾天了,怎么今天你才來找我。
小師妹紅着臉:F師兄,這不是使用的時候遇到了點問題,才想找你把知識再復習一遍。
更多精彩內容且看:
- 區塊鏈從入門到放棄系列教程-涵蓋密碼學,超級賬本,以太坊,Libra,比特幣等持續更新
- Spring Boot 2.X系列教程:七天從無到有掌握Spring Boot-持續更新
- Spring 5.X系列教程:滿足你對Spring5的一切想象-持續更新
- java程序員從小工到專家成神之路(2020版)-持續更新中,附詳細文章教程
那先把輸出類的結構再過一遍:
上面就是輸出的兩大系統了:Writer和OutputStream。
Writer主要針對於字符,而Stream主要針對Bytes。
Writer中最最常用的就是FileWriter和BufferedWriter,我們看下一個最基本寫入的例子:
public void useBufferedWriter() throws IOException {
String content = "www.flydean.com";
File file = new File("src/main/resources/www.flydean.com");
FileWriter fw = new FileWriter(file);
try(BufferedWriter bw = new BufferedWriter(fw)){
bw.write(content);
}
}
BufferedWriter是對FileWriter的封裝,它提供了一定的buffer機制,可以提高寫入的效率。
其實BufferedWriter提供了三種寫入的方式:
public void write(int c)
public void write(char cbuf[], int off, int len)
public void write(String s, int off, int len)
第一個方法傳入一個int,第二個方法傳入字符數組和開始讀取的位置和長度,第三個方法傳入字符串和開始讀取的位置和長度。是不是很簡單,完全可以理解?
小師妹:不對呀,F師兄,后面兩個方法的參數,不管是char和String都是字符我可以理解,第一個方法傳入int是什么鬼?
小師妹,之前跟你講的道理是不是都忘記的差不多了,int的底層存儲是bytes,char和String的底層存儲也是bytes,我們把int和char做個強制轉換就行了。我們看下是怎么轉換的:
public void write(int c) throws IOException {
synchronized (lock) {
ensureOpen();
if (nextChar >= nChars)
flushBuffer();
cb[nextChar++] = (char) c;
}
}
還記得int需要占用多少個字節嗎?4個,char需要占用2個字節。這樣強制從int轉換到char會有精度丟失的問題,只會保留低位的2個字節的數據,高位的兩個字節的數據會被丟棄,這個需要在使用中注意。
看完Writer,我們再來看看Stream:
public void useFileOutputStream() throws IOException {
String str = "www.flydean.com";
try(FileOutputStream outputStream = new FileOutputStream("src/main/resources/www.flydean.com");
BufferedOutputStream bufferedOutputStream= new BufferedOutputStream(outputStream)){
byte[] strToBytes = str.getBytes();
bufferedOutputStream.write(strToBytes);
}
}
跟Writer一樣,BufferedOutputStream也是對FileOutputStream的封裝,我們看下BufferedOutputStream中提供的write方法:
public synchronized void write(int b)
public synchronized void write(byte b[], int off, int len)
比較一下和Writer的區別,BufferedOutputStream的方法是synchronized的,並且BufferedOutputStream是直接對byte進行操作的。
第一個write方法傳入int參數也是需要進行截取的,不過這次是從int轉換成byte。
格式化輸出
小師妹:F師兄,我們經常用的System.out.println可以直接向標准輸出中輸出格式化過后的字符串,文件的寫入是不是也有類似的功能呢?
肯定有,PrintWriter就是做格式化輸出用的:
public void usePrintWriter() throws IOException {
FileWriter fileWriter = new FileWriter("src/main/resources/www.flydean.com");
try(PrintWriter printWriter = new PrintWriter(fileWriter)){
printWriter.print("www.flydean.com");
printWriter.printf("程序那些事 %s ", "非常棒");
}
}
輸出其他對象
小師妹:F師兄,我們看到可以輸出String,char還有Byte,那可不可以輸出Integer,Long等基礎類型呢?
可以的,使用DataOutputStream就可以做到:
public void useDataOutPutStream()
throws IOException {
String value = "www.flydean.com";
try(FileOutputStream fos = new FileOutputStream("src/main/resources/www.flydean.com")){
DataOutputStream outStream = new DataOutputStream(new BufferedOutputStream(fos));
outStream.writeUTF(value);
}
}
DataOutputStream提供了writeLong,writeDouble,writeFloat等等方法,還可以writeUTF!
在特定的位置寫入
小師妹:F師兄,有時候我們不需要每次都從頭開始寫入到文件,能不能自定義在什么位置寫入呢?
使用RandomAccessFile就可以了:
public void useRandomAccess() throws IOException {
try(RandomAccessFile writer = new RandomAccessFile("src/main/resources/www.flydean.com", "rw")){
writer.seek(100);
writer.writeInt(50);
}
}
RandomAccessFile可以通過seek來定位,然后通過write方法從指定的位置寫入。
給文件加鎖
小師妹:F師兄,最后還有一個問題,怎么保證我在進行文件寫的時候別人不會覆蓋我寫的內容,不會產生沖突呢?
FileChannel可以調用tryLock方法來獲得一個FileLock鎖,通過這個鎖,我們可以控制文件的訪問。
public void useFileLock()
throws IOException {
try(RandomAccessFile stream = new RandomAccessFile("src/main/resources/www.flydean.com", "rw");
FileChannel channel = stream.getChannel()){
FileLock lock = null;
try {
lock = channel.tryLock();
} catch (final OverlappingFileLockException e) {
stream.close();
channel.close();
}
stream.writeChars("www.flydean.com");
lock.release();
}
}
總結
今天給小師妹講了好多種文件的寫的方法,夠她學習一陣子了。
本文的例子https://github.com/ddean2009/learn-java-io-nio
本文作者:flydean程序那些事
本文鏈接:http://www.flydean.com/io-file-writer/
本文來源:flydean的博客
歡迎關注我的公眾號:程序那些事,更多精彩等着您!