I/O流可謂是博大精深呀,今天總結一下今天所學到的東西吧:
首先理解一下I/O流的概念吧,Java的I/O流是實現輸入、輸出的基礎,它可以方便實現數據的輸入、輸出操作,Java中把不同的輸入、輸出流抽象地表述爲“流”,通過流的方式允許Java程序使用相同的方式來訪問不同的輸入、輸出流。InputStream和OutputStream是兩個處理字節流的基礎的類(且爲抽象類),而用於處理字符流的兩個基礎的類是Reader和Writer,所有其他的流類都是以這4個類爲基礎的。
按不同的方式可以把流分爲不同的類:
1.按照流的方向分爲輸入流和輸出流
輸入流:只能從中讀取數據,而不能向其中寫入數據(如InputStream、FileInputStream);
輸出流:只能向其中寫入數據,而不能向其中讀取數據(如OutputStream、FileOutputStream等);
2.按照流的角色分爲節點流和處理流
節點流:可以向一個特定的IO設備讀/寫數據的流,也稱作基礎流、低級流(FileInputStream、FileOutputStream)
處理流:實現對一個已存在的流的連接和封裝,通過所封裝的流的功能調用實現數據讀/寫功能的流,也稱作高級流、包裝流(如BufferedInputStream/BufferedOutputStream緩衝流DataInputStream/DataOutputStream數據流);它同時也是IO的精華—>基礎流中構造中參數是介質,而處理流中構造中參數是流。
下面看幾個實例吧:
第一部分:先看看基礎流吧!
實例一:InputStream和OutputStream
這兩個流是抽象類,提供了輸入/輸出處理的基本接口,並且實現了其中的一些方法,他們讀/寫流的方式是以字節爲單位進行的。
InputStream用於從源按照字節讀取數據,它定義了一下一些方法:
int read():從輸入流中讀取數據的下一個字節,並將它返回。如果遇到源的末尾,則返回-1,可以利用這一點判斷是否已經到了流的末尾;
int read(byte[] buffer):將數據讀入一個字節數組中,同時返回讀取的字節數。同樣如果遇到源的末尾,則返回-1;
int read(byte[] buffer,int offset,int length):將數據讀入一個字節數組,放到數組的offset指定的位置開始,並用length來指定讀取的最大字節數同樣如果遇到源的末尾,則返回-1;
void close():用於關閉該流,在使用完流以後,一定要記得使用此方法關閉,否則數據可能不會寫入文件;
void close():用於沖刷數據流,將數據寫入文件,close方法中會先執行此方法。
*注意一點:緩衝區是採用數據覆蓋的形式進行傳遞數據的。
下面看一個代碼:
package day11.io; import java.io.*; public class TestTestInputStream { public static void main(String[] args) throws Exception { int size; InputStream is = new FileInputStream("E:\\test.txt");//input流需要先創建文件 System.out.println("可讀取 的字節:"+(size=is.available())); byte[] t = new byte[200]; for(int i=0;i<size;i++){ t[i] = ((byte)is.read()); System.out.println(t[i]); } System.out.println(); OutputStream os = new FileOutputStream("E:\\test.txt"); byte[] c = new byte[200]; for(int j = 0;j<200;j++){ c[j] =(byte)(j); os.write(c[j]); } os.close(); is.close(); } }
上述代碼中用了try、catch直接處理了IO異常,調用read方法時,文件必須存在,調用write方法時會自動創建一個文件。
實例二:FileInputStream和FileOutputStream
他們分別是InputStream和OutputStream的子類,在生成FileInputStream和FileOutputStream類的對象時,如果指定的文件找不到,都會產生FileNotFoundException異常,因此必須捕獲或拋出異常。
下面看一個代碼:
package day11.io; import java.io.*; public class TestIo { public static void main(String[] args) { InputStream is = null; OutputStream os = null; try { //使用read方法時,文件必須存在 //write方法會自動創建一個文件(如果沒有這個文件) os = new FileOutputStream("D:\\2.txt"); os.write(98); byte[] c = new byte[]{1,2,3}; os.write(c); System.out.println(); is = new FileInputStream("D:\\2.txt");//會報錯java.io.FileNotFoundException: int i = is.read(); System.out.println(i); System.out.println(c.length); } catch (IOException e) { e.printStackTrace(); }finally{ try { is.close(); os.close(); } catch (IOException e) { e.printStackTrace(); } } } }
實例三:FileInputStream和FileOutputStream進行文件內容複製,利用write是可以創建文件的特點,複製出另一個內容一模一樣的文件,爲了提高複製效率,引進了數組,如果不寫flush() 和close(),那麼複製出的文件會變小,如果write中參數不加後面兩個,那麼複製出的文件會變大。下面看一下例子
package blog; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; public class TestCopy { public static void main(String[] args) throws Exception { File file1 = new File("E:\\test.txt"); File file2 = new File("E:\\tt.txt"); FileInputStream fis = new FileInputStream(file1); FileOutputStream fos = new FileOutputStream(file2); int i; byte[] buff = new byte[8*1024]; while((i=fis.read(buff))!=-1){ fos.write(buff,0,i); } System.out.println("succees!"); System.out.println("file1的大小爲:"+file1.getUsableSpace()+" "); System.out.println("file2的大小爲:"+file2.getUsableSpace()+" "); // file1.delete();完成了剪切功能 fis.close(); fos.close(); } }
輸出結果如下所示:
succees! file1的大小爲:73930264576 file2的大小爲:73930264576
程序結合了InputStream和OutputStream的用法,通過一個FileInputStream從一個文件中讀出文件內容,然後將它作爲FileOutputStream的輸出,寫入另一個文本文件,這樣就完成了複製。
第二部分:接下來看看高級數據流^_^
高級流是對基礎流進行了封裝,並有許多功能的擴展,下面介紹幾個常用的高級數據流:
1.BufferedInputStream和BufferedOutputStream
實現了帶緩衝的過濾流,在讀寫的同事對數據進行緩存,這樣可以避免每次讀寫或發送數據時都要進行實際的物理讀寫操作,因此在輸入輸出時經常用到這兩個類。在用BufferOutputStream輸出時,數據先輸入緩衝區,緩衝區大小默認爲8K,當緩衝區滿時再寫入連接的輸出流,可以調用flush()方法來清空緩衝區。
看一個代碼理解一下:
實例四:
Buffered...有很多方法,在這裏就不一一列舉了,讀者可以查相應的api。
下面的方法複製了一個相同的文件:
package day11.io; import java.io.*; public class TestFileCopy { public static void main(String[] args) throws IOException { FileInputStream fis = new FileInputStream("D:\\1.txt"); FileOutputStream fos = new FileOutputStream("D:\\2.txt"); BufferedInputStream bis = new BufferedInputStream(fis); BufferedOutputStream bos = new BufferedOutputStream(fos); int i = 0; //默認讀的是8K,要是緩衝取滿了會自動寫入文件裏的 while((i=bis.read())!=-1){ bos.write(i); } // bos.flush();//沖刷緩衝區,強制將緩衝區中的內容寫入文件 // bos.close();//關閉緩衝區在close之前會先flush的 } }
下面說一下System.setOut();方法,它可以控制輸出方式,即在控制檯輸出還是在文件中輸出。
package day11.io; import java.io.*; public class TestSISO { public static void main(String[] args) throws IOException { // 從控制檯讀一行 // Scanner是5.0之後提供的類。 InputStream is = System.in; InputStreamReader isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr); String s = br.readLine(); //需要在控制檯路數數據然後直接在控制帶顯示; System.out.println(s); //PrintStream 中的println方法經常和BufferedReader的readLine()配合使用。 PrintStream ps = new PrintStream(new File("D:\\a.txt")); System.out.println("控制檯輸出!"); System.setOut(ps); System.out.println("文件中輸出!"); ps.print("I'm coming!"); ps.print(s); } }
控制檯輸出如下:
wo wo 控制檯輸出!
文件中內容如下:
文件中輸出! I'm coming!
2.DataInputStream和DataOutputStream
它們同樣分別繼承自FilterInputStream和FilterOutputStream,可以用與計算機無關的格式讀寫Java的基本數據類型以及String對象,DataInputStream中對應的以read開頭,如readByte(),readDouble等等,write也同樣。
在上一個代碼的基礎上增加了此數據流的功能:
實例五:
package day11.io; import java.io.*; public class TestDataFlow { public static void main(String[] args) throws IOException { //Data數據流中包含了8種基本數據流的重載,可輸入8種,另外還可查看api還有別的 File file = new File("D:\\a.txt"); FileOutputStream fos = new FileOutputStream(file); DataOutputStream dos = new DataOutputStream(fos); BufferedOutputStream bos = new BufferedOutputStream(dos); dos.writeChar('M'); dos.writeDouble(1.34154); dos.writeUTF("家"); FileInputStream fis = new FileInputStream(file); DataInputStream dis = new DataInputStream(fis); BufferedInputStream bis = new BufferedInputStream(dis); char a = dis.readChar(); double d = dis.readDouble(); String s = dis.readUTF(); System.out.println(a+" "+s+" "+d); //只需要關閉外層的就可以啦,它會自動關閉內層的流 dis.close(); dos.close(); } } 輸出如下: M 家 1.34154
Data...數據流則比較人性化,多了很多方法,提供更多數據類型,writeUTF也提供了漢字的輸入~
3.ObjectInputStream和ObjectOutputStream
萬物皆OBJ,同樣數據流也有Object類型的,見聞知其意,即數據流中傳遞的是對象的類型,下面看個小例子:
實例六:
首先實體類如下:
package day11.io; import java.io.Serializable; public class Pet implements Serializable{ private String name; private int age; public Pet() { super(); // TODO Auto-generated constructor stub } public Pet(String name, int age) { super(); this.name = name; this.age = age; } @Override public String toString() { return "Pet [name=" + name + ", age=" + age + "]"; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + age; result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Pet other = (Pet) obj; if (age != other.age) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
實體類代碼中均未自動生成的方法,在寫這個的時候明確了一點概念:Collection和Serializable接口均無字段和方法,Compatable只有一個方法即compareTo(),然後寫一下爲其測試類,將對象的數據存入D:\\Pet.content的文件中,然後從中讀取Pet類的數據:
package day11.io; import java.io.*; public class TestPetOOSIOS { public static void main(String[] args) throws Exception { File file = new File("D:\\Student.content"); FileOutputStream fos = new FileOutputStream(file); ObjectOutputStream oos = new ObjectOutputStream(fos); Pet p1 = new Pet("七仔",5); oos.writeObject(p1); FileInputStream fis = new FileInputStream(file); ObjectInputStream ois = new ObjectInputStream(fis); Pet p2 = (Pet) ois.readObject(); System.out.println("MY Dog'name is "+p2.getName()+"! \r\nAnd his age is "+p2.getAge()+" years old!"); oos.close(); ois.close(); } 測試結果如下: MY Dog'name is 七仔! And his age is 5 years old!
同樣還可定義另一個類讓其成爲本實體類的屬性,然後同樣也就可以傳遞這個類的數據流了
4.Reader和Writer
它處理的是字符類型的數據流,它也分高級流和低級流;
Reader與InputStream相同用於從流中讀取數據,它的方法如下:
int read():用於從流中讀出一個字符,並將其返回。
int read():將從流中讀出的字符放到字符數組buffer中,返回讀出的字符數。
Int read(char[] buffer,int offset,int length):將讀出的字符放到字符數組的指定offset開始的空間,每次最多讀出length個字符。
Void close():關閉Reader流,在使用完Reader流後,一定記得將其關閉。
Boolean ready():判斷流是否已經準備好被讀取。
其他等方法可查看api文檔。
Writer與OutputStream類似,用於向流中寫入數據,它的方法如下:
void write(int c)
void write(char[] buffer)
void write(char[] buffer,int offset,int length)
void write(String string)
void write(String string,int offset,int length)
均爲write方法的重載,意義同以上的read方法差不多
void close(),void flush()方法與以前的相同。
實例七:
package day11.io; import java.io.*; public class TestReader { public static void main(String[] args) throws IOException { File file = new File("D:\\a.txt"); FileReader fr = new FileReader(file);//同一下兩句的效果相同,但前提是保證是gbk編碼 // FileInputStream fis = new FileInputStream(file); // InputStreamReader isr = new InputStreamReader(fis,"gbk"); char c = (char)fr.read(); System.out.println(c); //注意輸入編碼格式要一致 FileOutputStream fos = new FileOutputStream(file); OutputStreamWriter osw = new OutputStreamWriter(fos,"UTF-8"); osw.write("中國"); for(int i=0;i<65535;i++){ osw.write(i); } fr.close(); osw.close(); } }
下面一個是用了Readre和Writer的方法
package day11.io; import java.io.*; public class TestBuffered { public static void main(String[] args) throws IOException { File file = new File("D:\\a.txt");//得先有這個文件 FileInputStream fis = new FileInputStream(file); InputStreamReader isr = new InputStreamReader(fis); BufferedReader br = new BufferedReader(isr); String str = null; while((str = br.readLine())!=null){ System.out.println(str); } FileOutputStream fos = new FileOutputStream(file); OutputStreamWriter osw = new OutputStreamWriter(fos); BufferedWriter bos = new BufferedWriter(osw); bos.write("一二三四五"); bos.newLine();// 新起一行。 bos.write("六七八九十"); bos.flush();// 注意沖刷 bos.close(); br.close(); } }
在a.txt中輸出了幾乎所有的編碼。在這個學習中有明確了一點編碼小問題:對於漢字來說,gbk/gb2312佔兩個字節,utf-8佔三個字節。
I/O果然是個不同凡響的東西,內容好多,以後再慢慢聊吧,今天就到這樣了~晚安!