黑馬程式設計師——Java IO流(一)之IO流概述、字元流、位元組流等
-----------android培訓、java培訓、java學習型技術部落格、期待與您交流!------------
IO流
一、概述
1.IO流是用來處理裝置之間的資料傳輸。
2.Java對資料的操作時通過流的方式。
3.Java用於操作流的物件都在IO包中。
4.流按操作資料分為兩種:位元組流和字元流。流按流向分為:輸入流和輸出流。
5.流常用的基類:
1)位元組流的抽象基類:ImputStream和OutputStream。
2)字元流的抽象基類:Reader和Writer。
6.體系架構:
字元流體系架構:
Reader:用於讀取字元流的抽象類。
|--BufferedReader:從字元輸入流中讀取文字,緩衝各個字元,從而實現字元、陣列和行的高效讀取。
|--LineNumberReader:跟蹤行號的緩衝字元輸入流。
|--InputStreamReader:轉換流,是位元組流通向字元流的橋樑。
|--FileReader:用來讀取字元檔案的便捷類。
Writer:寫入字元流的抽象類。
|--BufferedWriter:將文字寫入字元輸出流,緩衝各個字元,從而提供單個字元、陣列和字串的高效寫入。
|--OutputStreamWriter:轉換流,是字元流通向位元組流的橋樑。
|--FileWriter:用來寫入字元檔案的便捷類。
位元組流體系架構:
ImputStream:
|--FileInputStream:從檔案系統中的某個檔案中獲得輸入位元組, 用於讀取諸如影象資料之類的原始位元組流。
|--FilterInputStream:
|--BufferedInputStream:位元組輸入流緩衝區。
OutputStream:此抽象類是表示輸出位元組流的所有類的超類,輸出流接受輸出位元組並將這些位元組傳送到某個接收器。
|--FileOutputStream:檔案輸出流是用於將資料寫入
File 或 FileDescriptor 的輸出流,用於寫入諸如影象資料之類的原始位元組的流。
|--FilterOutputStream:此類是過濾輸出流的所有類的超類。
|--BufferedOutputStream:該類實現緩衝的輸出流。通過設定這種輸出流,應用程式就可以將各個位元組寫入底層輸出流中,而不必針對每次位元組寫入呼叫底層系統。
|--PrintStream:為其他輸出流添加了功能,使它們能夠方便地列印各種資料值表示形式。列印的所有字元都使用平臺的預設字元編碼轉換為位元組。PrintStream 永遠不會丟擲 IOException,而是,異常情況僅設定可通過 checkError 方法測試的內部標誌。在需要寫入字元而不是寫入位元組的情況下,應該使用 PrintWriter 類。
問題思考:位元組流和字元流有什麼不同?
答:位元組流可以用於進行任何資料型別的操作,而字元流帶編碼表,只能用於進行純文字資料型別的操作。
二、格式示例
IO異常格式的處理:
import java.io.*;
class FileWriterDemo2
{
public static void main(String[] args)
{
//需要將fw引用定義在外部,因為在finally中需要呼叫close方法
FileWriter fw=null;
try
{
//建立物件的時候,需要定義在try裡面,因為丟擲了IO異常。
fw=new FileWriter("E:\\heima\\deme2.txt");
//write方法丟擲了IO異常。
fw.write("hello world");
}
catch (IOException e)
{
System.out.println("cacht="+e.toString());
}
finally
{
//關閉流資源一定要執行,需定義在finally內部,且close方法丟擲了
//IO異常,需要進行try處理。
try
{
//需要對fw是否為null進行判斷,因為如果fw為null,則再呼叫close方法
//發發生NullPoterException異常。如果有多個流資源需要關閉,應進行多次
//判斷並關閉,不要寫在一個判斷裡。
if (fw!=null)
{
fw.close();
}
}
catch (IOException ex)
{
System.out.println(ex.toString());
}
}
}
}
三、字元流
字元流常用對字元檔案的操作。對於字元流的操作,應熟練掌握以下幾個內容:
1.在指定目錄下建立一個純文字檔案,並在這個檔案中寫入指定內容:
示例1:在"E:\\heima"的目錄下建立純文字檔案demo.txt,並向該檔案demo.txt寫入內容。
import java.io.*;
class FileWriterDemo
{
public static void main(String[] args) throws IOException
{
FileWriter fw=null;
try
{
//fileWriter物件一被初始化就必須明確被操作的檔案的物件。
//而且該檔案會被建立到指定目錄下,如果該目錄下已有同名檔案存在,
//則將被覆蓋。由於建立的目錄可能不存在,因此丟擲了IOException。
fw=new FileWriter("E:\\heima\\demo.txt");
//呼叫write方法,將字串寫入到流中。
fw.write("nihao");
//重新整理流物件中的緩衝區的資料。將資料刷到目的地中。
fw.flush();
//可重複進行寫入資料到檔案中。
fw.write("hello");
fw.flush();
}
catch (IOException e)
{
System.out.println("cacht="+e.toString());
}
finally
{
try
{
if (fw!=null)
{
//關閉流物件,在關閉前會重新整理流一次。關閉後不能再向流中寫入資料,
//否則將發生IO異常。該動作一定做,因為流在呼叫window資源,需要進行關閉。
fw.close();
}
}
catch (IOException ex)
{
System.out.println(ex.toString());
}
}
}
}
程式執行後在"E:\\heima"的目錄下的建立了一個Demo.txt檔案,檔案的內容如下圖:
2.對指定目錄下的一個純文字檔案進行內容續寫:
示例2:對示例1中建立的檔案demo.txt進行內容續寫。
import java.io.*;
class FileWriterDemo3
{
public static void main(String[] args)
{
FileWriter fw=null;
try
{
//傳遞一個true穿引數,代表不覆蓋已有檔案,並在已有檔案末尾處新增。
//如果沒有已有檔案,則會新建立一個檔案。
fw=new FileWriter("E:\\heima\\demo2.txt",true);
//新增內容
fw.write("zhangsan");
}
catch (IOException e)
{
System.out.println(e.toString());
}
finally
{
try
{
if (fw!=null)
{
fw.close();
}
}
catch (IOException ex)
{
System.out.println(ex.toString());
}
}
}
}
程式執行後,在示例1中的demo.txt檔案內容中續寫了一些指定內容,截圖如下:
3.寫入內容的方法總結:
1)寫入單個字元。
void write(int c):將int型別的字元c寫入到指定目的物件中。
2)寫入字元陣列或其中的某一部分。
void write(char[] cbuf):將字元陣列cbuf中的全部字元寫入到指定物件中。
void write(char[] cbuf,int off,int len):將字元陣列cbuf中從索引off開始寫入到指定物件,寫入的字元總數為len。
3)寫入字串或其中的某一部分。
void write(String str):將整個字串內容寫入到指定物件中。
void write(String str,int off,int len):將字串str中從索引off開始寫入到指定物件,寫入的字元總數為len。
注:上述所有寫入的方法都會丟擲IOException異常。
4.對指定目錄下的一個純文字檔案進行內容讀取:
示例3:對示例2續寫的檔案demo.txt進行內容讀取並列印到控制檯。
import java.io.*;
class FileReaderDemo1
{
public static void main(String[] args)
{
FileReader fr=null;
try
{
//建立一個檔案讀取流物件,和指定的檔案demo.txt相關聯。
//要保證該檔案是存在的,如果不存在則會發生FileNotFoundException異常。
fr=new FileReader("E:\\heima\\demo.txt");
int ch=0;
//read()方法讀取檔案的內容,一次讀一個字元,而且會自動往下讀。
//迴圈讀取檔案內容,如果讀到檔案末尾則會返回-1,可根據此條件
//來判斷是否繼續讀取。
while ((ch=fr.read())!=-1)
{
System.out.print((char)ch); //將讀取到int型別資料轉換成字元型別輸出。
}
}
catch (IOException e)
{
System.out.println(e.toString());
}
finally
{
try
{
if (fr!=null)
{
fr.close();
}
}
catch (IOException ex)
{
System.out.println(ex.toString());
}
}
}
}
程式執行後的結果如下圖:
5.讀取內容的方式總結:
1)讀取單個字元。
int read():返回值為讀取的int型別字元。如果讀取的字元已到達流的末尾,則返回 -1。
2)將字元讀入陣列或陣列中的某一部分。
int read(char[] cbuf):將字元讀入陣列。在某個輸入可用、發生 I/O 錯誤或者已到達流的末尾前,此方法一直阻塞。
int read(char[] cbuf,int offset,int length):將字元讀入字元陣列cbuf中,從索引offset開始儲存,存入的字元個數限制為length。
注:返回值為實際儲存到字元陣列的個數。如果讀取的字元數已到達流的末尾,則返回 -1。
3)將字元讀入指定的字元緩衝區。瞭解charBuffer更多用法,參加AIP文件。
int read(CharBuffer target):返回值為新增到緩衝區的字元數量,如果此字元源位於緩衝區末端,則返回 -1。
6.字元流緩衝區:
1)BufferedWriter:字元流寫入緩衝區。
寫入資料的方法有:
a)寫入單個字元:
void write(int c):該方法丟擲了IO異常。
b)寫入字元陣列:
void write(char[] c):該方法丟擲了IO異常。
c)寫入字元陣列的某一部分:
void write(char[] cbuf, int off, int len) :該方法丟擲了IO異常。
d)寫入字串:
void write(String str):該方法丟擲了IO異常。
e)寫入字串的某一部分:
write(String s, int off, int len):如果 len 引數的值為負數,則不寫入任何字元。這與超類中此方法的規範正好相反,它要求丟擲 IndexOutOfBoundsException。該方法丟擲了IO異常。
示例4:使用緩衝區技術,在"E:\\heima"的目錄下建立純文字檔案test.txt,並向該檔案test.txt寫入內容。
<pre name="code" class="java">import java.io.*;
class BufferedWriterDemo
{
public static void main(String[] args)
{
//建立字元流讀取緩衝區引用
BufferedWriter bufw=null;
try
{
//建立字元流讀取緩衝區物件,並傳入檔案讀取流物件
bufw=new BufferedWriter(new FileWriter("E:\\heima\\test.txt"));
//將字串內容寫入到字元流讀取緩衝區中
bufw.write("nihao");
//重新整理緩衝區中的資料,在寫入內容不多的情況下,可以不用flush方法,但一定要close方法關閉
//資源,即將寫入的內容最後關閉的時候一次性刷入檔案中。如果寫入內容很多,應加flush,防止
//在還沒有重新整理的情況下,出現停電,導致資料丟失。當加了flush後,可以即時將資料刷入檔案中。
bufw.flush();
//換行
bufw.newLine();
bufw.write("nihao");
}
catch (IOException e)
{
throw new RuntimeException("檔案寫入失敗");
}
finally
{
if (bufw!=null)
{
try
{
bufw.close();
}
catch (IOException ex1)
{
throw new RuntimeException("寫入關閉失敗");
}
}
}
}
}
程式執行後在"E:\\heima"的目錄下生成了test.txt檔案,檔案內容如下截圖所示:
2)BufferedReader:字元流讀取緩衝區。
讀取資料的方法有:
a)讀取單個字元:
int read():返回字元的int型別值。如果已到達流末尾,則返回 -1。
b)將字元讀入陣列:
int read(char[] cbuf):返回讀入的字元數。如果已到達流的末尾,則返回 -1。在某個輸入可用、發生 I/O 錯誤或者已到達流的末尾前,此方法一直阻塞。
c)將字元讀入陣列的某一部分:
int read(char[] c,int off,int len):返回讀取的字元數。如果已到達流末尾,則返回 -1。
d)讀取一個文字行:
int readLine():返回包含該行內容的字串,不包含任何行終止符。如果已到達流末尾,則返回 null。
e)試圖將字元讀入指定的字元緩衝區:
int read(charBuffer target):返回新增到緩衝區的字元數量,如果此字元源位於緩衝區末端,則返回 -1。
注:上述方法都丟擲了IO異常。
示例5:使用緩衝區技術,對示例4建立的test.txt檔案進行內容讀取並列印輸出到控制檯。
import java.io.*;
class BufferedReaderDemo
{
public static void main(String[] args)
{
//建立字元流讀取緩衝區引用
BufferedReader bufr=null;
try
{
//建立字元流讀取緩衝區物件,並傳入檔案讀取流物件
bufr=new BufferedReader(new FileReader("E:\\heima\\test.txt"));
String line=null;
//呼叫字元流讀取緩衝區物件的readLine方法,如果判斷不為空,則繼續讀下一行
while ((line=bufr.readLine())!=null)
{
System.out.println(line);
}
}
catch (IOException e)
{
throw new RuntimeException("檔案讀取失敗");
}
finally
{
if (bufr!=null)
{
try
{
bufr.close();
}
catch (IOException ex1)
{
throw new RuntimeException("讀取關閉失敗");
}
}
}
}
}
程式執行後的結果如下截圖所示:
7.裝飾設計模式:
1)概述
當想要對已有的物件進行功能增強時,可以定義類,將已有的功能,並提供加強功能。那麼自定義的類就稱為裝飾類。
裝飾類通常會通過構造方法接受被裝飾的物件,並基於被裝飾的物件的功能,提供更強的功能。
示例6:對已存在的Person類進行功能增強,並用SuperPerson類描述。
<pre name="code" class="java">class PersonDemo
{
public static void main(String[] args)
{
SuperPerson sp=new SuperPerson(new Person());
sp.superEat();
}
}
class Person
{
public void eat()
{
System.out.println("吃飯");
}
}
//裝飾類
class SuperPerson
{
private Person p;
SuperPerson(Person p)
{
this.p=p;
}
//對eat功能進行增強
public void superEat()
{
System.out.println("開胃酒");
p.eat();
System.out.println("甜點");
}
}
程式執行後的結果如下圖所示:
2)裝飾與繼承的區別:
繼承結構:
MyReader
|--MyTextReader
|--MyBuffferedTextReader:繼承增強類。
|--MyMediaReader
|--MyBufferedMediaReader:繼承增強類。
|--MyDataReader
|--MyBufferedDataReader:繼承增強類。
裝飾結構:
MyReader
|--MyTextReader
|--MyMediaReader
|--MyDataReader
|--MyBufferedReader:裝飾類,用於給MyReader子類的功能進行增強。
區別:裝飾模式比繼承要靈活,避免了繼承體系的臃腫,而且降低了類與類之間的關係。裝飾類因為增強已有物件,具備的功能和已有功能是相同的,只不過提供了更強的功能。所以裝飾類和被裝飾類通常屬於一個體系中的。但裝飾類只能在被裝飾類的父類的基礎上進行增強,而繼承可以在被繼承類的基礎上直接增強。
3)自定義裝飾類:
示例7:模擬BufferedReader類。
//自定義裝飾類
class MyBufferedReader extends Reader
{
private FileReader r;
MyBufferedReader(FileReader r)
{
this.r=r;
}
//定義讀取行功能
public String myReadLine() throws IOException
{
StringBuilder sb=new StringBuilder();
int ch=0;
while ((ch=r.read())!=-1)
{
if (ch=='\r')
continue;
else if (ch=='\n')
return sb.toString();
else
sb.append((char)ch);
}
if (sb.length()!=0)
{
return sb.toString();
}
return null;
}
//直接用傳進來的子類進行覆蓋,因為該子類一定實現了父類的方法
public int read(char buf,int off,int len)
{
r.read(buf,off,len);
}
//覆蓋父類的close抽象方法
public void close()
{
r.close();
}
}
8.LinenumberReader類:
1)概述
LineNumberReader是BufferedReader的子類,用於跟蹤行號的緩衝字元輸入流。該類的setLineNumber和getLineNumber方法可以用於設定和獲取行號。
示例8:對示例4建立的test.txt檔案進行內容讀取並列印輸出到控制檯,且要求列印的內容帶有行號,並要求從100行開始輸出。
import java.io.*;
class LineNumberReaderDemo
{
public static void main(String[] args)
{
LineNumberReader lnr=null;
try
{
lnr=new LineNumberReader(new FileReader("E:\\heima\\test.txt"));
String line=null;
//設定行號為100
lnr.setLineNumber(100);
while ((line=lnr.readLine())!=null)
{
//連同行號一起列印
System.out.println(lnr.getLineNumber()+"::"+line);
}
}
catch (IOException e)
{
throw new RuntimeException("檔案讀取失敗");
}
finally
{
if (lnr!=null)
{
try
{
lnr.close();
}
catch (IOException ex1)
{
throw new RuntimeException("讀取關閉失敗");
}
}
}
}
}
程式執行後的結果如下圖:
2)自定義LineNumberReader類:
/*
自定義LineNumberReader類:
思路:
1.定義MyLineNumberReader類,並繼承BufferedReader。
2.複寫readLine方法,使其沒讀取一次該方法,行號計數加1。
3.對外提供設定行號和獲取的方法。
*/
import java.io.*;
//繼承BufferedReader類,可以使類更加簡單,只需定義特有方法即可。
class MyLineNumberReader extends BufferedReader
{
//定義行號屬性
private int lineNumber;
MyLineNumberReader(Reader r)
{
super(r);
}
public String readLine() throws IOException
{
//每呼叫一次行號自增1
lineNumber++;
return super.readLine();
}
//設定行號
public void setLineNumber(int lineNumber)
{
this.lineNumber=lineNumber;
}
//獲取行號
public int getLineNumber()
{
return lineNumber;
}
}
9.程式碼練習:
練習1:完成對示例4建立的test.txt檔案的複製。
/*
需求:完成<span style="font-size:14px;">對示例4建立的test.txt檔案</span>的複製。
思路:
1.在指定目錄下建立一個檔案,用來儲存複製文字內容。
2.建立一個檔案讀取流物件,與要複製文字檔案相關聯。
3.將要複製的文字檔案的內容讀取到陣列中。
4.將陣列中的內容寫入到新建立的檔案中。
*/
import java.io.*;
class FileCopyDemo1
{
public static void main(String[] args)
{
FileWriter fw=null;
FileReader fr=null;
try
{
fw=new FileWriter("E:\\heima\\testCopy.txt");
fr=new FileReader("E:\\heima\\test.txt");
//定義字元陣列用來儲存
char[] buf=new char[1024];
int num=0;
while ((num=fr.read(buf))!=-1)
{
//將陣列中的內容寫入到新建立的文字檔案中,不需要重新整理。
fw.write(buf,0,num);
}
}
catch (IOException e)
{
throw new RuntimeException("檔案讀取失敗");
}
finally
{
if (fr!=null)
{
try
{
fr.close();
}
catch (IOException ex1)
{
throw new RuntimeException("讀取關閉失敗");
}
}
if (fw!=null)
{
try
{
fw.close();
}
catch (IOException ex2)
{
throw new RuntimeException("寫入關閉失敗");
}
}
}
}
}
程式執行後在"E:\\heima"的目錄下建立了一個testCopy.txt檔案,該檔案的內容的截圖如下:
練習2:使用字元流緩衝區技術完成練習1。
/*
思路:
1.在指定目錄下建立一個檔案,用來儲存複製文字內容。定義一個字元寫入流緩衝區,並與新建立的文字檔案相關聯。
2.建立一個檔案讀取流物件,與要複製文字檔案相關聯,定義一個字元讀取流緩衝區,並與檔案讀取流物件相關聯。
3.用readLine方法迴圈取出字元讀取緩衝區的內容。
4.將readLine取出的內容寫入到字元寫入流緩衝區中。
5.用newLine方法換行,並重新整理到新建立的檔案中。
*/
import java.io.*;
class FileCopyDemo2
{
public static void main(String[] args)
{
BufferedWriter bufw=null;
BufferedReader bufr=null;
try
{
bufw=new BufferedWriter(new FileWriter("E:\\heima\\test_Copy.txt"));
bufr=new BufferedReader(new FileReader("E:\\heima\\test.txt"));
String line=null;
while ((line=bufr.readLine())!=null)
{
bufw.write(line);
bufw.newLine();
bufw.flush();
}
}
catch (IOException e)
{
throw new RuntimeException("檔案讀取失敗");
}
finally
{
if (bufr!=null)
{
try
{
bufr.close();
}
catch (IOException ex1)
{
throw new RuntimeException("讀取關閉失敗");
}
}
if (bufw!=null)
{
try
{
bufw.close();
}
catch (IOException ex2)
{
throw new RuntimeException("寫入關閉失敗");
}
}
}
}
}
程式執行後在"E:\\heima"的目錄下建立了test_Copy.txt檔案,該檔案的內容截圖如下:
四、位元組流
位元組流常用於對非字元檔案的操作,如圖象、視訊等,但不表示位元組流不能操作字元檔案,而是用字元流來操作字元檔案更為便捷。位元組流應掌握的內容基本同字元流一致。
1.寫入內容的方法總結:
1)將指定位元組寫入此檔案輸出流。
void write(int b):該方法丟擲了IO異常。
2)將 b.length 個位元組從指定 byte 陣列寫入此檔案輸出流中。
void write(byte[] b):該方法丟擲了IO異常。
3)將指定 byte 陣列中從偏移量 off 開始的 len 個位元組寫入此檔案輸出流。
void write(byte[] b,int off,int len):該方法丟擲了IO異常。
注:上述所有方法都是FileOutputStream類特有方法,且寫入檔案時,不需要進行重新整理操作。
2.讀取內容的方法總結:
1)從此輸入流中讀取一個數據位元組。
int read():返回下一個資料位元組。如果已到達檔案末尾,則返回 -1。
2)從此輸入流中將最多 b.length 個位元組的資料讀入一個 byte 陣列中。
int read(byte[] b):返回讀入緩衝區的位元組總數。如果因為已經到達檔案末尾而沒有更多的資料,則返回 -1。在某些輸入可用之前,此方法將阻塞。
3)從此輸入流中將最多 len 個位元組的資料讀入一個 byte 陣列中。
int read(byte[] b,int off,int len):讀入緩衝區的位元組總數,如果因為已經到達檔案末尾而沒有更多的資料,則返回 -1。如果 len 不為 0,則在輸入可用之前,該方法將阻塞;否則,不讀取任何位元組並返回 0。
3.複製圖片(複製視訊等原理類似):
示例9:複製指定目錄的檔案。
/*
思路:
1.用位元組讀取流物件與圖片關聯。
2.用位元組寫入流物件建立一個新的圖片檔案,用來進行儲存獲取到的檔案資料。
3.通過迴圈讀寫完成資料的儲存。
4.關閉資源。
注:位元組流寫入資料時不需要重新整理,但仍需關閉資源。
*/
import java.io.*;
class CopyPicDemo
{
public static void main(String[] args)
{
FileInputStream fis=null;
FileOutputStream fos=null;
try
{
fos=new FileOutputStream("E:\\heima\\2.png"); //建立位元組寫入流物件,生成複製影象檔案。
fis=new FileInputStream("E:\\heima\\1.png"); //建立位元組讀取流物件,並與要被複制的影象檔案關聯。
byte[] by=new byte[1024];
int len=0;
while ((len=fis.read(by))!=-1)
{
fos.write(by,0,len);
}
}
catch (IOException e)
{
throw new RuntimeException("檔案複製失敗");
}
finally
{
if (fis!=null)
{
try
{
fis.close();
}
catch (IOException ex1)
{
throw new RuntimeException("讀取關閉失敗");
}
}
if (fos!=null)
{
try
{
fos.close();
}
catch (IOException ex1)
{
throw new RuntimeException("寫入關閉失敗");
}
}
}
}
}
程式執行後,複製影象檔案與被複製圖形檔案對比圖如下:
4.位元組流緩衝區
位元組流緩衝區有兩個:BufferedInputStream(位元組流讀取緩衝區)和BufferedOutputStream(位元組流寫入緩衝區)。
從輸入流讀取資料的方法總結:
1)從輸入流中讀取資料的下一個位元組:
int read():返回下一個資料位元組。如果到達流末尾,則返回 -1。
2)從此輸入流中將 byte.length 個位元組的資料讀入一個 byte 陣列中:
int read(byte[] b):返回讀入緩衝區的位元組總數,如果因為已經到達流末尾而沒有更多的資料,則返回 -1。在某些輸入可用之前,此方法將阻塞。
3)從此位元組輸入流中給定偏移量處開始將各位元組讀取到指定的 byte 陣列中:
int read(byte[] b, int off, int len):返回讀取的位元組數。如果已到達流末尾,則返回 -1。
注:上述方法都丟擲了IO異常。
將資料寫入緩衝的輸出流的方法:
1)將指定的位元組寫入此緩衝的輸出流:
void write(int b):該方法丟擲了IO異常。
2)將 b.length
個位元組寫入此輸出流:
void write(byte[] b):該方法丟擲了IO異常。
3)將指定
byte 陣列中從偏移量 off
開始的 len
個位元組寫入此緩衝的輸出流:
void write(byte[] b,int off,int len):該方法丟擲了IO異常。
示例10:使用緩衝區技術完成對示例9的複製檔案操作。
import java.io.*;
class BufferedStreamDemo
{
public static void main(String[] args)
{
BufferedInputStream bufis=null;
BufferedOutputStream bufos=null;
try
{
//建立位元組流寫入緩衝區物件
bufos=new BufferedOutputStream(new FileOutputStream("E:\\heima\\2.png"));
//建立位元組流讀取緩衝區物件
bufis=new BufferedInputStream(new FileInputStream("E:\\heima\\1.png"));
int num=0;
while ((num=bufis.read())!=-1)
{
bufos.write(num);
}
}
catch (IOException e)
{
System.out.println(e.toString());
}
finally
{
if (bufis!=null)
{
try
{
bufis.close();
}
catch (IOException ex1)
{
throw new RuntimeException("讀取關閉失敗");
}
}
if (bufos!=null)
{
try
{
bufos.close();
}
catch (IOException ex1)
{
throw new RuntimeException("寫入關閉失敗");
}
}
}
}
}
程式執行後,複製影象檔案與被複製圖形檔案對比圖如下:
5.讀取鍵盤錄入:
示例11:通過鍵盤錄入,當錄入一行的資料後,就將該行資料轉換成大寫並進行列印。如果錄入的資料是over,那麼停止錄入。
/*
System.out:對應的標準輸出裝置,控制檯。
System.in:對應的標準輸入裝置,鍵盤。
*/
import java.io.*;
class ReadInDemo
{
public static void main(String[] args)
{
InputStream in=System.in;
StringBuilder sb=new StringBuilder();
try
{
while (true)
{
int ch=in.read();
if (ch=='\r')
{
continue;
}
else if (ch=='\n')
{
String s=sb.toString();
if (s.equals("over"))
{
break;
}
System.out.println(s.toUpperCase());
sb.delete(0,sb.length());
}
else
sb.append((char)ch);
}
}
catch (IOException ex)
{
throw new RuntimeException("發生異常");
}
finally
{
try
{
in.close();
}
catch (IOException e)
{
throw new RuntimeException("關閉失敗");
}
}
}
}
程式執行後的結果如下圖:
6.轉換流
轉換流包括兩個:InputStreamReader(位元組流通向字元流)和OutpuStreamWriter(字元流通向位元組流)。
示例12:通過轉換流完成對示例11的操作。
import java.io.*;
class TransStreamDemo
{
public static void main(String[] args)
{
BufferedReader bufr=null;
BufferedWriter bufw=null;
try
{
//將位元組流轉換成字元流輸入
bufr=new BufferedReader(new InputStreamReader(System.in));
//將字元流轉換成位元組流輸出
bufw=new BufferedWriter(new OutputStreamWriter(System.out));
String line=null;
while ((line=bufr.readLine())!=null)
{
if (line.equals("over"))
{
break;
}
bufw.write(line.toUpperCase()); //將讀取內容轉換成大寫,並寫入字元流寫入緩衝區
bufw.newLine(); //換行
bufw.flush(); //重新整理字元流寫入緩衝區的資料到控制檯
}
}
catch (IOException ex)
{
throw new RuntimeException("發生異常");
}
finally
{
if (bufr!=null)
{
try
{
bufr.close();
}
catch (IOException e)
{
throw new RuntimeException("讀取關閉失敗");
}
}
if (bufw!=null)
{
try
{
bufw.close();
}
catch (IOException e)
{
throw new RuntimeException("寫入關閉失敗");
}
}
}
}
}
程式執行後的結果如下圖:
五、自定義緩衝區
1.自定義字元流緩衝區:
字元流緩衝區提供了一個一次讀一行的方法readline,方便於對文字資料的讀取。當返回null時,表示讀到檔案末尾。
readLine方法的原理:無論是讀一行,獲取讀取的多個字元。其實最終都是在硬碟上一個一個讀取,所以最終使用的還是read方法一次讀一個的方法。
注:readLine方法返回的時候,只返回回車符之前的資料內容,並不返回回車符。
示例13:自定義字元流讀取緩衝區。
<pre name="code" class="java">import java.io.*;
class MyBufferedReaderDemo
{
public static void main(String[] args)
{
MyBufferedReader bufr=null;
try
{
//建立自定義字元流讀取緩衝區物件
bufr=new MyBufferedReader(new FileReader("E:\\heima\\BufferDemo.txt"));
String line=null;
while ((line=bufr.myReadLine())!=null)
{
System.out.println(line);
}
}
catch (IOException e)
{
throw new RuntimeException("檔案讀取失敗");
}
finally
{
if (bufr!=null)
{
try
{
bufr.myClose();
}
catch (IOException ex1)
{
throw new RuntimeException("讀取關閉失敗");
}
}
}
}
}
//自定義字元讀取流緩衝區
class MyBufferedReader
{
private FileReader r;
MyBufferedReader(FileReader r)
{
this.r=r;
}
//定義讀取行功能
public String myReadLine() throws IOException
{
StringBuilder sb=new StringBuilder();
int ch=0;
while ((ch=r.read())!=-1)
{
if (ch=='\r')
continue;
else if (ch=='\n')
return sb.toString();
else
sb.append((char)ch);
}
if (sb.length()!=0)
{
return sb.toString();
}
return null;
}
//定義關閉字元讀取流功能
public void myClose() throws IOException
{
r.close();
}
}
檔案BufferedDemo.txt內容截圖如下:
程式執行後的結果如下圖:
2.自定義位元組流緩衝區:
示例14:自定義位元組流讀取緩衝區。
import java.io.*;
class MyBufferedInputStreamDemo
{
public static void main(String[] args)
{
MyBufferedInputStream mybufis=null;
BufferedOutputStream bufos=null;
try
{
//建立自定義位元組流寫入緩衝區物件
bufos=new BufferedOutputStream(new FileOutputStream("E:\\heima\\2.mp3"));
//建立位元組流讀取緩衝區物件
mybufis=new MyBufferedInputStream(new FileInputStream("E:\\heima\\1.mp3"));
// byte[] by=new byte[1024];
int num=0;
while ((num=mybufis.myRead())!=-1)
{
//num是int型別,那麼寫入檔案的位元組數是不是4個位元組呢?不是的,因為write方法
//裡對int型別做了強制型別轉換,只保留了int型別的最後八位。所以還是隻寫入了
//一個位元組的資料。
bufos.write(num);
}
}
catch (IOException e)
{
throw new RuntimeException("檔案複製失敗");
}
finally
{
if (mybufis!=null)
{
try
{
mybufis.myClose();
}
catch (IOException ex1)
{
throw new RuntimeException("讀取關閉失敗");
}
}
if (bufos!=null)
{
try
{
bufos.close();
}
catch (IOException ex1)
{
throw new RuntimeException("寫入關閉失敗");
}
}
}
}
}
//自定義位元組流讀取緩衝區
class MyBufferedInputStream
{
private InputStream is;
private byte[] by=new byte[1024*4];
private int pos=0,count=0;
MyBufferedInputStream(InputStream is)
{
this.is=is;
}
//一次讀一個位元組,從緩衝區(位元組陣列)獲取
public int myRead() throws IOException
{
if (count==0)
{
//通過is物件獲取硬碟上的資料,並存儲在by陣列中
count=is.read(by);
pos=0;
}
if (count>0)
{
int b=by[pos];
pos++;
count--;
//為什麼要與上255?因為位元組流的位元組資料都是二進位制,如果讀取的一個位元組資料為1111-1111,即十進位制為-1,
//那麼提升為int型別時,還是-1,此時這個位元組資料的-1就會和迴圈讀取的判斷條件-1一致,導致迴圈停止,復
//制檔案失敗,所以需與上255,從而保證int型別的最後一個八位不變,前面三個八位都為0,此時就不會出現返回
//-1的現象。這也是返回型別不是byte而是int的原因所在。
return b&255;
}
return -1;
}
public void myClose() throws IOException
{
is.close();
}
}
程式執行後的截圖如下: