----------------------
ASP.Net+Android+IO開發S、.Net培訓、期待與您交流! ----------------------
一、IO流概述
IO流即輸入(Input)輸出(Output)流,流是種形象的說法,計算機數據(字節或字符)的轉移都是大量、持續的,像水流般。輸入指將制定位置的數據讀入內存中,輸出指將內存數據寫到指定的位置上。那麼IO流是用來處理設備間的數據傳輸的。
在java中:
(1) 對數據的操作是通過流的方式。
(2) 用於操作流的對象都在IO包中。
(3) 流按操作數據分爲兩種:字節流與字符流
(4) 流按流向分爲:輸入流和輸出流。
IO流操作的異常處理:
由於IO流存在各方面的不確定,需要異常處理機制的支持,在常規處理時都建議採用捕獲方式,下面IO流知識點的例子,爲了突出知識點,簡化了異常的處理,採用拋出方式,以便能使代碼清晰些,常規處理應採用下列格式:
/*
IO異常的處理方式
*/
import java.io.*;
class IoExceptionDemo
{
public static void main(String[] args)
{
//在外面建立引用對象,在try中構造初始化
//colse也要求處理IO異常,所以也的進行try、
//即3個方法都要try處理
//如構造時拋出異常,將中斷,但會執行finally中的
//fw.close,因爲構造失敗fw=null,調用失敗
//需在前進行健壯性的判斷,if(fw != null)
FileWriter fw = null;
try
{
fw =new FileWriter("Demo.txt");
//write將內容寫入流緩存中,並未寫入文件中
fw.write("寫入文本內容");
//加flush刷新後,流中內容寫入文件中,但流並未關閉,可繼續寫
fw.flush();
}
catch (IOException e)
{
System.out.println(e.toString());
}
finally
{
try
{
//這步要有,安全判斷
//因fw =new FileWriter("Demo.txt");可能會失敗,比如在不存在的K盤下
if(fw != null)
fw.close(); //先刷新,後關閉流,不能再寫入數據了
}
catch (IOException e)
{
System.out.println(e.toString());
}
}
System.out.println("文件創建成功!");
}
}
二、字符流
在進行數據操作時,操作數據以字符爲單元,該類體系爲Reader和Writer。
1、 Writer和Reader
Writer爲寫入字符流的抽象類,其子類後綴名基本是Writer。其子類必須要實現父類的抽象方法爲write(char[], int,int)、flush() 和 close()。當我們向文件write數據時,數據僅存在緩衝中,需用flush刷新後數據才從緩衝寫入文件中。Close之前會進行一次緩衝的刷新。其實jvm是在調用系統內部的讀寫方法,因爲系統會不同,jvm如都用自已的方式寫入數據是不合適的。
Reader爲讀入字符流的抽象類,其子類後綴名基本是Reader,必須要複寫的方法有read(char[],int, int) 和 close()。
2、 BufferedWriter和BufferedReader
爲了提高對數據的讀寫效率,IO中定義了緩衝區,可以說是一個過渡區,將陸續的數據先寫入緩衝區積攢,而後一股腦寫入目標設備。就像我們寫word,寫一段後保存,而不是寫一個字保存一下。所以不建議採用,讀一字符,寫一字符的方式,因爲這使磁頭讀寫操作頻繁,建議先讀一次性入流中,再將流數據寫入目的文件中。 最後關閉讀寫流
特點:
①緩衝區的技術原理其實就是封裝了數組。
②它的出現是爲了提高流的操作效率存在的,所以在創建時,構造函數必須傳入一個流對象。
③調用close關閉緩衝區時,就會關閉緩衝區的流對象,所以關閉了緩衝區就不需要寫流對象關閉了。
④BufferedWriter和BufferedReader的類構造採用了裝飾設計模式,對流的操作類進行了功能的加強,至於裝飾模式,將在以後介紹。
BufferedWriter:字符流輸出的緩衝區,提供了一個跨平臺的換行方法newLine(),解決在不同系統中換行符不統一的問題。在向目標輸出前記得先flush()刷新。
BufferedReader:字符流輸入的緩衝區,同樣提供了一個readLine方法,從文件中讀取一行數據,返回給String,當讀到文件尾時返回空,其本質還是用read一個一個字符讀取的,只不過積攢一行返回。該類有個LineNumberReader子類,比BufferedReader多出行號的追蹤功能。方法有setlineNumber();設置行號getReadLineNumber();讀出行號
3、 InputStreamReader和OutputStreamWriter
轉換流:由於某些需求,需要在字符流和字節流間互相轉換,java便將該操作封裝成類,以供需求,如他們的子類FileWriter和FileReader,我們直接可以用其子類進行字符的操作,而不必去考慮複雜的字節。
InputStreamReader爲字節流轉換成字符流,這樣便可利用字節流的BufferedReader緩衝區操作提高效率,要求在創建時向構造函數傳入InputStream的字節流對象,也可以用構造函數的Charset參數指定編碼格式。InputStreamReader構造函數中傳入System.in流參數,便可接手鍵盤錄入數據。
OutputStreamWriter字符流轉換成字節流,同理,只不過參數輸入改輸出,如接收System.out打印在控制檯。
4、 FileWriter與FileReader
他們分別屬於InputStreamReader和OutputStreamWriter的子類,專門對字符數據的文件進行操作。可接收路徑字串和File對象等。
FileWriter:用來寫入字符文件的便捷類,有默認的字符編碼和緩衝區。可進行對文本的寫入和文本的追加,追加文本的構造函數只要將參數2用上,初始成true即可,寫入前需刷新。創建對象時,該類會打開指定文件,若文件已打開則創建失敗。
FileReader:用於讀取字符文件的便捷類,同樣具有默認編碼和緩衝區。由read()讀取單個字符,返回一個整數,讀到文件尾返回-1。
IO流操作會拋出異常,需處理,以應對文件已打開或文件不存在等異常,異常處理稍後介紹。下面是1、2、3的一個綜合例子:將鍵盤錄入,寫入文件中。
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
/*
* 需求:將鍵盤錄入寫入文件中
*
* 說明這裏不重點體現異常處理,所以簡單的拋出了
* 正規開發,建議用try進行捕獲
* */
public class CopyText
{
//由於異常處理不是這裏要體現的,所以採取拋出異常,正常編碼建議捕捉
public static voidmain(String[] args) throws IOException
{
//標準的鍵盤錄入方式,將輸入轉字符交給緩衝區。
BufferedReader bufr= new BufferedReader(new InputStreamReader(System.in));
//FileWriter讀取字符文件,交給緩衝,會拋出異常,簡單拋出了,平時建議捕捉
BufferedWriter bufw= new BufferedWriter(new FileWriter("Test.txt"));
String strLine =null;
while((strLine =(bufr.readLine()))!=null)
{
//輸入over時,結束
if("over".equals(strLine))
break;
//寫入文本
bufw.write(strLine);
bufw.newLine();//換行
bufw.flush();//刷新
}
bufr.close();
bufw.close();
}
}
5、 CharArrayReader和CharArrayWriter
提供在緩衝區和內存間的操作方式,用於操作字符數組,內部擁有可自動延長的內存空間,由於是操作內存,最後不需關閉。
CharArrayReader,需接收一個字符數組型的數據源,將內存的數據以數組的方式存入傳入的數組源中,無需關閉。
CharArrayWriter,同理。
6、 PrintWriter
向文本輸出流打印對象的格式化表示形式,爲其他字符流類添加了輸出功能,在構建時,將構造函數參數2置true,還可帶自動刷新功能,利用println()能實現換行,等於代替了上述代碼中的 bufw.write(strLine);bufw.newLine();bufw.flush();三句。
構造函數能接收的文件源可以是,File對象,字符串路徑,字節輸出流。
7、 PipedWriter和PipedReader
管道流,它具有將一個程序的輸出當做另一個程序的輸入的能力,這個涉及線程,也就是我們線程通信的另一種方式,利用管道流通信,它需要利用connect將兩者連接,即:讀.connect(寫)。
import java.io.IOException;
import java.io.PipedReader;
import java.io.PipedWriter;
/*
* 字符管道流的測試*/
class Read implements Runnable
{
private PipedReader pr;
Read(PipedReader pr)
{
this.pr = pr;
}
public void run()
{
try
{
//構建接收數組
char[] chs = new char[1024];
//讀取連接的寫入管道流,多句用循環
int len = pr.read(chs);
System.out.println(newString(chs,0,len));
pr.close();
}catch(Exception e)
{
throw new RuntimeException("讀取失敗!");
}
}
}
class Write implements Runnable
{
private PipedWriter pw;
Write(PipedWriter pw)
{
this.pw = pw;
}
public void run()
{
try
{
//直接寫出
pw.write("這是管道流數據!");
//多句要pw.flush()刷新,這裏再close前自動刷新了
pw.close();
}catch(Exception e)
{
throw new RuntimeException("寫入失敗!");
}
}
}
public class PipedDemo
{
public static void main(String[] args) throws IOException
{
PipedWriter pw = new PipedWriter();
PipedReader pr = new PipedReader();
//需連接,讀的連接寫的
pr.connect(pw);
new Thread(new Read(pr)).start();
new Thread(new Write(pw)).start();
}
}
8、RandomAccessFile
該類不算是IO體系中子類,而是直接繼承自Object。隨機訪問文件,自身具備讀寫方法。通過skipBytes(int x),seek(int x)來達到隨機訪問,即含有文件指針,可跳躍。
但是它是java.IO包中成員,因爲它具備讀和寫功能,
它內部封裝了一個數組,而且通過指針對數組的元素進行操作。可以通過getFilePointer獲取指針位置。同時可以通過seek改變指針的位置。
其實完成讀寫的原理就是內部封裝了字節輸入流和輸出流。
其構造函數只能接收文件對象,而且可以指定模式,如’r’,只讀。
侷限性:通過構造函數可已看出,該類只能操作文件。而且操作文件要用模式。
三、字節流
在進行數據操作時,操作數據以字節爲單元,該類體系爲InputStream和OutputStream,字節流的輸出是直接從內存寫入,不需要轉字符的過程,因此沒有字符流刷新的過程。
1、InputStream和OutputStream
InputStream爲寫出字節流的抽象類,其子類後綴名基本是InputStream。提供read()方法。
OutputStream爲讀入字節流的抽象類,其子類後綴名基本爲OutputStream。提供write()方法。
2、FileOutputStream和FileInputStream
分別爲InputStream和OutputStream的子類,複寫了read()方法,主要用於讀取諸如圖像數據之類的原始字節流。沒有緩衝區,自己構建byte類型的數組。
FileOutputStream:以字節流的形式輸出到文件中,複寫了write方法,一次寫入一個字節,若要已向文件結尾處追加字節,在創建時的構造函數中初始第二個參數爲true。
FileInputStream:將字節流讀入到內存中,複寫了read方法,每次只能讀一個字節,返回一個int型數據,當讀到文件尾時返回-1。他有個特有的方法available(),返回文件剩餘的字節數。如果文件較小,可以先讀出大小,聲明足夠的byte數組,不用循環,但是文件不能過大,虛擬機不可能開闢很大的內存空間,比如視頻文件,new一個數組不會成功,所以應視情況使用available()。
其格式類似與FileWriter與FileReader,在這裏不寫例子了,稍作改動即可,將字符讀入改爲字節流的byte,不用刷新等。
3、ByteArrayOutputStream與ByteArrayInputStream
與字符流的CharArrayReader和CharArrayWriter同理,只不過數據源爲byte數組。
4、PipedOutputStream與PipedInputStream
與字符流的PipedReader和PipedWriter同理,只不過一切按字節流的操作方式。
5、ObjectInputStream與ObjectOutputStream
對象序列化流,用於對象的序列化(持久化),即能將對象存入硬盤中,保存對象和對象數據。由ObjectOutputStream將java對象的基本數據類型和圖形寫入OutputStream(流)中,使用ObjectOutputStream可讀取重構該對象。被操作的對象所屬類必須實現Serializable接口,該接口沒有要複寫的方法,我們叫這種類型的接口爲標記接口。實現該接口,其實就是給每給要保存的類一個ID號,簡稱UID,若要保存對象的類不實現該接口使用對象序列化流,將會報NotserializableException異常。
默認對象序列化後,不能修改其類,因爲序列化文件中記錄的UID變化了,使用時或報錯,爲防止修改類時,保存文件變化UID而導致不可用,我們採用自定義UID,
如public static final long srrialVersionUID = 42L;
靜態成員不會被序列化,他再方法區,而對象在堆中。若不想某成員序列化,加tansient修飾該成員。
這兩個類必須成對使用,用ObjectOutputStream寫,就用ObjectInputStream。
如下例:
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class ObjectStreamDemo
{
public static void main(String[] args) throws Exception
{
writeObject();
readObject();
}
public static void writeObject() throwsException
{
//這裏爲簡化代碼,異常省略捕捉處理
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("obj.txt"));
//寫入一個Person對象
oos.writeObject(new Person("張三"));
oos.close();
}
public static void readObject() throws Exception
{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj.txt"));
//從文件中讀取序列化的對象
Person p = (Person)ois.readObject();
System.out.println(p);
ois.close();
}
}
//要序列化的對象類,必須向實現Serializable
classPerson implements Serializable
{
//自定義一個標記號,防止修改二影響序列化的對象不可用。
public static final long serialVersionUID =42L;
private String name=null;
Person(String name)
{
this.name = name;
}
public String toString()
{
return "姓名:" + name;
}
}
6、SequenceInputStream
SequenceInputStream對多個流進行合併。從第一個流,讀完,再接着讀取第二流,依次下去。可以將多個流的操作數據寫入到一個目標中。比如將三個文件的數據寫入到一個文件中。構造函數中傳入Enumeration對象,由Enumeration保存多個流對象。Enumeration時舊版本中Vector的類型。
先準備三個文件1.txt,2.txt,3.txt,將內容合併到4.txt中如下例子:
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.SequenceInputStream;
import java.util.Enumeration;
import java.util.Vector;
public class SequenceInputStreamDemo
{
//這裏爲代碼簡練省略捕捉處理異常
public static void main(String[] args)throws IOException
{
//由於SequenceInputStream的構造函數要傳Enumeration類型,這裏用Vector
Vector<FileInputStream> vct = new Vector<FileInputStream>();
//準備合併的三個文件
vct.add(new FileInputStream("1.txt"));
vct.add(new FileInputStream("2.txt"));
vct.add(new FileInputStream("3.txt"));
//獲取三個文件的流對象
Enumeration<FileInputStream> en =vct.elements();
//合併
SequenceInputStream sis = new SequenceInputStream(en);
//合併後寫入另一個文件
FileOutputStream fos = new FileOutputStream("4.txt");
byte[] buf = new byte[1024];
int len = 0;
while((len = sis.read(buf))!=-1)
{
fos.write(buf,0,len);
}
fos.close();
sis.close();
}
}
切割文件:
利用自定義byte[]數組限制讀入大小,將文件分批存入多個新建的文件中。
但是在切割較大的視頻的文件時,就不能new過大的byte數組了,
應定義1024*1024(1M)大小的數組向一文件存入數據,再採用一計數器記錄裝入了多少數據,達到要求時,再新建一文件,繼續裝入下一個。無太多技術要求,純算法,這裏不再舉例。
8、 FilterInputStream與FilterInputStream的子類
(1) BufferedInputStream與BufferedInputStream
功能與字符流的BufferedReader與BufferedWriter緩衝區類似,早構造時可以指定緩衝大小
下面寫一個拷貝圖片的例子:
import java.io.*;
public class CopyPicDemo
{
//簡化代碼,沒做異常的捕捉
public static void main(String[] args) throws IOException
{
//字節流緩衝區
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("1.jpg"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("2.jpg"));
int by = 0;;
while((by = bis.read())!=-1)
{
bos.write(by);
}
bis.close();
bos.close();
}
}
(2) DataOutputStream與DataIntputStream
可以對基本數據類型輸入輸出,構造時需傳入流對象,DataOutputStream使用writeInt(int)、writeBoolen(bool)等方法向文本寫入基本數據類型,用DataIntputStream的readInt()、readBoolean()等方法讀出,有嚴格的字節順序,讀錯順序後面的數據將不能正常讀出。
(3) PrintStream
類似於字符流的PrintWriter類。它爲其他輸出流添加了功能,使它們能夠方便地打印各種數據值表示形式。與其他輸出流不同,
PrintStream
永遠不會拋出IOException
;而是,異常情況僅設置可通過checkError
方法測試的內部標誌,通過checkError()方法獲得。
----------------------
ASP.Net+Android+IO開發S、.Net培訓、期待與您交流! ----------------------