Java之I/O處理

1.流的概念

Java的I/O系統涉及流的概念。一個讀取字節序列的對象被稱爲輸入流,一個可以寫入字節序列的對象稱爲輸出流。輸入流和輸出流是相對於程序本身而言的。程序讀取數據稱爲打開輸入流,程序向其他源寫入數據稱爲打開輸出流


程序讀入數據,首先打開一個輸入流,流以流對象的形式出現。數據文件或者網絡鏈接信息包裝在流對象內,流對象一旦啓動,程序可以從輸入流依次讀入數據。

當程序需要輸出數據時,就打開一個輸出流對象,該對象知道把數據寫到什麼地點(一個文件或通過網絡傳輸到其他機器上的文件),數據是依次寫入,通過輸出流對象把數據寫入目標文件。

在java.io包中有各種I/O流類,這裏按照輸入輸出流處理的不同數據類型對流進行分類,即字符流和字節流

2.字符流

在Java的I/O系統提供了InputStream和OutputStream兩個抽象類實現字節(8位)數據的輸入/輸出,其中InputStream是輸入流的抽象類,提供了read()方法,每個實現了該類的子類都要實現該方法,

(1)輸入流類InputStream

抽象類InputStream表示從不同的輸入源輸入數據的類,這些數據源的數據類型多樣,可以是字節數組、String對象、類的序列化對象,文件、管道或網絡鏈接。對於多樣的數據類型有相應的輸入流類與其對應。

InputStream是個抽象類,提供了抽象read()方法,下面幾個類是繼承自InputStream的子類:
ByteArrayInputStream(字節數組輸入流)

FileInputStream(文件輸入流)

PipedInputStream(管道輸入流)

SequenceInputStream(序列化輸入流)

StringBufferInputStream(字符串緩衝輸入流)

ObjectInputStream(對象輸入流)

FilterInputStream(過濾器輸入流)

以下的類繼承自FilterInputStream(過濾器輸入流),同時實現了DataInput接口

LineNumberInputStream(行號輸入流)

DataInputStream(數據輸入流)

BufferedInputStream(緩衝輸入流)

PushbackInputStream(推回輸入流)

下面分別介紹上述各種輸入流類的功能

1》ByteArrayInputStream:允許帶緩衝區的輸入流,顯然該類的功能就是允許將內存緩衝區作爲輸入流使用,它包裝了filterInputStream對象,把該對象的輸入文件作爲實際數據輸入到ByteArrayInputStream流對象緩衝區中。FilterInputStream包含幾個子類,分別是LineNumInputStream、DataInputStream、BufferedInputStream和PushbackInputStream.

2》FileInputSteam:從文件中讀取數據,其構造函數參數可以是文件對象、字符串或FileDescriptor對象。通過FileInputStream流類的包裝對程序提供讀數據的接口

3》PipedInputStream:該類產生輸入管道流,該流又產生寫入輸出管道流的數據,這樣就實現了管道化通信,通常在多線程編程中作爲線程間通信的實現方式

4》SequeceInputStream:該流把多個輸入流對象鏈接成一個輸入流。該類的構造函數參數是兩個InputStream對象和一個包含Inpustream對象的枚舉器對象

5》StringBufferInputStream:該類的功能是把String轉換成輸入流。應用程序創建一個該類的輸入流,把構造函數的參數中的String數據作爲程序的輸入。構造函數爲StringBufferInputStream(String str)

6》ObjectInputStream:對象輸入流讀取輸入流對象中的各種對象類型數據,用來處理序列化對象的傳輸。

7》FilterInputStream:該類繼承了抽象方法InputStream,實現了抽象方法read()

8》LineNumberInputStream:該類實現對輸入流中的行數的計數,對輸入流增加了行號,該類的構造函數是InputStream流對象

9》DataInputStream:數據輸入流允許程序從底層的輸入流中讀取基本類型的數據,如int型,float型和byte型等。該類包含了讀取基本類型的所有方法,如readByte()、readBoolean()、readChar()、readDouble()和readFloat()等。其構造函數參數爲一個InputStream流對象

10》BufferedInputStream:帶緩衝區的輸入流,可以把數據先放入緩衝區,防止每次讀取時進行實際的讀寫操作,減少了數據實際訪問的時間開銷。數據以字節數組的形式存放在緩衝區中,該類提供了read()方法,每次從輸入流讀取一個字節數據。構造函數的參數爲InputStream對象

11》PushbackInputStream:該類一般不被程序員使用,是爲java編譯器而設計的。

(2)輸出流類OutputStream

抽象類OutputStream是表示輸出數據流的抽象類,與抽象輸入流對應,提供各種流對象的數據輸出。

OutputStream是個抽象類,提供了抽象write方法,下面幾個類是繼承自OutputStream的子類,這些類都實現了write()方法

ByteArrayOutputStream(字節數組輸出流)

