Java BufferedOutputStream類的常用方法講解
BufferedOutputStream類的常用方法
BufferedOutputStream字節(jié)緩沖輸出流
構(gòu)造方式
第一種開發(fā)中
public BufferedOutputStream(OutputStream out)
采用的默認(rèn)的緩沖區(qū)大小(足夠大了) ,來(lái)構(gòu)造一個(gè)字節(jié)緩沖輸出流對(duì)象
public BufferedOutputStream(OutputStream out,int size)
指定size緩沖區(qū)大小構(gòu)造緩沖輸出流對(duì)象
IllegalArgumentException - 如果 size <= 0
常用方法
public void write(int b)throws IOException
一次寫一個(gè)字節(jié)
- b - 要寫入的字節(jié)。
public void write(byte[] b,int off,int len) throws IOException
一次寫一個(gè)字節(jié)數(shù)組的一部分
- b - 數(shù)據(jù)。
- off - 數(shù)據(jù)的起始偏移量。
- len - 要寫入的字節(jié)數(shù)。
public void flush() throws IOException
刷新此緩沖的輸出流。這迫使所有緩沖的輸出字節(jié)被寫出到底層輸出流中。
public void close() throws IOException
關(guān)閉此輸出流并釋放與此流有關(guān)的所有系統(tǒng)資源。
FilterOutputStream 的 close 方法先調(diào)用其 flush 方法,然后調(diào)用其基礎(chǔ)輸出流的 close 方法。
程序示例
public static void main(String[] args) throws Exception {
//符合Java一種設(shè)計(jì)模式:裝飾者設(shè)計(jì)模式(過(guò)濾器:Filter)
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("bos.txt")) ;
//寫數(shù)據(jù)
bos.write("hello".getBytes());
//釋放資源
bos.close();
}
BufferedOutputStream深入分析
FileOutputStream和BufferedOutputStream都提供了一系列的將數(shù)據(jù)寫入文件的方式,并且我們都知道BufferedOutputStream要比直接使用FileOutputStream寫入速度要快,本文通過(guò)案例實(shí)際演示一下兩者的區(qū)別。
代碼準(zhǔn)備
public class BufferFile {
public static void main(String[] args) {
//每次向文件中寫入一個(gè)8字節(jié)的數(shù)組
byte[] bytes = "1234567\n".getBytes();
//每隔100毫秒通過(guò)buffer的方式向文件中寫入數(shù)據(jù)
new Thread(() -> {
System.out.println("buffer_while start...");
File file = new File("/var/file_test_data/out_buffer_while.txt");
FileOutputStream fileOutputStream;
try {
fileOutputStream = new FileOutputStream(file);
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
while (true) {
Thread.sleep(100);
bufferedOutputStream.write(bytes);
}
} catch (Exception e) {
e.printStackTrace();
}
}).start();
//通過(guò)buffer的方式向文件中寫入1千萬(wàn)次
new Thread(() -> {
System.out.println("buffer_for start...");
File file = new File("/var/file_test_data/out_buffer_for.txt");
FileOutputStream fileOutputStream;
try {
fileOutputStream = new FileOutputStream(file);
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
for (int i = 0; i < 10000000; i++) {
bufferedOutputStream.write(bytes);
}
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(new Date() + ": buffer_for end...");
}).start();
//通過(guò)file的方式向文件中寫入1千萬(wàn)次
new Thread(() -> {
System.out.println("file_for start...");
File file = new File("/var/file_test_data/out_file_for.txt");
FileOutputStream fileOutputStream;
try {
fileOutputStream = new FileOutputStream(file);
for (int i = 0; i < 10000000; i++) {
fileOutputStream.write(bytes);
}
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(new Date() + ": file_for end...");
}).start();
}
}
開始運(yùn)行

強(qiáng)行停止后的運(yùn)行結(jié)果

1、file和buffe寫入速度比較
兩者分別寫入1千萬(wàn)次,時(shí)間上buffer比f(wàn)ile快8秒,如果當(dāng)寫入次數(shù)指數(shù)級(jí)增加時(shí),buffer的優(yōu)勢(shì)將更加明顯。
2、數(shù)據(jù)寫入完整性問(wèn)題
buffer雖然要比f(wàn)ile快,但是從最終數(shù)據(jù)上可以看出,buffer會(huì)丟數(shù)據(jù)
- 當(dāng)?shù)谝粋€(gè)線程寫入時(shí)數(shù)據(jù)還未滿8kb時(shí),強(qiáng)制停止java進(jìn)程,最終out_buffer_while.txt沒有數(shù)據(jù)。
- 第二個(gè)線程,雖然最終代碼執(zhí)行完畢,但是比較file方式,out_buffer_for.txt文件看起來(lái)也丟了一部分?jǐn)?shù)據(jù)。
原因分析
當(dāng)使用buffer讀寫文件時(shí),數(shù)據(jù)并沒有直接被寫入磁盤,而是被緩存到一個(gè)字節(jié)數(shù)據(jù)中,這個(gè)字節(jié)數(shù)組的大小是8kb,默認(rèn)情況下只有當(dāng)8kb被填充滿了以后,數(shù)據(jù)才會(huì)被一次性寫入磁盤,這樣一來(lái)就大大減少了系統(tǒng)調(diào)用的次數(shù)(file是每一次write都會(huì)產(chǎn)生系統(tǒng)調(diào)用),當(dāng)然也正是因?yàn)閎uffer中的每一次write只是寫入到內(nèi)存中(JVM自身內(nèi)存中),所以當(dāng)數(shù)據(jù)未寫入磁盤前,如果JVM進(jìn)程掛了,那么就會(huì)造成數(shù)據(jù)丟失。
手動(dòng)刷盤
為了解決數(shù)據(jù)丟失的問(wèn)題,buf中提供了flush()方法,用戶可以自行決定合適將數(shù)據(jù)刷寫到磁盤中
- 如果你的flush()調(diào)用的非常頻繁,那就會(huì)退化為普通的file模式了。
- 如果你的flush()調(diào)用的又不太頻繁,那么丟數(shù)據(jù)的可能性就比較高。
- 無(wú)論如何業(yè)務(wù)邏輯中數(shù)據(jù)寫完時(shí),一定要調(diào)用一次flush(),確保緩沖區(qū)的數(shù)據(jù)刷到磁盤上。
將無(wú)限循環(huán)寫入的代碼注釋掉,在buf寫1千萬(wàn)完成后,加上bufferedOutputStream.flush();
public class BufferFile {
public static void main(String[] args) {
//每次向文件中寫入一個(gè)8字節(jié)的數(shù)組
byte[] bytes = "1234567\n".getBytes();
//每隔100毫秒通過(guò)buffer的方式向文件中寫入數(shù)據(jù)
/*new Thread(() -> {
System.out.println("buffer_while start...");
File file = new File("/var/file_test_data/out_buffer_while.txt");
FileOutputStream fileOutputStream;
try {
fileOutputStream = new FileOutputStream(file);
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
while (true) {
Thread.sleep(100);
bufferedOutputStream.write(bytes);
}
} catch (Exception e) {
e.printStackTrace();
}
}).start();*/
//通過(guò)buffer的方式向文件中寫入1千萬(wàn)次
new Thread(() -> {
System.out.println("buffer_for start...");
File file = new File("/var/file_test_data/out_buffer_for.txt");
FileOutputStream fileOutputStream;
try {
fileOutputStream = new FileOutputStream(file);
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
for (int i = 0; i < 10000000; i++) {
bufferedOutputStream.write(bytes);
}
bufferedOutputStream.flush();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(new Date() + ": buffer_for end...");
}).start();
//通過(guò)file的方式向文件中寫入1千萬(wàn)次
new Thread(() -> {
System.out.println("file_for start...");
File file = new File("/var/file_test_data/out_file_for.txt");
FileOutputStream fileOutputStream;
try {
fileOutputStream = new FileOutputStream(file);
for (int i = 0; i < 10000000; i++) {
fileOutputStream.write(bytes);
}
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(new Date() + ": file_for end...");
}).start();
}
}
這次再看數(shù)據(jù)寫入完整了

buffer源碼分析
類的機(jī)構(gòu)圖

首先當(dāng)創(chuàng)建一個(gè)BufferedOutputStream對(duì)象時(shí),構(gòu)造方法就初始化了緩沖的字節(jié)數(shù)組大小為8kb
protected byte buf[];
public BufferedOutputStream(OutputStream out) {
this(out, 8192);
}
public BufferedOutputStream(OutputStream out, int size) {
super(out);
if (size <= 0) {
throw new IllegalArgumentException("Buffer size <= 0");
}
buf = new byte[size];
}
當(dāng)調(diào)用buffer.write(b)時(shí),調(diào)用的是父類FilterOutputStream的方法
public void write(byte b[]) throws IOException {
//寫入的字節(jié)數(shù)組b,從0開始,一共要寫入的長(zhǎng)度
write(b, 0, b.length);
}
public void write(byte b[], int off, int len) throws IOException {
if ((off | len | (b.length - (len + off)) | (off + len)) < 0)
throw new IndexOutOfBoundsException();
//遍歷數(shù)組,一個(gè)字節(jié)一個(gè)字節(jié)的把數(shù)據(jù)寫入數(shù)組中
for (int i = 0 ; i < len ; i++) {
write(b[off + i]);
}
}
public synchronized void write(int b) throws IOException {
//判斷字節(jié)長(zhǎng)度是否超過(guò)buf.length,buf在初始化已經(jīng)指定大小為8192,即8kb
//如果超過(guò)則調(diào)用flushBuffer
if (count >= buf.length) {
flushBuffer();
}
把每一個(gè)字節(jié)寫入緩沖的buf數(shù)組中,并且統(tǒng)計(jì)值count++
buf[count++] = (byte)b;
}
private void flushBuffer() throws IOException {
if (count > 0) {
//真正的調(diào)用OutputStream,寫入數(shù)據(jù)到磁盤中
//寫入buf緩沖字節(jié)數(shù)組數(shù)據(jù),從0下標(biāo)開始,一直寫到count,即有多少寫多少。
out.write(buf, 0, count);
count = 0;
}
}
關(guān)于buf緩沖數(shù)據(jù)大小設(shè)置
buffer提供了可以自定義緩沖大小的構(gòu)造方法
public BufferedOutputStream(OutputStream out, int size) {
super(out);
if (size <= 0) {
throw new IllegalArgumentException("Buffer size <= 0");
}
buf = new byte[size];
}
如果緩沖大小設(shè)置的比較大。
- 好處:進(jìn)一步減少調(diào)用系統(tǒng)內(nèi)核寫數(shù)據(jù)的方法,提高寫入速度,kafka的批寫入默認(rèn)就是16kb寫一次。
- 壞處:1、丟失的數(shù)據(jù)可能會(huì)更多,2、要注意堆內(nèi)存的消耗。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
通過(guò)實(shí)例學(xué)習(xí)JAVA對(duì)象轉(zhuǎn)成XML輸出
這篇文章主要介紹了通過(guò)實(shí)例學(xué)習(xí)JAVA對(duì)象轉(zhuǎn)成XML輸出,做流程圖的項(xiàng)目時(shí),新的流程定義為xml的,需要對(duì)xml與java對(duì)象進(jìn)行互轉(zhuǎn),下面我們來(lái)深入學(xué)習(xí),需要的朋友可以參考下2019-06-06
基于Java和XxlCrawler獲取各城市月度天氣情況實(shí)踐分享
本文主要講解使用Java開發(fā)語(yǔ)言,使用XxlCrawler框架進(jìn)行智能的某城市月度天氣抓取實(shí)踐開發(fā),文章首先介紹目標(biāo)網(wǎng)站的相關(guān)頁(yè)面及目標(biāo)數(shù)據(jù)的元素,然后講解在信息獲取過(guò)程的一些參數(shù)配置以及問(wèn)題應(yīng)對(duì),需要的朋友可以參考下2024-05-05
關(guān)于@RequestParam的使用所遇到的404問(wèn)題
這篇文章主要介紹了關(guān)于@RequestParam的使用所遇到的404問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08
spring boot集成rabbitmq的實(shí)例教程
這篇文章主要給大家介紹了關(guān)于spring boot集成rabbitmq的相關(guān)資料,springboot集成RabbitMQ非常簡(jiǎn)單,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友們可以參考借鑒,下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-11-11
springMVC返回復(fù)雜的json格式數(shù)據(jù)方法
下面小編就為大家分享一篇springMVC返回復(fù)雜的json格式數(shù)據(jù)方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-03-03
一文講解Java的String、StringBuffer和StringBuilder的使用與區(qū)別
String是不可變的字符序列,而StringBuffer和StringBuilder是可變的字符序列,本文就來(lái)詳細(xì)的介紹一下Java的String、StringBuffer和StringBuilder的使用與區(qū)別,感興趣的可以了解一下2024-03-03
如何配置cursor進(jìn)行Java springboot項(xiàng)目開發(fā)
本文介紹了如何在Cursor IDE中配置Java和Spring Boot項(xiàng)目開發(fā)環(huán)境,首先,設(shè)置了系統(tǒng)用戶級(jí)別的JDK配置,以便在多個(gè)項(xiàng)目之間切換時(shí)不需要重新配置,然后,配置了Gradle環(huán)境變量,并安裝了必要的Java開發(fā)插件,感興趣的朋友跟隨小編一起看看2025-02-02
java中字符串如何計(jì)算字節(jié)長(zhǎng)度
這篇文章主要介紹了java中字符串如何計(jì)算字節(jié)長(zhǎng)度,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-10-10
關(guān)于@Autowired的使用及注意事項(xiàng)
這篇文章主要介紹了關(guān)于@Autowired的使用及注意事項(xiàng),具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-05-05
Java基于Guava Retrying實(shí)現(xiàn)重試功能
這篇文章主要介紹了Java基于Guava Retrying實(shí)現(xiàn)重試功能,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-07-07

