——- android教育訓練、java教育訓練、期待與您交流! ———-
IO流
一 初識IO流
IO: Input Output的縮寫
IO流的特點:
- IO流是用來處理裝置之間的資料傳輸
- java對資料的操作是通過流的方式
- java用于操作流的對象都在IO包中
- 流按操作資料分為兩種: 位元組流和字元流
- 流按流向分為: 輸入流, 輸出流
IO流常用基類:
- 位元組流的抽象基流:InputStream和OutputStream
- 字元流的抽象基流:Reader和Writer
注意:
—由着四個類派生出來的子類名稱都是以其父類名作為子類名的字尾
如:
—InputStream的子類 FileInputStream
—Reader的子類FileReader
二 字元流
一 字元流的寫入:
字元流兩個基類
——–Reader 和 Writer
既然IO流是用于操作資料的
那麼資料的最常見的展現形式是:檔案
我們從一個小例子入手:
//需求:在硬碟上建立一個檔案并寫入一些文字資料
import java.io.*;
class FileWriterDemo
{
public static void main(String[] args) throws Exception
{
//第一步 建立一個Filewriter對象,該對象一被初始化就必須要明确被操作的檔案
//而且該檔案會被建立到指定目錄下,如果該目錄下已有同名檔案,将被覆寫
//其實該步就是在明确資料要存放的目的地(要寫字 要先有紙)
FileWriter fw = new FileWriter("D:/demo.txt");
//第二步 調用write(),将字元串寫入到流中
fw.write("duang duang");
//重新整理流對象中的緩沖區中的資料
//将資料重新整理到目的地中去
fw.flush();
//關閉流資源 但是關閉之前會重新整理一次緩沖中的資料
//将資料刷到目的地中
//和flush的差別:flush重新整理後,流可以繼續使用;close 關閉了
fw.close();
}
}
運作結果:
總結
字元流的寫入規律:
- 第一步 建立一個Filewriter對象,該對象一被初始化就必須要明确被操作的檔案
- 第二步 調用write(),将字元串寫入到流中
- 重新整理流對象中的緩沖區中的資料,将資料刷到目的地中
- 關閉流close();
注意事項:
1.其實java自身不能寫入資料,而是調用系統内部方式完成資料的書寫,使用系統資源後,一定要關閉資源。
2.由于在建立對象時,需要指定建立檔案位置,如果指定的位置不存在,就會發生IOException異常,是以在整個步驟中,需要對IO異常進行try處理。
IO異常處理方式:
看下面的執行個體代碼:
/*
IO異常處理方式
*/
import java.io.*;
class FileWriterDemo2
{
public static void main(String[] args)
{
//在try外面定義 在裡面new
FileWriter fw = null;
try
{
fw = new FileWriter("v:/demo.txt");//沒有執行成功,建立對象失敗
fw.write("java");
}
catch(IOException e)
{
System.out.println(e.toString());
}
finally{
if(fw!=null)//流還存在,則關閉
try{
fw.close();
}
catch(IOException e)
{
System.out.println(e.toString());
}
}
}
}
二 字元流的讀取:
先看一個簡單的例子:
/*
FileReader 單字元讀取方式.
*/
import java.io.*;
class FileReaderDemo
{
public static void main(String[] args) throws Exception
{
//建立一個檔案讀取流對象 和指定名稱的檔案相關聯
//要保障該檔案是已經存在的,如果不存在,則異常
FileReader fr = new FileReader("D:/demo.txt");
//調用讀取流對象的read方法
//read 一次讀取一個字元,而且會自動往下讀
int ch =;
while((ch=fr.read())!=-)
{
System.out.println((char)ch);
}
}
fr.close();
}
}
運作結果:
讀取規律總結:
1. 在建立一個檔案時,如果目錄下有同名檔案将被覆寫。
2. 在讀取檔案時,必須保證該檔案已存在,否則出異常。
三 字元流讀寫練習:
需求: 文本的複制: 将c盤一個文本檔案複制到D盤
/*
将c盤一個文本檔案複制到Dpan
原理
其實就是将c盤下的檔案資料存數到D盤的一個檔案中
1 在D盤建立一個檔案,用于存儲C槽檔案中的資料
2 定義讀取流和C槽檔案關聯
3 通過不斷的讀寫完成資料的存儲
4 關閉資源
*/
import java.io.*;
class CopyText
{
public static void main(String[] args) throws IOException
{
//copy_2;
copy_1();
}
//第二種方式讀寫: 一行行讀寫
public static void copy_2()
{
FileWriter fw = null;
FileReader fr = null;
try
{
fw = new FileWriter("D:/demo1.txt");
fr = new FileReader("D:/demo.txt");
char [] buf = new char[];
int len = ;
while((len = fr.read(buf))!=-)
{
fw.write(buf,,len);
}
}
catch (IOException e)
{
throw new RuntimeException("讀寫失敗");
}
finally
{
if(fr!=null)
try
{
fr.close();
}
catch(IOException e)
{
}
if(fw!=null)
try
{
fw.close();
}
catch(IOException e)
{
}
}
}
//第一種方式讀寫:從C槽讀一個寫一個
public static void copy_1() throws IOException
{
//建立目的地
FileWriter fw = new FileWriter("D:/demo2.txt");
//與已有檔案關聯
FileReader fr = new FileReader("D:/demo.txt");
int ch = ;
while((ch=fr.read())!=-)
{
fw.write(ch);
}
fw.close();
fr.close();
}
}
運作結果:
三字元流緩沖區——BufferedReader和BufferedWriter
1.緩沖區的出現是為了提高流的操作效率而出現的
2.是以在建立緩沖區之前 必須先有流對象
3.該緩沖區中提供了一個跨平台的換行符 newLine();
使用BufferedWriter步驟:
- 建立一個字元寫入流對象。
- 為了提高字元寫入流效率。加入緩沖技術。隻要将需要被提高效率的流對象作為參數傳遞給緩沖區的構造函數即可。
- 調用write方法寫入資料到指定檔案
- 其實關閉緩沖區,就是在關閉緩沖區中的流對象。
如下面的示例代碼:
/*
緩沖區的出現是為了提高流的操作效率而出現的
是以在建立緩沖區之前 必須先有流對象
該緩沖區中提供了一個跨平台的換行符 newLine();
*/
import java.io.*;
class BufferedWriterDemo
{
public static void main(String[] args) throws IOException
{
//建立一個字元寫入流對象
FileWriter fw = new FileWriter("buf.txt");
//為了提高字元寫入流效率,加入了緩沖技術
//實際上是把數組封裝成對象
//隻要将需要被提高效率的流對象作為參數傳遞給緩沖區的構造函數即可
BufferedWriter bufw = new BufferedWriter(fw);
for(int i = ;i<;i++)
{
bufw.write("asdfg"+i);
bufw.newLine();
bufw.flush();
}
//隻要用到緩沖區 就一定要重新整理
//bufw.flush();
//關閉緩沖區就是關閉對應的流對象
bufw.close();
}
}
運作結果:
使用BufferedWriter步驟:
1.建立一個讀取流對象和檔案相關聯
2.了提高效率。加入緩沖技術。将字元讀取流對象作為參數傳遞給緩沖區對象的構造函數。
3.調用該緩沖區提供的readLine方法一行一行讀取,如果到達檔案末尾,則傳回null
4.關閉流資源
示例代碼:
/*
字元讀取緩沖區
該緩沖區提供了一個一次讀一行的方法readLine 友善對文本資料的擷取
當傳回null 時 表示讀到檔案末尾
readLine()方法傳回的時候隻傳回回車符之前的額資料内容,并不傳回回車符
*/
import java.io.*;
class BufferedReaderDemo
{
public static void main(String[] args) throws IOException
{
//建立一個讀取流對象和檔案相關聯
FileReader fr = new FileReader("buf.txt");
//加入緩沖區
BufferedReader bufr = new BufferedReader(fr);
String line = null;
while((line =bufr.readLine())!=null)
{
System.out.println(line);
}
bufr.close();
}
}
運作結果:
緩沖區手機實際應用的小練習
需求: 練習:通過緩沖區複制一個.java檔案
/*
練習:通過緩沖區複制一個.java檔案
*/
import java.io.*;
class CopyTextByBuf
{
public static void main(String[] args)
{
BufferedReader bufr = null;
BufferedWriter bufw = null;
try
{
bufr = new BufferedReader(new FileReader("BufferedReaderDemo.java"));
bufw = new BufferedWriter(new FileWriter("bufwiter_copy.txt"));
String line = null;
while((line = bufr.readLine())!=null)
{
bufw.write(line);
bufw.newLine();
bufw.flush();
}
}
catch(IOException e)
{
throw new RuntimeException("讀寫失敗");
}
finally
{
try
{
if(bufr!=null)
bufr.close();
}
catch(IOException e)
{
throw new RuntimeException("讀取關閉失敗");
}
try
{
if(bufw!=null)
bufw.close();
}
catch(IOException e)
{
throw new RuntimeException("寫入關閉失敗");
}
}
}
}
運作結果:
四 裝飾設計模式
1.裝飾類:
當想對已有對象進行功能增強時,可定義類:将已有對象傳入,基于已有對象的功能,并提供加強功能,那麼自定義的該類稱之為裝飾類。
2.裝飾類通常都會通過構造方法接收被裝飾的對象,并基于被裝飾的對象的功能,提供更強的功能。
3.裝飾和繼承的差別:
—1.裝飾模式比繼承要靈活。避免了繼承體系的臃腫,且降低了類與類之間的關系。
—2.裝飾類因為增強已有對象,具備的功能和已有的是相同的,隻不過提供了更強的功能,是以裝飾類和被裝飾的類通常都是屬于一個體系。
—3.從繼承結構轉為組合結構。
裝飾者模式設計範例:
/*
需求:根據readLine方法原理,模拟BufferedReader寫一個自己的MyBufferedReader
*/
import java.io.*;
//自定義緩沖類
class MyBufferedReader extends Reader
{
private Reader r;//定義接收的流對象
MyBufferedReader(Reader r)
{
this.r=r;
}
//自定義整行讀取
public String myReadLine()throws IOException
{
//建立一個容器,用來存儲一行的字元
StringBuilder sb =new StringBuilder();
//一個字元一個字元讀取
for (int ch=;(ch=r.read())!=- ; )
{
if(ch=='\r')//如果遇到換行符,則繼續
continue;
if(ch=='\n')//如果遇到回車符,表示該行讀取完畢
return sb.toString();
else
sb.append((char)ch);//将該行的字元添加到容器
}
if(sb.length()!=)//如果讀取結束,容器中還有字元,則傳回元素
return sb.toString();
return null;
}
//複寫父類中的抽象方法
public int read(char[] cbuf, int off, int len) throws IOException
{
return r.read(cbuf,off,len);
}
//複寫父類的close方法
public void close()throws IOException
{
r.close();
}
}
//測試MyBufferedReader
class MyBufferedReaderDemo
{
public static void main(String[] args)
{
MyBufferedReader mbr=null;
try
{
mbr=new MyBufferedReader(new FileReader("BufferedReaderDemo.java"));
for (String line=null;(line=mbr.myReadLine())!=null ; )
{
System.out.println(line);//顯示效果
}
}
catch (IOException e)
{
throw new RuntimeException("讀取資料失敗");
}
finally
{
try
{
if(mbr!=null)
mbr.close();
}
catch (IOException e)
{
throw new RuntimeException("讀取流關閉失敗");
}
}
}
}
運作結果:
小結:
在定義類的時候,不要以繼承為主;可通過裝飾設計模式進行增強類功能。靈活性較強,當裝飾類中的功能不适合,可再使用被裝飾類的功能。
上面講到的MyBufferedReader的例子就是最好的裝飾設計模式的例子。
五 位元組流
- 位元組流包括: InputStream 輸入流(讀) OutputStream 輸出流(寫)
- 位元組流和字元流的基本操作是相同的,但位元組流還可以操作其他媒體檔案。
- 由于媒體檔案資料中都是以位元組存儲的,是以,位元組流對象可直接對媒體檔案的資料寫入到檔案中,而可以不用再進行刷流動作。
示例代碼:
/*
練習: 複制一張圖檔
步驟
1 用位元組流讀取流對象和圖檔關聯
2 用位元組寫入流對象建立一個檔案,用于存儲擷取到的圖檔資料
3 通過循環讀寫 完成資料的存儲
4 關閉資源
*/
import java.io.*;
class CopyPic
{
public static void main(String[] args)
{
FileOutputStream fos = null;
FileInputStream fis =null;
try{
//指定複制的路勁
fos = new FileOutputStream("D:\\2.jpg");
//關聯要複制的圖檔
fis = new FileInputStream("D:\\1.jpg");
byte [] buf = new byte[];
int len = ;
while((len=fis.read(buf))!=-)
{
fos.write(buf,,len);
}
}
catch(IOException e)
{
throw new RuntimeException("讀取圖檔失敗");
}
finally
{
try
{
if(fis!=null)
fis.close();
}
catch(IOException e)
{
throw new RuntimeException("寫入失敗");
}
try
{
if(fos!=null)
fos.close();
}
catch(IOException e)
{
throw new RuntimeException("讀取失敗");
}
}
}
}
運作結果:
六 位元組流緩沖區
1.位元組流緩沖區的出現也是為了提高讀寫效率.
2.位元組流的讀寫原理特點:
- 将資料拷貝一部分,讀取一部分,循環,直到資料全部讀取完畢。
- 循環這個動作,直到最後取出一組資料存入數組,可能數組并未填滿,同樣也取出包含的元素。
- 每次取出的時候,都有一個指針在移動,取到數組結尾就自動回到數組頭部,這樣指針在自增。
- 取出的時候,數組中的元素在減少,取出一個,就減少一個,直到減到0即元素取完。
- 當檔案中的全部資料都被讀取出時,read()方法就傳回-1。
3.自定義讀取位元組流緩沖區
示例代碼:
/*
自定義位元組流讀取緩沖區
思路:
1、定義一個固定長度的數組
2、定義一個指針和計數器用于讀取數組長度,和計數數組元素是否取完為0
3、每次将位元組資料存入元素要先将數組中的元素取完
*/
import java.io.*;
class MyBufferedInputStream
{
private InputStream in;
private byte[] by=new byte[];
private int count=,pos=;
MyBufferedInputStream(InputStream in)
{
this.in=in;
}
//自定義讀方法,一次讀一個位元組
public int myRead()throws IOException
{
//通過in對象讀取硬碟上資料,并存儲by中。
//存儲在數組中的資料被讀取完,再通過in對象從硬碟上讀取資料
if(count==)
{
count=in.read(by);
if(count<)//檔案資料全部被讀取出來了
return-;
pos=;//初始化指針
byte b=by[pos];
count--;//每被讀一個位元組,表示數組中的位元組數少一個
pos++;//指針加1
return b&;//傳回的byte類型提升為int類型,位元組數增加,且高24位被補1,原位元組資料改變。
//通過與上255,主動将byte類型提升為int類型,将高24位補0,原位元組資料不變。
//而在輸出位元組流寫入資料時,隻寫該int類型資料的最低8位。
}
else if(count>) //如果數組中的資料沒被讀取完,則繼續讀取
{
byte b=by[pos];
count--;
pos++;
return b&;
}
return-;
}
//自定義關閉資源方法
public void close()throws IOException
{
in.close();
}
}
//測試自定義輸入位元組流緩沖區
class MyBufferedCopyMp3
{
public static void main(String[] args)
{
long start=System.currentTimeMillis();
//利用位元組流的緩沖區進行複制
copy_2();
long end=System.currentTimeMillis();
System.out.println("複制共用時:"+(end-start)+"毫秒");
}
//使用位元組流的緩沖區進行複制
public static void copy_2()
{
BufferedOutputStream bout=null;
MyBufferedInputStream bin=null;
try
{
//關聯複制檔案輸入流對象到緩沖區
bin=new MyBufferedInputStream(new FileInputStream("C:/Users/asus/Desktop/一樣的月光.mp3"));
//指定檔案粘貼位置的輸出流對象到緩沖區
bout=new BufferedOutputStream(new FileOutputStream("C:/Users/asus/Desktop/一樣的月光2.mp3"));
int by=;
while((by=bin.myRead())!=-)
{
bout.write(by);//将緩沖區中的資料寫入指定檔案中
}
}
catch(IOException e)
{
throw new RuntimeException("MP3複制失敗");
}
finally
{
try
{
if(bin!=null)
bin.close();//關閉輸入位元組流
}
catch(IOException e)
{
throw new RuntimeException("讀取位元組流關閉失敗");
}
try
{
if(bout!=null)
bout.close();//關閉輸出位元組流
}
catch(IOException e)
{
throw new RuntimeException("寫入位元組流關閉失敗");
}
}
}
}
注意事項:
1.位元組流的讀一個位元組的read方法為什麼傳回值類型不是byte,而是int。因為有可能會讀到連續8個二進制1的情況,8個二進制1對應的十進制是-1.那麼就會資料還沒有讀完,就結束的情況。因為我們判斷讀取結束是通過結尾标記-1來确定的。 是以,為了避免這種情況将讀到的位元組進行int類型的提升。并在保留原位元組資料的情況前面了補了24個0,變成了int類型的數值。而在寫入資料時,隻寫該int類型資料的最低8位。
2.byte類型的-1提升為int類型時還是-1。原因:因為在bit8個1前面補的全是1導緻的。如果在bit8個1前面補0,即可以保留原位元組資料不變,又可以避免-1的出現。這時将byte型資料&0xff即255即可。
七 流的操作規律總結
一 基本分析思路:
1.明确目的和源
- 源:鍵盤錄入。
- 目的:控制台。
2.明确需求: 想把鍵盤錄入的資料存儲到一個檔案中。
- 源:鍵盤
- 目的:檔案。
- 使用位元組流通向字元流的轉換流(橋梁):InputStreamReader
3.明确需求: 想要将一個檔案的資料列印在控制台上。
- 源:檔案
- 目的:控制台
- 使用字元流通向位元組流的轉換流(橋梁):OutputStreamWriter
二 基本操作規律:
三個明确:
- 明确源和目的。
- 源:輸入流。InputStream Reader
- 目的:輸出流。OutputStream Writer
- 明确操作的資料是否是純文字。
- 是:字元流
- 否:位元組流
- 當體系明确後,再明确要使用哪個具體的對象。通過裝置來進行區分:
- 源裝置:記憶體,硬碟,鍵盤
- 目的裝置:記憶體,硬碟,控制台
分析小例子:
1 . 需求: 将一個文本檔案中的資料存儲到另一個檔案中 複制檔案
- 源 因為是源 是以使用讀取流 InputStream Reader
- 是不是操作文本檔案?
- 是!這時選擇Reader
這樣體系就明确了
接下來明确該體系中的那個對象?
- 明确裝置:硬碟上的一個檔案
- Reader體系中可以操作檔案的對象 FileReader
- 是否需要提高效率 是 BufferedReader
- FileReader fr = new FileReader(“a.txt”);
-
目的:OutputStream Writer
是不是純文字
是!Writer
裝置:硬碟上的檔案
Writer中可以操作檔案的對象時FileWriter
是否需要提高效率 是 BufferedWriter
FileWriter fr = new FileWriter(“b.txt”);
代碼實作如下:
import java.io.*;
class InputStreamReaderDemo
{
public static void main(String[] args) throws IOException
{
// //擷取鍵盤錄入對象
// InputStream in = System.in;
//
// //将位元組流對象轉換成字元流對象 使用轉換流 InputStreamReader
// InputStreamReader isr = new InputStreamReader(in);
//
// //為了提高效率 将字元串進行緩沖區的技術高效操作 使用BufferedReader
//
// BufferedReader bufr = new BufferedReader(isr);
//把上面三句話變成一句話
BufferedReader bufr = new BufferedReader(new InputStreamReader(new FileInputStream("InputStreamReaderDemo.java")));//鍵盤錄入最常見寫法 記下來
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));
String line = null;
while((line=bufr.readLine())!=null)
{
if ("over".equals(line))
break;
bufw.write(line.toUpperCase());
bufw.newLine();
bufw.flush();
}
}
}
——- android教育訓練、java教育訓練、期待與您交流! ———-