FileOutputStream(文件輸出流類)

ObjectOutputStream(對象輸出流類)

PipedOutputStream(管道輸出流類)

FilterOutputStream(過濾器輸出流類)

下面3個類繼承自FilterOutputStream類並實現了DataOut接口

DataOutputStream(數據輸出流類)

BufferedOutputStream(緩衝輸出流類)

printStream(打印輸出流類)

這些流是類OutputStream下的子類,這些流類都可以實現Byte(8bit)類型的數據

1》ByteArrayOutputStream:在向輸出流寫入數據前先將數據緩衝處理,其緩衝區大小通過構造函數的參數設置

2》FileOutputStream:通過該輸出流把數據寫入文件,構造函數的參數可以是字符串、文件對象、文件或FileDescriptor對象。

3》ObjectOutputStream:對象輸出流把各種對象類型數據寫入輸出流文件中,用來處理序列化對象的傳輸

4》PipedOutputStream:與管道輸入流(PipedInputStream)對應,任何管道輸入流都通過輸出流寫出。兩者搭配使用實現管道通信

5》FilterOutputStream:該類繼承了抽象類OutputStream,實現了抽象方法write()

下面3個類繼承自FilterOutputStream(過濾器輸出流):

DataOutputStream:數據輸出流同數據輸入流(DataInputStream)對應,實現把基本數據類型寫入輸出流。該類提供了把數據寫入輸出流的所有方法,如writerBoolean(Boolean v)、writeByte(int v)、writeBytes(String s)、writeChar(int v) 和writeChars(String s)等

BufferedOutputStream:該類實現輸出數據時首先進行數據緩衝,該類提供了flush()方法實現清空數據緩衝區。該類的構造函數參數爲輸出流對象或輸出流對象和輸出緩衝區大小。即:BufferedOutputStream(OutputStream out)和BufferedOutputStream(OutputStream out,int size)

PrintStream:該類的目的是實現Java基本數據類型的格式化輸出。

3.字節流

Java在設計其I/O系統時,把輸入輸出的系統分爲兩類:一類是字符流(8bit),另一類是字節流(16bit)。字節流也分爲讀流數據和寫流數據,即Reader和Writer類及其子類。

(1)Writer類

Writer類是一個抽象類,所有繼承自該類的子類都必須實現抽象方法write()。

下面列出繼承自Writer類的子類:

BufferedWriter(帶緩衝Writer)

CharArrayWriter(字符數組Writer)

FilterWriter(帶過濾器Writer)

PrintWriter(打印Writer)

PipedWriter(管道Writer)

StringWriter(字符串Writer)

OutputStreamWriter(輸出流Writer)

其中下面的類繼承自類OutputStreamWriter(輸出流Writer)

FileWriter(文件Writer)

下面分別介紹上述各輸出Writer類的功能

《1》BufferedWriter:該類將文本寫入字符流,將字節轉換爲字符同時緩衝讀取每個字符,提供單個字符、數據和字符串的寫入。該類提供兩種構造函數指定緩衝區的大小,一種是指定大小,另一種是採用默認值。一般情況下采用默認值就足夠使用了。在使用Writer類向文件寫入數據時最好使用BufferedWriter包裝開銷大的writer操作,如FileWriter類

這樣可以緩衝字節或字符流,而不是把字符轉換成字節後立即寫入到文件(這樣會造成不斷地讀寫數據,顯然效率低)

《2》CharArrayWriter:該類作爲Writer的字符緩衝區。該緩衝區隨着向流中寫入數據而自動增長。該類的構造函數提供兩種形式,一種是默認的形式,另一種是有整型參數的形式。第二種構造函數構造指定初始容量的CharArrayWriter.

《3》FileWriter:該類把字符寫入文件,文件位置在構造函數中指定,其構造函數接受默認的字符編碼和默認的字符緩衝區大小。該類用於寫入字符流,如果要寫的是原始字節流,則參考FileOutputStream流來實現

《4》FilterWriter:該類是一個抽象類,用於寫入已經過濾的字符流。該類提供了寫字符流的方法write,其子類必須重寫這些方法,也可以提供其他的方法或屬性。

《5》OutputStreamWriter:該類提供了字符流向字節流的轉換功能。並向流中寫入字符。每次調用該類的write()方法都會導致向輸出流寫入一個或多個字節。爲了提高字節到字符的轉換效率,可以使用緩衝機制。如可以把OutputStreamReader類包裝在Bufferedeader中以提高字符讀取的效率。

《6》PrintWriter:該類完成向文本輸出流打印格式化數據的形式,它實現了在PrintStream中的所有print()方法,但是不包含寫入原始字節的方法。

《7》PipedWriter:管道Writer類創建管道通信的輸出流,通過該輸出流把數據寫入文件,和管道Reader想對應建立起管道通信。

