1.0 為什么要寫這個。
- 在當(dāng)初學(xué)
java語言的時候,其實感覺這算是最難的基礎(chǔ)部分內(nèi)容之一,因為字符流和字節(jié)流的存在,還有緩沖字節(jié)流、字符流,選擇太多,不同的限制,導(dǎo)致使用的時候根本不知道:
1. 到底什么情況下怎么寫
2. 什么方案和代碼,寫會沒有什么大的問題。
- 所以在這里寫下相關(guān)知識點,所謂
授人以漁,更要授人以鱗、 鯉、 鯽、 鯨、 鰭、 鰲、 鰓、 鱖、 鱸、 鮭、 鯀、 鯤、 鯡、 鯫、 鯢、 鮒、 鱒、 鳒、 鰣、 鲝、 鲹、 鯖、 鲉、 鰈、 鳀、 鮐、 鯁、 鳑、 鳛、 鲞、 鲬、 鰉、 鰱、 鯪、 鰩、 鮪……
3.本來一個內(nèi)容就打算寫一篇的,簡書說我寫得太長了,不許我發(fā)布,所以拆成兩篇,查閱本篇的朋友請結(jié)合另一篇一同參考,謝謝。
鏈接如下:
【Java】2.0 Java核心之IO流(二)——生猛理解字符流
2.0 概念
- 2.1
IO流用來處理設(shè)備之間的數(shù)據(jù)傳輸 - 2.2
Java對數(shù)據(jù)的操作是通過流的方式 - 2.3
Java用于操作流的類都在IO包中 - 2.4
流按流向分為兩種:輸入流,輸出流。 - 2.5
流按操作類型分為兩種- 字節(jié)流 : 字節(jié)流可以操作任何數(shù)據(jù),因為在計算機中任何數(shù)據(jù)都是以字節(jié)的形式存儲的
- 字符流 : 字符流只能操作純字符數(shù)據(jù),比較方便。
3.0 IO流常用父類
- 字節(jié)流的抽象父類:
InputStreamOutputStream
- 字符流的抽象父類:
ReaderWriter
4.0 說到這里 ,你肯定覺得煩,因為上面講地的確都是廢話。
字節(jié)流讀取
英文、數(shù)字、視頻、音頻、圖片等超級好用的,速度快,這個是它存在的最大用處。字符流就是涉及中文輸入,如果用字節(jié)流,就會出現(xiàn)把一個字符,人為地拆開成兩個字節(jié),然后就各種亂碼。
所以,當(dāng)你知道自己需要傳英文、數(shù)字、視頻、音頻、圖片等不涉及中文傳輸?shù)臅r候,用字節(jié)流
比較好,規(guī)模小,耗時小,占用資源少,效率高。
當(dāng)涉及中文傳輸時,那是不是用字符流才是王道,不出錯才是爸爸呢?
錯! 如果在只讀或著只寫中文的情況下,才建議用用字符流,如果讀就是為了寫(比如復(fù)制,上傳存儲,發(fā)送給對方文件等),那還是用字節(jié)流會比較好(后面會解釋)。
再重復(fù)一遍,就是除了一種特殊情況,不然還是用字節(jié)流處理就行了。(這種情況是只讀或著只寫)字符流有缺點:不可以拷貝非純文本的文件!
比如說,用字符流讀取一張圖片,或者某個網(wǎng)址,因為在讀的時候會將字節(jié)轉(zhuǎn)換為字符,在轉(zhuǎn)換過程中,可能找不到對應(yīng)的字符,就會用?代替,寫出的時候會將?字符轉(zhuǎn)換成字節(jié)寫出去,如果是?字符直接寫出,寫出之后的文件就亂了,完全看不了。
5.0 IO程序代碼只有3步
是的,無論你要考慮什么情況,判斷什么條件,發(fā)送什么東西,通通只要2元……
好吧,總共分為4步走:
- 使用前,導(dǎo)入IO包中的類
- 2.使用時,進(jìn)行IO異常處理
- 3.使用后,釋放資源
- 4.沒了
6.0 字節(jié)流(7.0是緩沖字節(jié)流,8.0是字符流、9.0是緩沖字符流,END,IO流結(jié)束,本篇結(jié)束)
6.1 下面貼上一段完整的版本的處理方案:
package com.edpeng.stream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class Demo_TryFinally {
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
//demo1();
try(
//這個xxx.txt,自己在工程目錄下新建一個就好,里面自己錄入一些英文字母就可以
//別錄入中文,早晚會錯
FileInputStream fis = new FileInputStream("xxx.txt");
FileOutputStream fos = new FileOutputStream("yyy.txt");
//下面這行純粹為了測試用,可以沒有的
MyClose mc = new MyClose();
){
int b;
//讀一行就寫(復(fù)制)一行,讀寫就是這樣完成的,當(dāng)然你可以拆開,往里面加邏輯。
while((b = fis.read()) != -1) {
fos.write(b);
}
}
}
public static void demo1() throws FileNotFoundException, IOException {
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream("xxx.txt");
fos = new FileOutputStream("yyy.txt");
int b;
while((b = fis.read()) != -1) {
fos.write(b);
}
}finally {
try{
if(fis != null)
fis.close();
}finally { //try fianlly的嵌套目的是能關(guān)一個盡量關(guān)一個
if(fos != null)
fos.close();
}
}
}
}
class MyClose implements AutoCloseable {
public void close() {
System.out.println("我關(guān)了");
}
}
6.11 demo1()方法和main主函數(shù)里面的方法,是兩套。
demo1()方法主要是java jdk1.6以前版本使用的方法
先解釋demo1()方法,雖然里面的套路我們一般不這么用,但這樣的確是最嚴(yán)謹(jǐn)?shù)膶懛?,常常會在面試中用到,?dāng)然,如果你平時都這么寫,肯定是最好的。
-demo1()方法和main主函數(shù)兩個方法的目的都是讀取文件,輸出(復(fù)制)新的文件。
-demo1()方法,如果去掉里面的try/finally,也是可以的,因為異常已經(jīng)在方法那里直接拋出來了,這樣做相當(dāng)于try/finally里面鑲嵌了一套try/finally。
-demo1()方法,之所以這樣做,主要是為了處理3個方面的問題,:
1. fis和 fos報錯。
2. read()和write()報錯。
3. fis.close();和 fos.close();報錯。
為什么要處理這3種報錯情況?
第1種,可能存在讀取不到指定文件(可能不存在了),或可能存在沒法寫入(指定目錄不存在)。
如果沒有最外層的try/finally,假設(shè)fis = new FileInputStream("xxx.txt");工作正常,fos = new FileOutputStream("yyy.txt");拋異常,問題來了,第2個fis代表的FileInputStream文鍵輸入流沒關(guān)!運行不到“fis.close();”代碼就嗝屁了。
第2種,于是我們有了最外層的try/finally,那么無論try里面怎么拋異常,fis.close();和 fos.close();都能在finally里面關(guān)掉。
但是try里面還有一個問題,read()和write()拋異常。
有人會說,只要文件存在,讀和寫怎么會拋異常呢,文件里面就算是亂碼,那就亂讀亂寫唄。在windows操作環(huán)境下,基本沒什么大問題,但比如在linux環(huán)境下,文件經(jīng)常存在可讀性、可改性等基本屬性,所以很可能即使fis和 fos不拋異常,read()和write()還是會報錯的。所以這個也需要寫入到try/finally里面。
第3種,finally里面又嵌入了try/finally語句
這是為了解決如果第一個fis.close();拋異常(比如數(shù)據(jù)庫奔潰,服務(wù)器宕機等,沒關(guān)成)的話,至少第二個 fos.close();不會因為第一個直接拋異常而終止程序,導(dǎo)致 fos.close();沒關(guān),好歹至少關(guān)一個是吧。
完畢。
6.12 main主函數(shù),大家不要上去翻代碼了,這里直接貼下來:
public static void main(String[] args) throws IOException {
try(
//這個xxx.txt,自己在工程目錄下新建一個就好,里面自己錄入一些英文字母就可以
//別錄入中文,早晚會錯
FileInputStream fis = new FileInputStream("xxx.txt");
FileOutputStream fos = new FileOutputStream("yyy.txt");
//下面這行純粹為了測試用,可以沒有的
MyClose mc = new MyClose();
){
int b;
//讀一行就寫(復(fù)制)一行,讀寫就是這樣完成的,當(dāng)然你可以拆開,往里面加邏輯。
while((b = fis.read()) != -1) {
fos.write(b);
}
}
}
class MyClose implements AutoCloseable {
public void close() {
System.out.println("我關(guān)了");
}
}
可以看到,代碼簡化了不少,用了一個try( ){ }語句,這個是java jdk1.7之后,可以使用的新玩法。
這個語句命令的意思:無論大括號里面做什么,最后小括號里的東西,都會調(diào)用自己本來就是實現(xiàn)好的接口中的close()方法。
這里面有個MyClose( )方法,這主要為了我們這個例子測試用,平常不要加這個方法。 這個方法實現(xiàn)了接口AutoCloseable。因為我們的 InputStream和OutputStream類其實都實現(xiàn)了這個接口。
不信我們舉個例子,查看FileInputStream類的源代碼:

