InputStream和OutputStream
1.InputStream
◇ 從流中讀取資料:
int read( ); //讀取一個位元組,傳回值為所讀的位元組
int read( byte b[ ] ); //讀取多個位元組,放置到位元組數組b中,通常
//讀取的位元組數量為b的長度,傳回值為實際
//讀取的位元組的數量
int read( byte b[ ], int off, int len ); //讀取len個位元組,放置
//到以下标off開始位元組
//數組b中,傳回值為實
//際讀取的位元組的數量
int available( ); //傳回值為流中尚未讀取的位元組的數量
long skip( long n ); //讀指針跳過n個位元組不讀,傳回值為實際
//跳過的位元組數量
◇ 關閉流:
close( ); //流操作完畢後必須關閉
◇ 使用輸入流中的标記:
void mark( int readlimit ); //記錄目前讀指針所在位置,readlimit
//表示讀指針讀出readlimit個位元組後
//所标記的指針位置才失效
void reset( ); //把讀指針重新指向用mark方法所記錄的位置
boolean markSupported( ); //目前的流是否支援讀指針的記錄功能
有關每個方法的使用,詳見java API。
2.OutputStream
◇ 輸出資料:
void write( int b ); //往流中寫一個位元組b
void write( byte b[ ] ); //往流中寫一個位元組數組b
void write( byte b[ ], int off, int len ); //把位元組數組b中從
//下标off開始,長度為len的位元組寫入流中
◇ flush( ) //刷空輸出流,并輸出所有被緩存的位元組
由于某些流支援緩存功能,該方法将把緩存中所有内容強制輸出到流中。
◇ 關閉流:
close( ); //流操作完畢後必須關閉
4.4.3 I/O中的例外
進行I/O操作時可能會産生I/O例外,屬于非運作時例外,應該在程式中處理。如:FileNotFoundException,EOFException, IOException
4.5檔案處理
I/O進行中,最常見的是對檔案的操作,java.io包中有關檔案處理的類有:File、FileInputStream、FileOutputStream、RamdomAccessFile和FileDescriptor;接口有:FilenameFilter。
4.5.1 檔案描述
類File提供了一種與機器無關的方式來描述一個檔案對象的屬性。下面我們介紹類File中提供的各種方法。
◇ 檔案或目錄的生成
public File(String path);
public File(String path,String name);//path是路徑名,name是檔案名
public File(File dir,String name);//dir是路徑名,name是檔案名
◇ 檔案名的處理
String getName( ); //得到一個檔案的名稱(不包括路徑)
String getPath( ); //得到一個檔案的路徑名
String getAbsolutePath( );//得到一個檔案的絕對路徑名
String getParent( ); //得到一個檔案的上一級目錄名
String renameTo(File newName); //将目前檔案名更名為給定檔案的
完整路徑
◇ 檔案屬性測試
boolean exists( ); //測試目前File對象所訓示的檔案是否存在
boolean canWrite( );//測試目前檔案是否可寫
boolean canRead( );//測試目前檔案是否可讀
boolean isFile( ); //測試目前檔案是否是檔案(不是目錄)
boolean isDirectory( ); //測試目前檔案是否是目錄
◇ 普通檔案資訊和工具
long lastModified( );//得到檔案最近一次修改的時間
long length( ); //得到檔案的長度,以位元組為機關
boolean delete( ); //删除目前檔案
◇ 目錄操作
boolean mkdir( ); //根據目前對象生成一個由該對象指定的路徑
String list( ); //列出目前目錄下的檔案
【例4-3】
import java.io.*; //引入java.io包中所有的類
public class FileFilterTest{
public static void main(String args[]){
File dir=new File("d://ex"); //用File對象表示一個目錄
Filter filter=new Filter("java"); //生成一個名為java的過濾器
System.out.println("list java files in directory "+dir);
String files[]=dir.list(filter); //列出目錄dir下,檔案字尾名
為java的所有檔案
for(int i=0;i<files.length;i++){
File f=new File(dir,files[i]); //為目錄dir下的檔案或目錄
建立一個File 對象
if(f.isFile()) //如果該對象為字尾為java的檔案,
則列印檔案名
System.out.println("file "+f);
else
System.out.println("sub directory "+f ); //如果是目錄
則列印目錄名
}
}
}
class Filter implements FilenameFilter{
String extent;
Filter(String extent){
this.extent=extent;
}
public boolean accept(File dir,String name){
return name.endsWith("."+extent); //傳回檔案的字尾名
}
}
4.5.2檔案的順序處理
類FileInputStream和FileOutputStream用來進行檔案I/O處理,由它們所提供的方法可以打開本地主機上的檔案,并進行順序的讀/寫。例如,下列的語句段是順序讀取檔案名為text的檔案裡的内容,并顯示在控制台上面,直到檔案結束為止。
FileInputStream fis;
try{
fis = new FileInputStream( "text" );
System.out.print( "content of text is : ");
int b;
while( (b=fis.read())!=-1 ) //順序讀取檔案text裡的内容并指派
給整型變量b,直到檔案結束為止。
{
System.out.print( (char)b );
}
}catch( FileNotFoundException e ){
System.out.println( e );
}catch( IOException e ){
System.out.println( e );
}
4.5.3 随機通路檔案
對于InputStream 和OutputStream來說,它們的執行個體都是順序通路流,也就是說,隻能對檔案進行順序地讀/寫。随機通路檔案則允許對檔案内容進行随機讀/寫。在java中,類RandomAccessFile提供了随機通路檔案的方法。類RandomAccessFile的聲明為:
public class RandomAccessFile extends Object implements DataInput,DataOutput
接口DataInput中定義的方法主要包括從流中讀取基本類型的資料、讀取一行資料、或者讀取指定長度的位元組數。如:readBoolean()、readInt( )、readLine( )、readFully( ) 等。
接口DataOutput 中定義的方法主要是向流中寫入基本類型的資料、或者寫入一定長度的位元組數組。如:writeChar()、writeDouble( )、write( ) 等。 下面詳細介紹RandomAccessFile類中的方法。
◇ 構造方法:
RandomAccessFile(String name,String mode); //name是檔案名,mode
//是打開方式,例如"r"表示隻讀,"rw"表示可讀寫,"
RandomAccessFile(File file,String mode); //file是檔案對象
◇ 檔案指針的操作
long getFilePointer( ); //用于得到目前的檔案指針
void seek( long pos ); //用于移動檔案指針到指定的位置
int skipBytes( int n ); //使檔案指針向前移動指定的n個位元組
4.6過濾流
過濾流在讀/寫資料的同時可以對資料進行處理,它提供了同步機制,使得某一時刻隻有一個線程可以通路一個I/O流,以防止多個線程同時對一個I/O流進行操作所帶來的意想不到的結果。類FilterInputStream和FilterOutputStream分别作為所有過濾輸入流和輸出流的父類
過濾流類層次:
java.lang.Object
|
+----java.io.InputStream
|
+----java.io.FilterInputStream
為了使用一個過濾流,必須首先把過濾流連接配接到某個輸入/出流上,通常通過在構造方法的參數中指定所要連接配接的輸入/出流來實作。例如:
FilterInputStream( InputStream in );
FilterOutputStream( OutputStream out );
4.6.1 幾種常見的過濾流
◇BufferedInputStream和BufferedOutputStream
緩沖流,用于提高輸入/輸出處理的效率。
◇ DataInputStream 和 DataOutputStream
不僅能讀/寫資料流,而且能讀/寫各種的java語言的基本類型,如:boolean,int,float等。
◇ LineNumberInputStream
除了提供對輸入處理的支援外,LineNumberInputStream可以記錄目前的行号。
◇ PushbackInputStream
提供了一個方法可以把剛讀過的位元組退回到輸入流中,以便重新再讀一遍。
◇ PrintStream
列印流的作用是把Java語言的内構類型以其字元表示形式送到相應的輸出流。
4.7字元流的處理
java中提供了處理以16位的Unicode碼表示的字元流的類,即以Reader和Writer 為基類派生出的一系列類。
4.7.1Reader和Writer
這兩個類是抽象類,隻是提供了一系列用于字元流處理的接口,不能生成這兩個類的執行個體,隻能通過使用由它們派生出來的子類對象來處理字元流。
1.Reader類是處理所有字元流輸入類的父類。
◇ 讀取字元
public int read() throws IOException; //讀取一個字元,傳回值為讀取的字元
public int read(char cbuf[]) throws IOException;
public abstract int read(char cbuf[],int off,int len) throwsIOException;
◇ 标記流
public boolean markSupported(); //判斷目前流是否支援做标記
public void mark(int readAheadLimit) throws IOException;
//給目前流作标記,最多支援readAheadLimit個字元的回溯。
public void reset() throws IOException; //将目前流重置到做标記處
◇ 關閉流
public abstract void close() throws IOException;
2. Writer類是處理所有字元流輸出類的父類。
◇ 向輸出流寫入字元
public void write(int c) throws IOException;
//将整型值c的低16位寫入輸出流
public void write(char cbuf[]) throws IOException;
//将字元數組cbuf[]寫入輸出流
public abstract void write(char cbuf[],int off,int len) throwsIOException;
//将字元數組cbuf[]中的從索引為off的位置處開始的len個字元寫入輸出流
public void write(String str) throws IOException;
//将字元串str中的字元寫入輸出流
public void write(String str,int off,int len) throwsIOException;
//将字元串str 中從索引off開始處的len個字元寫入輸出流
◇ flush( )
刷空輸出流,并輸出所有被緩存的位元組。
◇ 關閉流
public abstract void close() throws IOException;
4.7.2InputStreamReader和OutputStreamWriter
java.io包中用于處理字元流的最基本的類,用來在位元組流和字元流之間作為中介。
◇ 生成流對象
publicInputStreamReader(InputStream in);
public OutputStreamWriter(OutputStream out);
public OutputStreamWriter(OutputStream out,String enc) throwsUnsupportedEncodingException; //enc是編碼方式
InputStreamReader和OutputStreamWriter的方法:
◇ 讀入和寫出字元
基本同Reader和Writer。
◇ 擷取目前編碼方式
public String getEncoding();
◇ 關閉流
public void close() throws IOException;
4.7.3BufferedReader和BufferedWriter
◇ 生成流對象
public BufferedReader(Reader in); //使用預設的緩沖區大小
public BufferedReader(Reader in, int sz); //sz為緩沖區的大小
public BufferedWriter(Writer out);
public BufferedWriter(Writer out, int sz);
◇ 讀入/寫出字元
除了Reader和Writer中提供的基本的讀寫方法外,增加對整行字元的處理。
public String readLine() throws IOException; //讀一行字元
public void newLine() throws IOException; //寫一行字元
【例4-4】
import java.io.*;
public class NumberInput{
public static void main(String args[]){
try{
InputStreamReader ir;
BufferedReader in;
ir=new InputStreamReader(System.in);
//從鍵盤接收了一個字元串的輸入,并建立了一個字元輸入流的對象
in=new BufferedReader(ir);
String s=in.readLine();
//從輸入流in中讀入一行,并将讀取的值指派給字元串變量s
System.out.println("Input value is: "+s);
int i = Integer.parseInt(s);//轉換成int型
i*=2;
System.out.println("Input value changed after doubled: "+i);
}catch(IOException e)
{System.out.println(e);}
}
}
運作結果
D:\>java NumberInput 123 Input value is 123 Input value changed after doubled: 246 |
D:\>java NumberInput
123
Input value is 123
Input value changed after doubled: 246
注意:在讀取字元流時,如果不是來自于本地的,比如說來自于網絡上某處的與本地編碼方式不同的機器,那麼我們在構造輸入流時就不能簡單地使用本地預設的編碼方式,否則讀出的字元就不正确;為了正确地讀出異種機上的字元,我們應該使用下述方式構造輸入流對象:
ir = new InputStreamReader(is, "8859_1");
采用ISO 8859_1編碼方式,這是一種映射到ASCII碼的編碼方式,可以在不同平台之間正确轉換字元。
4.8對象的串行化(Serialization) 4.8.1 串行化的定義 1. 什麼是串行化 對象的壽命通常随着生成該對象的程式的終止而終止。有時候,可能需要将對象的狀态儲存下來,在需要時再将對象恢複。我們把對象的這種能記錄自己的狀态以便将來再生的能力,叫做對象的持續性(persistence)。對象通過寫出描述自己狀态的數值來記錄自己,這個過程叫對象的串行化(Serialization)。 2. 串行化的目的 串行化的目的是為java的運作環境提供一組特性,其主要任務是寫出對象執行個體變量的數值。 4.8.2 串行化方法 在java.io包中,接口Serializable用來作為實作對象串行化的工具,隻有實作了Serializable的類的對象才可以被串行化。 1. 定義一個可串行化對象 public class Student implements Serializable{ int id; //學号 String name; //姓名 int age; //年齡 String department //系别 public Student(int id,String name,int age,String department){ this.id = id; this.name = name; this.age = age; this.department = department; } } 2. 構造對象的輸入/輸出流 要串行化一個對象,必須與一定的對象輸入/輸出流聯系起來,通過對象輸出流将對象狀态儲存下來,再通過對象輸入流将對象狀态恢複。 java.io包中,提供了ObjectInputStream和ObjectOutputStream将資料流功能擴充至可讀寫對象。在ObjectInputStream中用readObject()方法可以直接讀取一個對象,ObjectOutputStream中用writeObject()方法可以直接将對象儲存到輸出流中。 Student stu=new Student(981036,"Liu Ming",18, "CSD"); FileOutputStream fo=new FileOutputStream("data.ser"); //儲存對象的狀态 ObjectOutputStream so=new ObjectOutputStream(fo); try{ so.writeObject(stu); so.close(); }catch(IOException e ) {System.out.println(e);} FileInputStream fi=new FileInputStream("data.ser"); ObjectInputStream si=new ObjectInputStream(fi); //恢複對象的狀态 try{ stu=(Student)si.readObject(); si.close(); }catch(IOException e ) {System.out.println(e);} 在 這個例子中,我們首先定義一個類Student,實作了Serializable接口,然後通過對象輸出流的writeObject()方法将Student對象儲存到檔案data.ser中。之後,通過對象輸入流的readObject()方法從檔案data.ser中讀出儲存下來的Student對象。 4.8.3 串行化的注意事項 1.串行化能儲存的元素 隻能儲存對象的非靜态成員變量,不能儲存任何的成員方法和靜态的成員變量,而且串行化儲存的隻是變量的值,對于變量的任何修飾符,都不能儲存。 2.transient關鍵字 對于某些類型的對象,其狀态是瞬時的,這樣的對象是無法儲存其狀态的,例如一個Thread對象,或一個FileInputStream對象,對于這些字段,我們必須用transient關鍵字标明 3. 定制串行化 預設的串行化機制,對象串行化首先寫入類資料和類字段的資訊,然後按照名稱的上升排列順序寫入其數值。如果想自己明确地控制這些數值的寫入順序和寫入種類,必須定義自己的讀取資料流的方式。就是在類的定義中重寫writeObject()和readObject()方法。 例如可在4.8.2的例子中,加入重寫的writeObject()和readObject()方法,對Student類定制其串行化。 private void writeObject(ObjectOutputStream out)throwsIOException { out.writeInt(id); out.writeInt(age); out.writeUTF(name); out.writeUTF(department); } private void readObject(ObjectInputStream in)throwsIOException { id=in.readInt(); age=in.readInt(); name=in.readUTF(); department=in.readUTF(); } |
4.8對象的串行化(Serialization)
4.8.1 串行化的定義
1. 什麼是串行化
對象的壽命通常随着生成該對象的程式的終止而終止。有時候,可能需要将對象的狀态儲存下來,在需要時再将對象恢複。我們把對象的這種能記錄自己的狀态以便将來再生的能力,叫做對象的持續性(persistence)。對象通過寫出描述自己狀态的數值來記錄自己,這個過程叫對象的串行化(Serialization)。
2. 串行化的目的
串行化的目的是為java的運作環境提供一組特性,其主要任務是寫出對象執行個體變量的數值。
4.8.2 串行化方法
在java.io包中,接口Serializable用來作為實作對象串行化的工具,隻有實作了Serializable的類的對象才可以被串行化。
1. 定義一個可串行化對象
public class Student implements Serializable{
int id; //學号
String name; //姓名
int age; //年齡
String department //系别
public Student(int id,String name,int age,String department){
this.id = id;
this.name = name;
this.age = age;
this.department = department;
}
}
2. 構造對象的輸入/輸出流
要串行化一個對象,必須與一定的對象輸入/輸出流聯系起來,通過對象輸出流将對象狀态儲存下來,再通過對象輸入流将對象狀态恢複。
java.io包中,提供了ObjectInputStream和ObjectOutputStream将資料流功能擴充至可讀寫對象。在ObjectInputStream中用readObject()方法可以直接讀取一個對象,ObjectOutputStream中用writeObject()方法可以直接将對象儲存到輸出流中。
Student stu=new Student(981036,"Liu Ming",18, "CSD");
FileOutputStream fo=new FileOutputStream("data.ser");
//儲存對象的狀态
ObjectOutputStream so=new ObjectOutputStream(fo);
try{
so.writeObject(stu);
so.close();
}catch(IOException e )
{System.out.println(e);}
FileInputStream fi=new FileInputStream("data.ser");
ObjectInputStream si=new ObjectInputStream(fi);
//恢複對象的狀态
try{
stu=(Student)si.readObject();
si.close();
}catch(IOException e )
{System.out.println(e);}
在 這個例子中,我們首先定義一個類Student,實作了Serializable接口,然後通過對象輸出流的writeObject()方法将Student對象儲存到檔案data.ser中。之後,通過對象輸入流的readObject()方法從檔案data.ser中讀出儲存下來的Student對象。
4.8.3 串行化的注意事項
1.串行化能儲存的元素
隻能儲存對象的非靜态成員變量,不能儲存任何的成員方法和靜态的成員變量,而且串行化儲存的隻是變量的值,對于變量的任何修飾符,都不能儲存。
2.transient關鍵字
對于某些類型的對象,其狀态是瞬時的,這樣的對象是無法儲存其狀态的,例如一個Thread對象,或一個FileInputStream對象,對于這些字段,我們必須用transient關鍵字标明
3. 定制串行化
預設的串行化機制,對象串行化首先寫入類資料和類字段的資訊,然後按照名稱的上升排列順序寫入其數值。如果想自己明确地控制這些數值的寫入順序和寫入種類,必須定義自己的讀取資料流的方式。就是在類的定義中重寫writeObject()和readObject()方法。
例如可在4.8.2的例子中,加入重寫的writeObject()和readObject()方法,對Student類定制其串行化。
private void writeObject(ObjectOutputStream out)throwsIOException
{
out.writeInt(id);
out.writeInt(age);
out.writeUTF(name);
out.writeUTF(department);
}
private void readObject(ObjectInputStream in)throwsIOException
{
id=in.readInt();
age=in.readInt();
name=in.readUTF();
department=in.readUTF();
}
4.9其它常用的流
4.9.1 管道流
管道用來把一個程式、線程或代碼塊的輸出連接配接到另一個程式、線程或代碼塊的輸入 。
管道輸入流作為一個通信管道的接收端,管道輸出流作為發送端。在使用管道之前,管道輸出流和管道輸入流必須進行連接配接。下面有兩種連接配接的方法:
1. 構造方法中連接配接
PipedInputStream(PipedOutputStream src);
PipedOutputStream(PipedInputStream snk);
2. connect()方法進行連接配接
類PipedInputStream中定義為:
void connect(PipedOutputStream src);
類PipedOutputStream中定義為:
void connect(PipedInputStream snk);
4.9.2 記憶體的讀/寫
1. ByteArrayInputStream和ByteArrayOutputStream
ByteArrayInputStream //從位元組數組中讀取以位元組為機關的資料
ByteArrayOutputStream //向位元組數組中寫入以位元組為機關的資料
2. StringBufferInputStream和StringBufferOutputStream
StringBufferInputStream
//從字元串緩沖區StringBuffer中讀取以字元為機關的資料
StringBufferOutputStream
//向字元串緩沖區StringBuffer中寫入以字元為機關的資料
4.9.3 順序輸入流
SequenceInputStream把幾個輸入流順序連接配接起來。順序輸入流提供了把若幹不同的流統一為同一個流的功能,使得程式變得更加簡潔。
【本講小結】
例外處理是java語言中一個獨特之處,主要使用捕獲例外和聲明抛棄例外兩種方法來處理程式中可能出現例外的語句塊,其中捕獲例外的方法是一種積極地處理例外的方法,而聲明抛棄例外是一種消極的處理例外的方法。
Java中的輸入/輸出處理是通過使用流技術,用統一的接口表示而實作的。輸入/輸出流中,最常見的是對檔案的處理。Java語言中提供專門處理檔案和目錄的類,例如:java.io.File,java.io.FileInputStream,java.io.FileOutputStream,java.io.RandomAccessFile和接口java.io.FilenameFilter。輸入/輸出流根據處理的内容,分為字元流和位元組流兩種,其中位元組流是以byte為基本處理機關的流;而字元流是以16位的Unicode碼為處理機關的流。
4.9其它常用的流 4.9.1 管道流 管道用來把一個程式、線程或代碼塊的輸出連接配接到另一個程式、線程或代碼塊的輸入 。 管道輸入流作為一個通信管道的接收端,管道輸出流作為發送端。在使用管道之前,管道輸出流和管道輸入流必須進行連接配接。下面有兩種連接配接的方法: 1. 構造方法中連接配接 PipedInputStream(PipedOutputStream src); PipedOutputStream(PipedInputStream snk); 2. connect()方法進行連接配接 類PipedInputStream中定義為: void connect(PipedOutputStream src); 類PipedOutputStream中定義為: void connect(PipedInputStream snk); 4.9.2 記憶體的讀/寫 1. ByteArrayInputStream和ByteArrayOutputStream ByteArrayInputStream //從位元組數組中讀取以位元組為機關的資料 ByteArrayOutputStream //向位元組數組中寫入以位元組為機關的資料 2. StringBufferInputStream和StringBufferOutputStream StringBufferInputStream //從字元串緩沖區StringBuffer中讀取以字元為機關的資料 StringBufferOutputStream //向字元串緩沖區StringBuffer中寫入以字元為機關的資料 4.9.3 順序輸入流 SequenceInputStream把幾個輸入流順序連接配接起來。順序輸入流提供了把若幹不同的流統一為同一個流的功能,使得程式變得更加簡潔。 【本講小結】 例外處理是java語言中一個獨特之處,主要使用捕獲例外和聲明抛棄例外兩種方法來處理程式中可能出現例外的語句塊,其中捕獲例外的方法是一種積極地處理例外的方法,而聲明抛棄例外是一種消極的處理例外的方法。 Java中的輸入/輸出處理是通過使用流技術,用統一的接口表示而實作的。輸入/輸出流中,最常見的是對檔案的處理。Java語言中提供專門處理檔案和目錄的類,例如:java.io.File,java.io.FileInputStream,java.io.FileOutputStream,java.io.RandomAccessFile和接口java.io.FilenameFilter。輸入/輸出流根據處理的内容,分為字元流和位元組流兩種,其中位元組流是以byte為基本處理機關的流;而字元流是以16位的Unicode碼為處理機關的流。 |