《8》StringWriter:顯然該類是一個字符流,利用其字符串緩衝區中的輸出字符構造字符串。該類的構造函數有兩種,一種是創建默認初始化字符串緩衝區大小的新字符串Writer,另一種是創建具有指定初始字符串緩衝區大小的新字符串Witer.

(2)Reader類

所有繼承自該類的子類都必須實現抽象方法read()和close().

繼承自Reader類的子類:

BufferedReader(帶緩衝Reader)

CharArrayReader(字符數組Reader)

FileReader(文件Reader)

FilterReader(過濾器Reader)

InputStreamReader(輸入流Reader)

LineNumberReader(帶行號Reader)

Pipedeader(管道Reader)

PushbackReader(推回Reader)

StringReader(字符串Reader)

下面介紹具體的類含義和注意事項:

《1》BufferedReader:該類完成從字符輸入流中讀取文本,且緩衝讀到的字符。用戶可以指定緩衝區的大小,或者使用默認的緩衝區大小。爲了實現高效的讀取文本建議read()操作開銷高的Reader,如FileReader採用BufferedReader包裝,將緩衝FileReader指定文本的輸入。如果不使用緩衝機制,則每次調用read()或readLine()都會導致從文件中讀取字節而後直接轉換成字符返回退出方法,顯然這樣的效率很低

《2》CharArrayReader:該類繼承了Reader抽象類,實現用做字符輸入流的字符緩衝區讀取字符。

《3》FileReader:該類繼承自InputStreamReader類,讀取字符流,其構造函數默認採用了合適的字符編碼和字符緩衝區大小。該類通過指定一個File對象、一個文件名或者指定FileDescriptor的條件下創建一個新FileReader對象。

《4》InputStreamReader:該類提供了字節流向字符流的轉換功能,並讀取字符流。每次調用該類的read()方法都會從輸入流中讀取一個或多個字節。爲了提高字節到字符的轉換效率,可以使用緩衝機制從輸入流中讀取更多的字節後再進行轉換,如可以把InputStreamReader類包裝在BufferedReader中以提高字符讀取的效率。

《5》LineNumberReader:跟蹤行號的緩衝字符輸入流。該類提供了兩個方法void setLineNumber(int)和int getLineNumber()分別用於設置和獲取當前行號。

《6》PipedReader:字符輸入流,通過管道的方式從PipedWriter流讀字符流

《7》PushbackReader:該類讀取字符流,允許字符退回到流中的字符流

《8》StringReader:從源數據流爲字符串流的源讀取字符串。

3.File類

File類可以表示特定文件名(帶絕對路徑),也可以是某個目錄下一組文件,該類提供了方法可以用來訪問多個文件。File類提供了豐富的方法來處理和文件或目錄相關的操作,如創建和刪除文件。創建和刪除文件夾以及通過和其他類配合使用實現文件的複製和移動等。

(1)創建文件夾(目錄)

建立目錄的方法是調用mkdir()方法

創建文件夾程序示例:

import java.io.File;
public class CreateNewFolder {
//參數newFolder表示新建目錄的名稱,該方法在創建新目錄時首先判斷該目錄文件是否存在,如存在,則程序跳到異常處理代碼,打印一行
//錯誤提示;如果不存在,則建立該目錄
private void newFolder(String newfolder){
try{
String filepath=newfolder;
File myPath=new File(filepath);
if(!myPath.exists()){
myPath.mkdirs();
}
}catch(Exception e){
System.out.println("新建目錄存在");
e.printStackTrace();
}
}
public static void main(String[]args){
//創建該public類的對象,以調用其函數來建立目錄
CreateNewFolder createNewFolder = new CreateNewFolder();
//獲得執行程序時的參數,該參數在執行程序的代碼後直接給出,該參數放在臨時變量mynewpath中
String mynewpath="one/two.three";
//對象調用其函數來創建新目錄
createNewFolder.newFolder(mynewpath);

}
}

(2)創建文件

在Java的File類中創建新文件只需要調用該類的一個方法createNewFile().

創建文件程序示例:

import java.io.File;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.io.Writer;


public class CreateNewFile {
//創建一個方法完成創建文件的目的,文件的第一個參數是文件路徑和文件名稱,第二個參數是文件內容
public void createNewFile(String fileDerectoryAndName,String fileContent){
try{
String fileName=fileDerectoryAndName;
//創建File對象,參數爲String類型,表示目錄名
File myFile = new File(fileName);
//判斷目錄是否存在,如果不存在則調用createNewFile()方法創建新目錄,否則跳至異常處理代碼
if(!myFile.exists()){
myFile.createNewFile();
}
//下面把數據寫入創建的文件,首先新建文件名爲參數創建FileWriter對象
FileWriter resltFile = new FileWriter(myFile);
//把該對象包裝進PrintWriter對象
//Writer resultFile;
PrintWriter myNewFile = new PrintWriter(resltFile);
//再通過PrintWriter對象的println()方法把字符串數據寫入新建文件。
myNewFile.println(fileContent);
resltFile.close();//關閉文件寫入流
}catch(Exception ex){
System.out.println("無法新建文件");
ex.printStackTrace();
}
}

public static void main(String[] args){
//創建類的對象並調用該對象的createNewFile()方法,創建新文件並寫入數據
CreateNewFile createFile = new CreateNewFile();
createFile.createNewFile("D:/111.doc", "liujinqing");
}
}