在eclipse里面按住ctrl鍵,然后挪動鼠標(biāo)放到
FileInputStream類上,點擊Open Declaration,這樣我們可以查看源代碼(查看不了的可以想想辦法,百度一下就好了。需要的留言,給教程)。

可以看到,
FileInputStream類繼承了InputStream類,接著查看InputStream類源代碼:
可以看到,
InputStream類實現(xiàn)了一個Closeable接口,接著查看Closeable接口源代碼:
可以看到,
Closeable類繼承了AutoCloseable類,,接著查看AutoCloseable類源代碼:
/*
* Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*/
package java.lang;
/**
* An object that …… …… non-I/O-based forms.
*
* @author Josh Bloch
* @since 1.7
*/
public interface AutoCloseable {
/**
* Closes this resource, relinquishing any ………… if this resource cannot be closed
*/
void close() throws Exception;
}
這里不適合截圖,直接放上來源代碼,里面大段注釋被我省略號了。
可以看到這個@since 1.7,是java jdk1.7版本之后才實現(xiàn)的接口,里面就一個方法close( )。
其實我們只要某個方法實現(xiàn)了這個AutoCloseable抽象類,就相當(dāng)于擁有了可以自閉 的技能。
我們用一個MyClose( )方法,實現(xiàn)抽象方法AutoCloseable類后,重寫里面的close( )方法,不僅能夠自閉 ,還可以告訴別人它自閉 了。所以在try 語句里面小括號就會自動調(diào)用 InputStream類、OutputStream類和MyClose( )方法中自帶的自閉技能 關(guān)閉自己。
6.2 細(xì)節(jié)來了。
6.21 大家注意到:
int b;
//讀一行就寫(復(fù)制)一行,讀寫就是這樣完成的,當(dāng)然你可以拆開,往里面加邏輯。
while((b = fis.read()) != -1) {
fos.write(b);
}
read()方法 返回值為什么是int ?
首先,上面兩句話是事實,我們下面這么多分析,只是為了理解為什么java要這么設(shè)計。
- 字節(jié)輸入流可以操作任意類型的文件,比如圖片音頻等,這些文件底層都是以二進(jìn)制形式的存儲的,我們這些文件肯定會有結(jié)束標(biāo)記,而且我們可以去簡單設(shè)計代碼測試一下,最后的返回值是-1。
所以我們的while語句里面用讀到最后會是-1作為判斷條件,表示文件讀完了。
如果是byte 類型,那就是1個字節(jié)(8位)為單位讀取,
就像下面這樣:
00010100 00100100 01000001 11111111 0000000
這樣,byte 要read() 5次才能全部讀完。但是,當(dāng)我們讀到11111111 的時候,出事了。因為它就是一個byte類型的-1,分析如下:
10000001??byte類型的-1的原碼
11111110??-1的反碼
11111111??-1的補碼
如果每次讀取都返回byte,有可能在讀到中間的時候遇到111111111,那么這11111111是byte類型的-1,我們的程序是遇到-1就會停止不讀了,后面的數(shù)據(jù)就讀不到了。
如果是int 類型,那就是4個字節(jié)為單位讀取,當(dāng)讀到11111111的時候,它會在前面補上3個字節(jié),補上24個0湊足4個字節(jié),變成下面這樣:
00000000 00000000 00000000 11111111 這樣,,這個數(shù)就會變成一個正的255。
這樣可以保證整個數(shù)據(jù)讀完,而結(jié)束標(biāo)記的-1就是int類型
read( )方法 是1個字節(jié)1個字節(jié)地讀,所以每次讀都會去補上24個0湊足4個字節(jié)。
我知道你上面可能還是看不懂,沒關(guān)系,至少可以知道是這么回事,o(╯□╰)o實在沒辦法解釋得更加通俗了。
接著繼續(xù)深入:
上面說了,read( )方法 會給每次讀都會去補上24個0湊足4個字節(jié),那寫的時候豈不是要出錯?
不要擔(dān)心,因為我們其實讀取文件是用read( )方法讀取的,我們的 write( )方法 會在每次寫的時候,自動去掉前面的24個0,一樣會保證數(shù)據(jù)的原樣性。( write( )方法 一次寫出也是1個字節(jié))
FileOutputStream fos = new FileOutputStream("bbb.txt"); //如果沒有bbb.txt,會創(chuàng)建出一個
//雖然寫出的是一個int數(shù),但是在寫出的時候會將前面的24個0去掉,所以寫出的是一個byte
fos.write(97);
fos.write(98);
fos.write(99);
fos.close();
這里的結(jié)果是,會在bbb.txt文件里面存入“abc”,解釋如上,所以如果手動寫入,大可不必自己先在想輸入的字節(jié)面前傻乎乎的手動添0。
6.22 如下聲明:
FileOutputStream fos = new FileOutputStream("yyy.txt");
FileOutputStream輸出流在創(chuàng)建對象的時候,如果沒有這個文件,會幫我們創(chuàng)建出來新的文件,如果有現(xiàn)成的,會把里面的內(nèi)容清空,把新的內(nèi)容寫進(jìn)去。
這里也有一個細(xì)節(jié),有人可能注意到,到底什么時候,哪條語句,執(zhí)行了清空(新建)的命令。
沒錯,就是FileOutputStream fos = new FileOutputStream("yyy.txt");這句。而且是所有的輸出流都是這樣,不只是FileOutputStream字節(jié)輸出流。
重點來了:如果想讀取同一個文件,經(jīng)過一番邏輯處理后把數(shù)據(jù)又存回原文件,千萬不要聲明FileInputStream指向文件,接著聲明FileOutputStream指向同一個文件,這時候因為你的聲明導(dǎo)致那個文件里面的內(nèi)容已經(jīng)被清空了。
舉例說:下面這樣是不行的,運行結(jié)果是null。因為aaa的文件在輸出流聲明之后里面已經(jīng)沒東西了。
public static void demo() throws FileNotFoundException, IOException {
FileInputStream fis = new FileInputStream("aaa.txt");
FileOutputStream fos = new FileOutputStream("aaa.txt");
byte[] arr = new byte[fis.available()];
fis.read(arr);
fos.write(arr);
fis.close();
fos.close();
}
下面這樣就沒問題了:
public static void demo() throws FileNotFoundException, IOException {
FileInputStream fis = new FileInputStream("aaa.txt");
byte[] arr = new byte[fis.available()];
fis.read(arr);
FileOutputStream fos = new FileOutputStream("aaa.txt");
fos.write(arr);
fis.close();
fos.close();
}
這個例子也讓我們體驗到流使用時(對,凡是各種語言中,不僅僅是java語言,只要是涉及流這種定義的),常遵循的一個原則——晚開早關(guān),晚開早關(guān),晚開早關(guān),重要的話說三遍,什么時候用,什么時候再開流,不用了就及時關(guān)掉。
6.23 接著說寫的事情。前面說到:FileOutputStream輸出流在創(chuàng)建對象的時候,如果沒有這個文件,會幫我們創(chuàng)建出來新的文件,如果有現(xiàn)成的,會把里面的內(nèi)容清空,把新的內(nèi)容寫進(jìn)去。
那么如何把里面原有的內(nèi)容不刪掉,直接在后面添加就好?
FileOutputStream fos = new FileOutputStream("bbb.txt",true); //如果沒有bbb.txt,會創(chuàng)建出一個
fos.write(97);
fos.write(98);
fos.write(99);
fos.close();
看懂了沒有,只需要多一個true 。
6.254 拷貝圖片也是這樣:
//創(chuàng)建輸入流對象,讀取,關(guān)聯(lián)致青春.mp3
public static void demo() throws FileNotFoundException, IOException {
FileInputStream fis = new FileInputStream("狂狼.mp3");
//創(chuàng)建輸出流對象,寫出,關(guān)聯(lián)copy.mp3
FileOutputStream fos = new FileOutputStream("copy.mp3");
int b;
while((b = fis.read()) != -1) {
fos.write(b);
}
fis.close();
fos.close();
}
6.25 字節(jié)流一次讀寫一個字節(jié)復(fù)制音頻、視頻等,效率太低,怎么辦?
// 本方法不推薦使用,因為有可能會導(dǎo)致內(nèi)存溢出
public static void demo() throws FileNotFoundException, IOException {
//創(chuàng)建輸入流對象,關(guān)聯(lián)狂狼.mp3
FileInputStream fis = new FileInputStream("狂狼.mp3");
//創(chuàng)建輸出流對象,關(guān)聯(lián)copy.mp3
FileOutputStream fos = new FileOutputStream("copy.mp3");
//創(chuàng)建與文件一樣大小的字節(jié)數(shù)組
byte[] arr = new byte[fis.available()];
//將文件上的字節(jié)讀取到內(nèi)存中
fis.read(arr);
//將字節(jié)數(shù)組中的字節(jié)數(shù)據(jù)寫到文件上
fos.write(arr);
fis.close();
fos.close();
}
這里用了一個available() 方法,想想也知道這個就是可以查詢得知輸入流文件大小的方法。
為什么不推薦使用呢,因為如果我們讀取例如200MB大小的文件,這個沒什么關(guān)系,如果是一個20GB的壓縮包呢,當(dāng)我們在創(chuàng)建數(shù)組arr的時候,內(nèi)存根本放不下。至少,我們平常見到的電腦,其運行內(nèi)存一般也就4GB、8GB,16GB都算高配了。
優(yōu)化一下,我們可以這樣:
public static void demo() throws FileNotFoundException, IOException {
FileInputStream fis = new FileInputStream("xxx.txt");
FileOutputStream fos = new FileOutputStream("yyy.txt");
byte[] arr = new byte[2];
int len;
while((len = fis.read(arr)) != -1) {
fos.write(arr,0,len);
}
fis.close();
fos.close();
}
這里大家可能要說了——又變了,你個渣男,說好的一開始就給的完整版,都等著套模板了,你居然還要變……
這里write( )方法多了個變化,意思是:一次讀取arr長度的字節(jié)內(nèi)容,偏移量為0個字節(jié),讀取len個長度。,從0個字節(jié)的位置開始寫入arr數(shù)組里面的內(nèi)容,從前頭開始數(shù)len長度的個數(shù)。
看不懂具體了解這個方法請移步java中文開發(fā)說明文檔。
總之,這是為了處理最后剩下需要讀取不足arr數(shù)組長度的時候,將末尾會自動補足的0給去掉,保證數(shù)據(jù)的原樣性。
當(dāng)然,我們還有最終優(yōu)化,因為這樣做僅僅比讀取1一個字節(jié)提高了1倍的效率。
FileInputStream fis = new FileInputStream("狂狼.mp3");
FileOutputStream fos = new FileOutputStream("copy.mp3");
byte[] arr = new byte[1024 * 8];
int len;
while((len = fis.read(arr)) != -1) { //如果忘記加arr,返回的就不是讀取的字節(jié)個數(shù),而是字節(jié)的碼表值
fos.write(arr,0,len);
}
fis.close();
fos.close();
看到了沒有,這里把arr數(shù)組變成1024的整數(shù)倍,因為計算機的字節(jié)倍數(shù)就是1024的倍數(shù)(比如1KB = 1024B),這樣的效率就很高了,當(dāng)然,你可以定義更好的處理邏輯。
7.0 緩沖字節(jié)流
其實就是在 FileInputStream類和 FileOutputStream類外面包了兩層皮,不信你去查看源碼或者查看繼承關(guān)系。
BufferedInputStream類
BufferedOutputStream類
7.1 首先說明的是緩沖流的思想
A:緩沖思想
- 字節(jié)流一次讀寫一個數(shù)組的速度明顯比一次讀寫一個字節(jié)的速度快很多。
- 這是加入了數(shù)組這樣的緩沖區(qū)效果,java本身在設(shè)計的時候,也考慮到了這樣的設(shè)計思想(裝飾設(shè)計模式,下面會解釋這個
設(shè)計模式),所以提供了字節(jié)緩沖區(qū)流
B:BufferedInputStream
-
BufferedInputStream內(nèi)置了一個緩沖區(qū)(數(shù)組) - 從
BufferedInputStream中讀取一個字節(jié)時 -
BufferedInputStream會一次性從文件中讀取8192個, 存在緩沖區(qū)中, 返回給程序一個 - 程序再次讀取時, 就不用找文件了, 直接從緩沖區(qū)中獲取
- 直到緩沖區(qū)中所有的都被使用過, 才重新從文件中讀取8192個
C:BufferedOutputStream
-
BufferedOutputStream也內(nèi)置了一個緩沖區(qū)(數(shù)組) - 程序向流中寫出字節(jié)時, 不會直接寫到文件, 先寫到緩沖區(qū)中
- 直到緩沖區(qū)寫滿,
BufferedOutputStream才會把緩沖區(qū)中的數(shù)據(jù)一次性寫到文件里
看懂了沒有,其實緩沖流其實和上面6.25小點里面的自定義數(shù)組是一個尿性。
所以能用緩沖流就用緩沖流,畢竟讀取也快,寫出也快。
7.2 代碼相當(dāng)簡單:
//創(chuàng)建文件輸入流對象,關(guān)聯(lián)狂狼.mp3
FileInputStream fis = new FileInputStream("狂狼.mp3");
//創(chuàng)建緩沖區(qū)對fis裝飾
BufferedInputStream bis = new BufferedInputStream(fis);
//創(chuàng)建輸出流對象,關(guān)聯(lián)copy.mp3
FileOutputStream fos = new FileOutputStream("copy.mp3");
//創(chuàng)建緩沖區(qū)對fos裝飾
BufferedOutputStream bos = new BufferedOutputStream(fos);
int b;
while((b = bis.read()) != -1) {
bos.write(b);
}
//只關(guān)裝飾后的對象即可
bis.close();
bos.close();
如果你覺得還是多了兩行代碼,明明已經(jīng)變復(fù)雜了,其實我們一般寫成這樣:
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("狂狼.mp3"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy.mp3"));
int b;
while((b = bis.read()) != -1) {
bos.write(b);
}
bis.close();
bos.close();
這充分證明了我不是渣男。
這里還有一個疑問,那如果我像6.25小點里面的自定義數(shù)組和用帶Buffered的讀取哪個更快?
- 定義小數(shù)組如果是8192個字節(jié)大小和Buffered比較的話
- 定義小數(shù)組會略勝一籌,因為讀和寫操作的是同一個數(shù)組
- 而Buffered操作的是兩個數(shù)組
7.3 細(xì)節(jié)來了。
7.31 我們知道,在這些IO流操作里面,可能大家在學(xué)習(xí)的時候,可能是培訓(xùn)老師,可能是某本書,會告訴你每次記得——最后放進(jìn)去一個flush( )方法,目的是確保緩沖流的所有數(shù)據(jù)都會寫進(jìn)去,別丟數(shù)據(jù)。
但是!其實你不用也沒關(guān)系……只要你有下面的操作就可以了:
bis.close();
bos.close();
flush( )方法
- 用來刷新緩沖區(qū)的,刷新后可以再次寫出
close( )方法
- 用來關(guān)閉流釋放資源的的,如果是帶緩沖區(qū)的流對象的close()方法,不但會關(guān)閉流,還會再關(guān)閉流之前刷新緩沖區(qū),關(guān)閉后不能再寫出
所以,到底什么時候用flush( )方法?
- ??如果緩沖流還想接著用,比如聊天軟件,我發(fā)一段話給你,總不能等我湊夠8192個字節(jié)后,再一波發(fā)給對方看。肯定發(fā)一次就清空一次緩沖流。
- ??但是如果調(diào)用
close( )方法,那不好意思,說完這句都沒法再說了,因為流已經(jīng)關(guān)閉了。所以往往是這種情況下就要用flush( )方法,發(fā)出去后,沒關(guān)系,想發(fā)還可以繼續(xù)發(fā),想收的,先把收到的及時清空,顯示給對方看,再繼續(xù)收。
7.32 字節(jié)流讀取中文的問題
總有人問,那字節(jié)流就搞不定中文的問題了?不,是真搞不定……
我們知道1個中文占用1個字符,等于2個字節(jié)。
字節(jié)流在讀中文的時候有可能會讀到半個中文,造成亂碼,可以用下面的方法測試:
public static void demo() throws FileNotFoundException, IOException {
FileInputStream fis = new FileInputStream("yyy.txt");
byte[] arr = new byte[4];
int len;
while((len = fis.read(arr)) != -1) {
System.out.println(new String(arr,0,len));
}
fis.close();
}
當(dāng)然,"yyy.txt"里面現(xiàn)在存了一些中文在里面,自己隨便測吧,總之當(dāng)一次讀取4個字節(jié)的時候,剛好斷開點是一個中文的上半個字節(jié)的話,亂碼就來了。
7.33 字節(jié)流寫出中文的問題
那么寫會有什么問題么?答案是寫出中文不會有算命會造成亂碼的問題!寫不會有什么問題,寫不會有什么問題,重要的話說三遍。就像下面這樣:
public static void demo() throws FileNotFoundException, IOException {
FileOutputStream fos = new FileOutputStream("zzz.txt");
fos.write("我讀書少,你不要騙我".getBytes());
fos.write("\r\n".getBytes());
fos.close();
}
隨便測試,不會出錯的。
- 字節(jié)流直接操作的字節(jié),所以寫出中文必須將字符串轉(zhuǎn)換成字節(jié)數(shù)組
- 寫出回車換行
write("\r\n".getBytes());
只是讀寫中文的話(中間不進(jìn)行任何操作),雖然讀出來會亂碼,但是寫出來后又會恢復(fù)原樣,當(dāng)然,這時候?qū)懙臅r候就不需要用什么getBytes()方法了。
END