在程序中所有的數據都是以流的方式進行傳輸或者保存的,程序需要數據的時候需要使用輸入流讀取數據,而當程序需要將一些數據保存起來的時候,就要使用輸出流。
可以通過下面的輸入輸出流關系圖表示這種方式。
在java.io包中流的操作主要有字節流、字符流兩大類,並且兩個都具備輸入輸出的操作。
在字節流中輸出數據主要使用OutputStream類完成,輸入則是InputStream類。
在字符流中輸出數據主要使用Writer類完成,輸入則是Reader類。
任何一種流接口,它下面都會有多個實現類。下圖展示的是流之間的層次關系圖。
字節流:
字節流主要操作byte類型數據,以byte數組為准,主要操作類是OutputStream類和InputStream類。
1)字節輸出流:OutputStreame
OutputStream是整個IO包中字節輸出流最大的父類,此類的定義如下:
public abstract class OutputStream extends Object implements Closeable, Flushable
從這個定義中可以發現,OutputStream類是一個抽象類,如若要使用此類,首先就必須要通過子類來實例化對象。假設現在要操作的事一個文件,則可以使用FileOutputStream類,通過向上轉型后,可以為OutputStream實例化,在OutputStream類中的主要操作方法如下:
1 public void close() throws IOException //關閉輸出流 2 public void flush() throws IOException //刷新緩沖區 3 public void write(byte[] b) throws IOException //將一個byte數組寫入數據流 4 public void write(byte[] b,int off,int len) throws IOException //將一個指定范圍的byte數組寫入數據流 5 public abstract void write(int b) throws IOException //將一個字節數據寫入數據流
此時會用FileOutputStream子類,此類的構造方法如下:
public FileOutputStream(File file) throws FileNotFoundException
操作時必須接收File類實例,指明要輸出的文件路徑。
在OutputStream類的定義中可以發現此類實現了Closeable和Flushable兩個接口,這兩個接口的作用從其定義方法中可以得知,Closeable表示可關閉,Flushable表示可刷新,而且在OutputStream類中已經有了這兩個方法的實現,所以操作時一般不會關心這兩個接口,而直接使用OutputStream類就行了。下面舉一個例子:
1 import java.io.*; 2 3 /** 4 * Author : Rabbit Yulin 5 * Created on 2017/2/24 0024 上午 11:10 6 * Description : 向文件中寫入字符串 7 */ 8 public class OutputStreamDemo1 { 9 public static void main(String[] args) throws IOException { 10 /*使用File指定一個文件*/ 11 File f = new File("d:"+File.pathSeparator+"test.txt");//申明File對象 12 /*通過子類實例化父類*/ 13 OutputStream out; //輸出對象 14 out = new FileOutputStream(f); //通過對象多態性進行實例化 15 /*寫入操作*/ 16 String str = "Hello , World"; //字符串 17 byte b[] = str.getBytes(); //因為只能輸出byte數組,所以將字符串變為byte數組 18 out.write(b); //將內容輸出 19 /*關閉輸出流*/ 20 out.close(); 21 } 22 }
程序運行后可以發現D盤下面已經新增了一個test.txt的文件,打開查看的時候妥妥的內容是:“Hello , World”。上面的代碼在實例化、寫、關閉的時候都會有異常,為了方便起見所以在主方法上直接throws了IOException拋出異常,這樣可以減少try…catch語句。
可以很清楚的知道一個問題,那就是test.txt文件在程序操作之前是不存在的(當然如果硬是說“我創建了一個”那也沒辦法,反正會被覆蓋掉),程序會自動創建這個文件,並且將內容寫入到這個文件之中。將一個字符串變為byte的數組之后,然后使用write(byte[] b)的方式將byte數組直接寫入到文件中,當然也可以通過循環把每一個字節一個一個地寫入到文件中(其實並沒有什么不同,實現的功能都是一樣的,可以根據喜好隨意),同樣的,下面放出一個Demo:
1 import java.io.*; 2 /** 3 * Author : Rabbit Yulin 4 * Created on 2017/2/24 0024 上午 11:29 5 * Description : 6 */ 7 public class OutputStreamDemo2 { 8 public static void main(String[] args) throws IOException { 9 /*File指定一個文件*/ 10 File f = new File("d:"+File.pathSeparator+"test2.txt"); //申明對象 11 /*子類實例化父類*/ 12 OutputStream out; //輸出對象 13 out = new FileOutputStream(f); //實例化 14 /*寫操作*/ 15 String str = "Hello , Again"; 16 byte b[] = str.getBytes(); 17 for(int i = 0;i<b.length;i++){ 18 out.write(b[i]); 19 } 20 /*關閉輸出流*/ 21 out.close(); 22 } 23 }
2)追加內容
剛才有提到過,假若在指定的目錄之下已經有一個同名文件,在程序運行的時候該文件會被覆蓋。一樣的,如果再直接往文本中寫入內容,會造成原有數據的覆蓋。那么此時就可以通過FileOutputStream向文件中追加內容,FileOutputStream的另外一個構造方法:
public FileOutputStream(File file,boolean append) throws FileNotFoundException
在該構造方法中,如果將append的值設置為true,則表示在文件的末尾追加內容。如果要在文件之后的追加內容,是緊跟在原有的內容之后增加換行,使文件內容顯示更加清晰,則可以使用"\r\n"換行。
3)字節輸入流 InputStream
既然程序可以向文件寫入內容,則可以通過InputStream從文件中把數據讀取進程序當中。InputStream類的定義如下:
public abstract class InputStream extends Object implements Closeable
與OutputStream類一樣,InputStream本身也是一個抽象類,必須依靠子類。如果現在從文件中讀取,子類肯定是FileInputStream。InputStream類中的主要方法如下:
public int availabel() throws IOException //可以取得輸入文件的大小 public void close() throws IOException //關閉輸入流 public abstract int read() throws IOException //讀取內容,以數字方式讀取 public int read(byte[] b) throws IOException //將內容讀到byte數組中同時返回讀入的個數
通過一個例子來看看如何從文件中讀取內容:
import java.io.*; /** * Author : Rabbit Yulin * Created on 2017/2/24 0024 下午 1:46 * Description : */ public class InputStreamDemo1 { public static void main(String[] args) throws IOException { /*File指定一個文件*/ File f = new File("d:"+File.pathSeparator+"test.txt"); //申明File文件 /*子類實例化父類*/ InputStream is; //准備一個輸入對象 is = new FileInputStream(f); //通過對象多態性進行實例化 /*進行讀操作*/ byte[] b = new byte[1024]; //所有的內容讀到次數組中 is.read(b);/*關閉輸入流*/ is.close(); System.out.println("內容為:"+new String(b)); //把數組變為字符串 } }
雖然內容都被讀取進來了,但是有一個問題就是打印結果的后面有很多個空格。這是因為byte數組的大小有1024,然而實際的內容只有13個字節,也就是說還存在着1011個空白的弓箭,在將byte數組變為字符串時也將這1011個無用的空間轉為了字符串,這樣的操作肯定是不合理的。如果要想要解決以上的問題,則要觀察read的方法,在次方法上有一個返回值,此返回值表示想數組中寫入了多少個數據。因此把上面的程序修改一下的代碼如下:
1 import java.io.*; 2 3 /** 4 * Author : Rabbit Yulin 5 * Created on 2017/2/24 0024 下午 1:46 6 * Description : 7 */ 8 public class InputStreamDemo1 { 9 public static void main(String[] args) throws IOException { 10 /*File指定一個文件*/ 11 File f = new File("d:"+File.pathSeparator+"test.txt"); //申明File文件 12 /*子類實例化父類*/ 13 InputStream is; //准備一個輸入對象 14 is = new FileInputStream(f); //通過對象多態性進行實例化 15 /*進行讀操作*/ 16 byte[] b = new byte[1024]; //所有的內容讀到次數組中 17 int len = is.read(b); 18 /*關閉輸入流*/ 19 is.close(); 20 System.out.println("讀取的數據長度:"+len); 21 System.out.println("內容為:"+new String(b,0,len)); //把數組變為字符串 22 } 23 }
這樣處理之后的代碼再次運行,發現那些多余的空格就不會再產生了。因為程序在最后只是將byte數組指定范圍中的內容編程了字符串。需要注意的一點是,FileInputStream讀取時如果指定的路徑不存在,則程序會出現異常。
以上的這個問題解決的方式似乎還是有些不太完美,因為雖然最后指定了byte數組的范圍,但是程序依然開辟了很多的無用空間,這樣肯定會造成資源的浪費,那么此時能否根據根據文件的數據長度來開辟空間大小?查看了API可以得知File類中存在一個length()方法,此方法可以取得文件的大小,來開辟指定的byte數組空間。
1 import java.io.*; 2 3 /** 4 * Author : Rabbit Yulin 5 * Created on 2017/2/24 0024 下午 2:37 6 * Description : 7 */ 8 public class InputStreamDemo2 { 9 public static void main(String[] args) throws IOException { 10 File f = new File("d:"+ File.pathSeparator+"test.txt"); 11 InputStream is; 12 is = new FileInputStream(f); 13 byte[] b = new byte[(int)f.length()]; 14 is.read(b); 15 is.close(); 16 System.out.println("內容為:"+new String(b)); 17 } 18 }
這種讀取的方式其實適用范圍比較小,因為文本數據只要很大,那就造成一些未知的錯誤了。
字符流:
在程序中一個字符等於兩個字節,那么Java提供了Reader和Writer兩個專門操作字符流的類。
1)字符輸出流 Writer
Writer 本身是一個字符流的輸出類,此類的定義如下:
public abstract calss Writer extends Object implements Appendable,Closeable,Flushable;
此類也是一個抽象類,如果要使用這個類則需要使用其子類,這個時候如果要往文件中寫入數據,應該使用FileWriter的子類。Writer類的常用方法如下:
public abstract void close() throws IOException //關閉輸出流 public void write(String str) throws IOException //將字符串輸出 public void write(char[] cbuf) throws IOException //將字符數組輸出 public abstract void flush() throws IOException //強制性清空緩存
FileWriter類的構造方法定義如下:
public FileWriter(File file) throws IOException
在Writer類中除了實現Closeable和Flushable兩個接口之外,還實現一個Appendable接口,此接口表示的是內容可以被追加,接受的參數是CharSequence,實際上String類就實現了此接口,所以可以直接通過次接口的方法向輸出流中追加內容。例如:
1 import java.io.File; 2 import java.io.FileWriter; 3 import java.io.IOException; 4 import java.io.Writer; 5 6 /** 7 * Author : Rabbit Yulin 8 * Created on 2017/2/24 0024 下午 3:31 9 * Description : 10 */ 11 public class WriterDemo1 { 12 public static void main(String[] args) throws IOException { 13 File f = new File("d:"+File.pathSeparator+"test.txt"); 14 Writer out; 15 out = new FileWriter(f); 16 String str = "Hello,World"; 17 out.write(str); 18 out.flush(); 19 out.close(); 20 } 21 }
整個代碼看下來和OutputStream沒有太大的區別,唯一的好處就是可以直接輸出字符串,而不用再將字符串變為byte數組之后再輸出。
2)使用FileWriter追加文件的內容
在使用字符流操作的時候,也可以實現文件的追加功能,直接使用FileWriter類黃總的一下構造,就可以實現追加:
public FileWriter(File file,boolean append) throws IOException
將append的值設置為truw,表示追加。
3)字符輸入流Reader
Reader是使用字符的方式從文件中取出數據,Reader類的定義如下:
public abstract class Reader extends Object implement Readable,Closeable
Reader不用說,也是一個抽象類,如果要在從文件中刦內容時,則可以直接使用FileReader子類。Reader類的常用方法:
1 public abstract void close() throws IOException //關閉輸出流 2 public int read() throws IOException //讀取單個字符 3 public int read(char[] cbuf) throws IOException //將內容讀到字符數組內,返回讀入的長度
FileReader的構造方法定義如下:
public FileReader(File file) throws FileNotFoundException
以下放出實例來讀取文件中的文本內容
1 import java.io.*; 2 3 /** 4 * Author : Rabbit Yulin 5 * Created on 2017/2/24 0024 下午 3:51 6 * Description : 7 */ 8 public class ReaderDemo1 { 9 public static void main(String[] args) throws IOException { 10 File f = new File("d:"+File.pathSeparator+"test.txt"); 11 Reader reader = new FileReader(f); 12 char[] c = new char[1024]; 13 int len = reader.read(c); 14 reader.close(); 15 System.out.println("內容為:"+new String(c,0,len)); 16 } 17 }
如果此時不知道數據的長度,也可以使用循環的方式進行內容的讀取:
1 import java.io.*; 2 3 /** 4 * Author : Rabbit Yulin 5 * Created on 2017/2/24 0024 下午 3:54 6 * Description : 7 */ 8 public class ReaderDemo2 { 9 public static void main(String[] args) throws IOException { 10 File f = new File("d:"+ File.pathSeparator+"test.txt"); 11 Reader reader = new FileReader(f); 12 int len = 0; 13 char[] c = new char[1024]; 14 int temp = 0; 15 while((temp = reader.read()) != -1){ 16 c[len] = (char)temp; 17 len++; 18 } 19 reader.close(); 20 System.out.println("內容為:"+ new String(c,0,len)); 21 } 22 }