(3)複製文件

文件的複製涉及文件流的概念,爲了實現文件操作,這裏使用了FileInputStream和FileOutputStream兩個流類,通過文件輸入流讀取源文件,通過文件輸入流把讀入緩衝區的字節數據寫入新文件。如果新文件已經存在,則覆蓋該文件;如果不存在,則新建一個文件。

複製文件程序示例:

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;


public class CopyFileTest {
//參數oldFile如:D:/old.txt,參數newFile 如D:/new.txt
public void copyFile(String oldFile,String newFile){
try{
int bytesum=0;
int byteread=0;
File oldfile = new File(oldFile);
//判斷文件是否存在,如果文件存在則實現該文件向新文件的複製
if(oldfile.exists()){
//讀取原文件
InputStream ins=new FileInputStream(oldFile);
//創建文件輸出流,寫入文件
FileOutputStream outs= new FileOutputStream(newFile);
//創建緩衝區,大小爲500字節
byte[] buffer = new byte[500];
//每次從文件輸入流中讀取500字節數據,計算當前爲止讀取的數據總數
while((byteread=ins.read(buffer))!=-1){
bytesum+=byteread;
System.out.println(bytesum);
//把當前緩衝區中的數據寫入新文件
outs.write(buffer, 0, byteread);
}
ins.close();//關閉文件輸入流
}
}catch(Exception ex){
System.out.println("原文件不存在");
ex.printStackTrace();
}
}
public static void main(String[] args){
//創建類對象實例,並調用copyFile()函數
//第一個參數表示老文件,第二個參數表示新文件
CopyFileTest copyfile = new CopyFileTest();
copyfile.copyFile("D:/oldFile.txt", "D:/newFile.txt");
}
}

(4)刪除文件

在Java的File類中刪除文件只需要調用該類的一個方法delete()。該方法可以刪除指定的文件

在程序執行時,用戶給出要刪除的文件的目錄和文件名或文件夾,就可以完成刪除操作。

import java.io.File;


public class DeleteFile {
public void delFile(String fileDerecatorAndName){
try{
//以要刪除的文件或文件夾名爲參數,創建File對象
File deletedFile = new File(fileDerecatorAndName);
//調用File類的delete()方法刪除文件
deletedFile.delete();
}catch(Exception ex){
System.out.println("刪除文件錯誤");
ex.printStackTrace();
}
}
public static void main(String[]args){
//創建類DeleteFile的對象
DeleteFile deleteFile=new DeleteFile();
//調用類的delFile()方法,參數爲要刪除的文件或文件夾
deleteFile.delFile("D:/newFile.txt");
}
}

(5)刪除文件夾

在Java的File類中刪除文件夾,需要首先刪除掉文件夾中的文件,再刪除空文件夾。刪除空文件夾的方法與刪除文件的方法相同,所以關鍵是如何實現刪除文件夾下的所有文件,可以想象欲刪除一個目錄下的所有文件只要獲得該文件的目錄和文件名,使用一個循環調用來依次刪除文件夾中的文件即可。

範例:下面的類提供了兩個方法,一個方法是刪除文件夾,另一個是刪除文件夾下的文件。如果在刪除文件夾時即有目錄又有文件,則刪除文件,再繼續刪除文件夾;如果該文件夾下還是既有文件又有文件夾則繼續上面的操作,直到把目錄下的文件和子目錄全部刪除。

import java.io.File;


public class DeleteFolder {
//定義刪除文件夾函數,參數爲文件路徑
public void delFolder(String folderPath){
try{
//調用刪除所有文件函數,刪除該目錄下的所有文件
delAllFile(folderPath);
//創建文件對象,參數爲欲刪除的目錄
File myFilePath =new File(folderPath);
myFilePath.delete();//調用刪除目錄函數
}catch(Exception ex){
System.out.println("刪除文件夾錯誤");
ex.printStackTrace();
}
}
public void delAllFile(String path){
//定義並創建刪除所有文件方法,參數爲文件路徑
File file =new File(path);
if(!file.exists()){//如果文件不存在則跳出函數
return;
}
if(!file.isDirectory()){//如果該file對象不是目錄也跳出函數
return;
}
String[] tempList = file.list();//取出文件目錄下的文件名或目錄名
File temp=null;
for(int i=0;i<tempList.length;i++){
//列出當前文件夾下的文件或目錄,可以方便的觀察刪除了哪些文件。
System.out.println(tempList[i].toString());
if(path.endsWith(File.separator)){
temp=new File(path+tempList[i]);
}
else{
//爲欲刪除目錄下的每一個文件或目錄創建臨時File對象,參數爲全路徑
temp=new File(path+File.separator+tempList[i]);
}
//如果temp是文件則刪除該文件
if(temp.isFile()){
temp.delete();
}
//如果temp是目錄則調用刪除所有文件的方法,此時出現了delAllFile()方法的迭代調用
if(temp.isDirectory()){
delAllFile(path+"/"+tempList[i]);
delFolder(path+"/"+tempList[i]);
}
}
}
public static void main(String[]args){
DeleteFolder deleteFoler = new DeleteFolder();
deleteFoler.delFolder("C:/Users/Administrator/workspace/LianXi/one");
}
}

4.I/O流的典型運用

(1)文件流

文件流操作的目的是實現文件之間的數據傳輸,把數據從一個文件複製到另一個文件。文件的輸入流可以是流類的對象,如FileReader、FileInputStream。文件的輸出流是一個流類的對象,如FileWriter、FileOutputStream.通過在文件上建立流來實現文件間的數據傳輸。

文件流操作示例:

import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;


//讀文件流和寫文件流
public class FileStream {
public static void main(String []args) throws IOException{
/*在兩個文件上建立輸入和輸出流,該文件是兩個File實例,從輸入流對象filein讀數據,源文件是FileStream.java,通過輸出流對象
* fileout寫數據到指定文件目錄文件
* */
FileReader filein = new FileReader(new File("C:/Users/Administrator/workspace/LianXi/src/LianXi/FileStream.java"));
FileWriter fileout = new FileWriter(new File("D:/copyFileStream.txt"));
int c;
//依次讀取輸入流中的數據,先存儲在變量c中,然後並把數據寫入輸出流
//寫入文件copyFileStream.txt中
      while((c= filein.read())!=-1){
     fileout.write(c);
     //關閉輸入流和輸出流,釋放流佔用的資源
      }
      filein.close();
      fileout.close();
}
}

代碼說明:

該程序首先建立一個輸入流對象filein,該流類FileReader的參數是一個文件對象,輸入流從該對象文件讀數據。同時建立了一個輸出流類fileout,該類FileWriter的參數也是一個文件對象,表示寫入數據到指定的文件(copyFileStream.txt)。一旦執行該程序,在同一目錄下會生成一個名爲copyFileStream.txt的文本文件。該文件會保存文件Filetream.java的全部內容。對於保存的文件類型有多種選擇,如可以保存爲Word文件或Excel文件,只要把文件輸出流的文件對象的參數String(D:/copyFileStream.txt)的文件後綴修改爲.doc或.xls即可。再編譯執行該程序時會輸出名爲FileStream.doc或FileStream.xls文件。

(2)讀取內存數據

在Java的輸入輸出流中提供了讀取內存數據的類,這些類包括StringReader和StringWriter、CharArrayReader和CharArrayWriter、ByteArrayInputStream和ByteArrayOutputStream。在內存中讀寫數據,通常在已經 存在的數據中創建I/O流。

讀內存數據示例:

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.StringReader;


public class ReadMemoryTest {
public static void main(String[]args) throws IOException{
//創建一個文件reader對象,並用BufferedReader封裝
BufferedReader inReader = new BufferedReader(new FileReader("C:/Users/Administrator/workspace/LianXi/src/LianXi/ReadMemoryTest.java"));
String s1,s2=new String();
//每次讀一行數據賦予字符串變量s1
while((s1=inReader.readLine())!=null){
//把每次讀到的一行數據添加到字符串變量s2,該變量的值存儲在內存中
s2+=s1+"\n";
}
inReader.close();
//使用StringReader從內存讀出數據,並在控制檯打印輸出
StringReader inReader2=new StringReader(s2);
int c;
//把讀到的字符值賦予int型變量c,如果對讀到字符串的末尾會返回-1值,這也是循環結束標誌
while((c=inReader2.read())!=-1)
//打印讀到的數據,注意必須將int型數據上轉換成char型,不然看到的就是一系列數字,這些數字是字符的編碼
System.out.print((char)c);
}
}

代碼說明:

程序中ReadMemoryTest.java文件存放在程序運行的當前目錄下,就是該程序自己的源文件。爲了使讀者更清楚的瞭解讀到的數據,在使用StringReader讀到內存中s2的數據後,把讀到的數據打印到控制檯輸出,一次讀取一個字符,如果直接輸出變量c,則屏幕上的內容都是數字,這些數字是字符的編碼

(3)鏈接文件

Java提供SequenceInputStream類把多個輸入流鏈接起來放在一個輸入流中。多個輸入流可以存入Enumeration對象,將依次讀取每個流對象內的數據,直到最後一個流對象的結尾;也可以直接鏈接兩個輸入流,通過構造函數實現。

示例:首先創建兩個輸入流,之後再直接鏈接這兩個輸入流,並打印到控制檯輸出。

import java.io.FileInputStream;
import java.io.InputStream;
import java.io.SequenceInputStream;


public class LinkedFileTest {
//定義兩個輸入流,作爲鏈接文件的輸入
private InputStream firstin;
private InputStream secondin;
//定義方法對兩個輸入流參數初始化
private void getInputStream(String fileone,String filetwo){
try{
firstin=new FileInputStream(fileone);
secondin=new FileInputStream(filetwo);
}catch(Exception ex){
ex.printStackTrace();
}
}
//定義鏈接函數,調用SequenceInputStream類,創建鏈接文件對象,並打印鏈接文件的內容
private void linkFile(){
try{
SequenceInputStream s=new SequenceInputStream(firstin,secondin);
int c;
//一次讀取鏈接流的一個字符,直到流的結尾,輸出讀到的字符,最後關閉鏈接文件流
while((c=s.read())!=-1){
System.out.write(c);
}
s.close();
}catch(Exception ex){
System.out.println("輸入流異常");
ex.printStackTrace();
}
}
public static void main(String[] args){
LinkedFileTest lftest = new LinkedFileTest();
lftest.getInputStream("D:/oldFile.txt","D:/111.doc");
lftest.linkFile();
}
}

(4)管道流

有兩種管道流,一種是通過PipedInputStream和PipedOutputStream實現,另一種是通過PipedReader和PipedWriter實現。

管道流是對應多線程的概念,實現線程間通信,它建立在兩個線程之上,它的實現原理是在管道的一端讀入數據,而在管道的另一端讀出數據。實現管道流時,關鍵是在線程中建立管道,體現在下面兩行關鍵語句中:

PipedWriter pipeOut = new PipedWriter();

PipedReader pipIn = new PipedReader(pipeOut);

上述語句的作用就是建立一個通信管道,管道的一端是輸出流(PipedWriter),另一端是輸入流(PipedReader)。數據從輸出端寫入,從輸入端讀出,這樣形成一個管道流,實現多線程之間的通信。

(5)隨機訪問文件

類RandomAccessFile實現文件的隨機訪問,可以在文件的任意位置讀取或寫入數據。該類與InputStream和OutputStream不同。它把輸入輸出放在同一個類中,通過構造函數的參數確定是輸入還是輸出或輸入/輸出操作可同時實現。該類的構造函數有兩個參數,第一個參數是文件目錄,第二個參數指定相應的操作,“r”表示讀。“rw”表示讀寫。

RandomAccessFile in =new RandomAccessFile("readme.txt","rw");

RandomAccessFile in =new RandomAccessFile("readme.txt","r");

隨機存取文件對任意位置的數據讀取和寫入全依賴於文件指針,文件指針指向的位置是緊接着要讀或寫數據的位置,但進行讀或寫數據時,指針就異動到下一個數據單位(Byte)。其中seek(long)方法來移動指針到文件內的任意字節處,該方法參數的大小從0到文件長度(按字節計算),而getFilePointer()獲取文件指針的當前位置。

由於RandomAccessFile類實現了DataOutput和DataInput接口,所以該類具有讀、寫各種基本類型數據的各種方法,如readLong()、readFloat()、readByte()、wirteLong()、writeFloat()、writeByte().

隨機訪問文件示例:

import java.io.IOException;
import java.io.RandomAccessFile;


public class RandomAccessTest {
public static void main(String[]args) throws IOException{
//創建隨機訪問文件對象,對文件的權限爲可讀可寫,若程序所在目錄下沒有randomfile.dat文件,則在該目錄下創建該文件
RandomAccessFile rf=new RandomAccessFile("D:/randomfile.dat","rw");
//向文件中寫入10個Double類型的數據
for(int i=0;i<10;i++){
rf.writeDouble(i*1.414);
}
//關閉對文件的隨機訪問
rf.close();
//創建隨機訪問文件對象,對文件有讀寫權限
rf=new RandomAccessFile("D:/randomfile.dat","rw");
//對象rf調用seek(long)函數,把當前的文件指針指向第40個字節位置,即第6個數據的開始位置,接着調用writeDouble()函數
//修改第6個數據爲47.99991
rf.seek(5*8);
rf.writeDouble(47.99991);
rf.close();
//創建隨機訪問文件對象,讀文件randomfile中的Double類型數據
rf=new RandomAccessFile("D:/randomfile.dat","r");
for(int i=0;i<10;i++){
System.out.println("Value"+i+":"+rf.readDouble());
}
//關閉對文件randomfile.dat的讀訪問
rf.close();
}
}

運行結果:

Value0:0.0
Value1:1.414
Value2:2.828
Value3:4.242
Value4:5.656
Value5:47.99991
Value6:8.484
Value7:9.898
Value8:11.312
Value9:12.725999999999999

代碼說明:

類RandomAccessFile提供了對文件數據的非順序讀寫,在程序中rf.seek(5*8)將文件指針指向文件中第40個字節處,接下來對該文件的寫操作將從第40個字節開始,寫入double類型數據,此時該數據覆蓋掉文件中原來該位置的數據。而在讀數據時調用了rf.readDouble()方法,這裏是順序訪問文件,隨着for語句中變量的遞增而輸出文件中的double類型的數據。

(6)從標準輸入讀取

本節介紹如何使用InputStreamReader讀取用戶的輸入,然後再進行包裝,並輸出到界面

示例:從標準輸入流讀取數據

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;


public class ReadStanInput {
public static void main(String[]args) throws IOException{
//把輸入數據包裝成一個BufferedReader
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String s;
//把BufferedReader讀到的數據賦予變量s,並判斷是否讀到字符串的末尾
while((s=in.readLine())!=null &&s.length()!=0)
//輸出從標準輸入讀到的數據
System.out.println("輸出:"+s);
}
}

代碼說明:

在代碼中用InputStreamReader把System.in包裝成Reader,再包裝成BufferedReader,從標準輸入讀數據放入緩存,再從緩存中讀出數據賦予變量s,最後從變量s中讀取數據並在屏幕上輸出。

(7)I/O重定向

I/O重定向是指把標準輸入定向到一個文件,把這個文件作爲程序輸入源,而把數據輸出到一個指定的文件。因爲I/O操縱的是字節流,所以採用InputStream和OutputStream流類族實現輸入輸出重定向。

實現I/O重定向示例程序:

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;


//I/O重定向,從一個文件(Redirecting.java)讀出,寫入另一個文件(test.out)
public class Redirecting {
public static void main(String[]args) throws IOException{
//設置輸入對象,用FileInputStream對文件Redirecting.java進行包裝,創建一個文件輸入流對象
//接着創建一個緩衝輸入流對象in,重定向標準輸入
BufferedInputStream in = new BufferedInputStream(new FileInputStream("C:/Users/Administrator/workspace/LianXi/src/LianXi/Redirecting.java"));
//創建輸出標準輸出重定向,創建文件按輸出流對象,並依次用BufferedOutputStream和PrintStream進行包裝,最後得到一個PrintStream類
//對象out
 PrintStream out=new PrintStream(new BufferedOutputStream(new FileOutputStream("D:/test.txt")));
 //設置輸出對象
 //設置輸入流爲in對象,輸入數據是Redirecting.java文件
 System.setIn(in);
 System.setOut(out);//把輸出定向到test.out文件
 //從標準輸入讀數據,此時是讀重定向後的文件中的數據
 BufferedReader breader = new BufferedReader(new InputStreamReader(System.in));
 String s;
 //通過中間變量s把文件Redirecting.java中的數據輸出,此時是輸出到重定向的文件test.txt中
  while((s=breader.readLine())!=null)
 System.out.println(s);
   out.close();//關閉輸出流,釋放流佔用的資源
 
}
}

代碼說明:

I/O重定向是指把標準輸入定向到一個文件,把這個文件作爲程序輸入源,而把數據輸出到一個指定的文件。因爲I/O操縱的是字節流,所以採用InputStream和OutputStream流類族實現輸入/輸出重定向。

(8)過濾流

按照標準I/O模型,Java提供了標準的輸入/輸出方法,而System.out是經過包裝的流對象,可以將數據寫到標準輸出,但是System.in無法直接實現數據輸入,因爲System.in是

沒有包裝的流,所以在讀取標準輸入前必須對System.in進行包裝

範例:用InputStreamReader來包裝System.in成Reader,再包裝成BufferedReader使用

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;


public class FilterIOTest {
public static void main(String[] args)throws IOException{
//創建一個過濾輸出流,過濾處理後的數據輸入到文件FilterOut.xls
DataOutputStream filterout=new DataOutputStream(new FileOutputStream("D:/FilterOut.xls"));
//創建3個不同數據類型的數組
double[]prices={11,22,21,41,23,8,9,10,29,12};
int[]counts={1,2,3,4,5,6,7,9,3,4};
String[] desc={"JavaT-shirt","JavaDoc","Javapin","Javaapp","Hello","aa","bb","cc","dd","ee"};
//將不同類型的數據寫入DataOutputStream,結束標誌爲換行符'\n'
for(int i=0;i<prices.length;i++){
filterout.writeDouble(prices[i]);
filterout.writeChar('\t');
filterout.writeInt(counts[i]);
filterout.writeChar('\t');
filterout.writeChars(desc[i]);
filterout.writeChar('\n');
}
//關閉過濾輸出流,不再向文件FilterOut.xls寫入數據
filterout.close();
//在一個FileInputStream流上建立一個DataInputStream流,從文件FilterOut.xls中讀出數據
DataInputStream in =new DataInputStream(new FileInputStream("D:/FilterOut.xls"));
double price;
int unit;
StringBuffer descd;
double total=0.0;
//下面try區塊循環且順序讀出存入文件的double型數據,int型數據,char型數據,每一次循環則打印結果
try{
while(true){
price=in.readDouble();
in.readChar();
unit=in.readInt();
in.readChar();
char chr;
descd=new StringBuffer(20);
//判斷如果沒有到文件末尾,則繼續執行,把讀到的Char數據放入StringBuffer對象
while  ((chr=in.readChar())!='\n')
descd.append(chr);
System.out.println("您定製了"+unit+"個"+descd+"單價是"+price+"$");
total=total+unit*price;
}
}catch(EOFException e){}
//關閉過濾輸入流,不再從過濾輸入文件讀數據
in.close();
}
}

運行結果:

您定製了1個JavaT-shirt單價是11.0$
您定製了2個JavaDoc單價是22.0$
您定製了3個Javapin單價是21.0$
您定製了4個Javaapp單價是41.0$
您定製了5個Hello單價是23.0$
您定製了6個aa單價是8.0$
您定製了7個bb單價是9.0$
您定製了9個cc單價是10.0$
您定製了3個dd單價是29.0$
您定製了4個ee單價是12.0$

(9)序列化對象

對象的序列化後的輸入/輸出通過ObjectInputStream和ObjectOutputStream類實現。序列化的本質是把具有一定結構的Java對象進行打包,而後通過特定的輸入輸出流來處理。

類ObjectInputStream和ObjectOutputStream不能單獨使用,必須附加在其他流之上,對其他輸入/輸出流進行包裝,因爲對象的輸入輸出必須對應一個存儲對象的文件。這裏採用FileInputStream/FileOutputStream對象作爲參數來構造ObjectInputStream/ObjectOutputStream對象,實現對象在文件之間的傳輸。ObjectInputStream類提供了各種read()方法讀取特定類型的數據,如readObject()、readInt()、readBoolean()等方法讀取流中的對象。ObjectOutputStream類提供了各種write()方法讀取特定類型的數據,如writeObject()、writeInt()、writeBoolean()等方法向流中寫入數據。

對象序列化示例:

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Date;


public class ObjectSeri {
public static void main(String []args){
try{
//創建文件輸出流對象,該對象指明把對象數據 寫入參數所指目錄中的ObjectSeriOut.txt文件中
FileOutputStream out = new FileOutputStream("D:/ObjectSeriOut.txt");
//創建對象輸出流,該流對象的參數是文件輸出流對象
ObjectOutputStream sout = new ObjectOutputStream(out);
sout.writeObject(new String("current time is:"));//向對象輸出流寫對象
sout.writeObject(new Date());//向對象輸出流寫對象,該對象是int類型
sout.writeInt(1000);//向對象輸出流寫對象,該對象是Int類型
//將對象輸出流中的對象全部推出緩衝區寫入文件
sout.flush();
sout.close();//關閉對象輸出流,釋放流佔用的資源
//創建文件輸入流對象,對象流文件從ObjectSeri.txt中讀取對象數據
FileInputStream in = new FileInputStream("D:/ObjectSeriOut.txt");
//創建對象輸入流,通過該流的各種方法讀取對象數據
ObjectInputStream sIn=new ObjectInputStream(in);
//通過對象輸入流從文件ObjectSeriOut.txt讀各種對象,並賦予對應類型的變量
String flag=(String)sIn.readObject();
Date date=(Date)sIn.readObject();
int i=(int)sIn.readInt();
System.out.println(flag+date);//輸出讀入的對象數據
System.out.println("int型數據:"+i);
in.close();//關閉對象輸入流,釋放輸入流佔用資源
//捕獲IOException異常和ClassNotFoundException異常
}catch(IOException ex){
System.out.println("IOException happend");
}catch(ClassNotFoundException f){
System.out.println("ClassNotFoundException happend");
}
}
}

運行結果:

current time is:Wed Dec 13 16:16:21 CST 2017
int型數據:1000

代碼說明:

在該程序中,在FileInputStream和FileOutputStream對象的基礎上創建了ObjectInputStream和ObjectOutputStream對象sln和sout。通過對象輸入、輸出實例調用相應的寫對象方法(writeObject())和讀對象方法(readObject())向文件寫入和讀出對象數據。類ObjectInputStream提供了多個讀入簡單對象的方法,如writeInt()、writeFloat()和writeBoolean()。類ObjectOutputStream提供了多個寫入簡單對象的方法,如readInt()、readFloat()和readBoolean()。



發佈了38 篇原創文章 · 獲贊 16 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章