流的概念
程序中的輸入輸出都是以流形式,流中保存的實際上都是字節文件。
字節流與字符流
字節流的操作:
1)輸入:inputStream,
2)輸出:outPutStream;
字符流的操作:
1)輸入主要使用:write類。
2)輸出主要使用:reader類。
內容操作就四個類。
操作流程:
使用File類操作一定有路徑問題,注意分隔符:
實際上四個操作類都是抽象類(區別接口,抽象類的成員都是抽象,並且只能單繼承,接口可以有全局變量,但是接口可以多繼承)
IO操作屬於資源操作,對於資源操作,操作最后必須關閉,否則有可能出現未知錯誤。
字節流
字節流主要操作byte類型數據,以byte數組為准,注意操作類為字節輸出流:outputStream,字節輸入流:inputStream,類。
Byte是字節,肯定使用字節流操作,所有的數據基本都可以使用byte數組表示出來。
OutputStream類
字節輸出流的最大父類:定義如下:
public abstract class OutputStream extends Object implements Closeable ,Flushable;
Closeable表示可以關閉的操作,因為程序最后要關閉。
Flushable表示刷新,清空內存中數據。
此類是抽象類,所以要想使用此類,必須要通過子類實例化父類對象。
那么要操作的是一個文件,則可以使用FileOutputStream類,通過向上轉型之后,可以為OutputStream實例化。
FileOutputStream構造方法:
public FileOutputStream( File file) throws FielNotFoundException
可見FileOutputStream需要操作File類的對象,並且有異常處理。
import java.io.File ; import java.io.OutputStream ; import java.io.FileOutputStream ; public class OutputStreamDemo01{ public static void main(String args[]) throws Exception{ // 異常拋出,不處理 // 第1步、使用File類找到一個文件 File f= new File("d:" + File.separator + "test.txt") ; // 聲明File對象 // 第2步、通過子類實例化父類對象,注意下面是通過向上轉型,實例化父類對象。 OutputStream out = null ; // 准備好一個輸出的對象 out = new FileOutputStream(f) ; // 通過對象多態性,進行實例化 // 第3步、進行寫操作 String str = "Hello World!!!" ; // 准備一個字符串 byte b[] = str.getBytes() ; // 只能輸出byte數組,所以將字符串變為byte數組 out.write(b) ; // 將內容輸出,保存文件 // 第4步、關閉輸出流 out.close() ; // 關閉輸出流 } };
1)因為FielOutputStream有異常,wire()方法也有異常,為了節省操作,可以在主方法拋出異常,不處理
2)輸出字符串:可以先用方法 getBytes()一次性讀取字符串到字節數組中,然后再把字節數組輸出。
3)OutputStream的實例對象調用write(byte []b)方法,把數組輸出。
4)在操作時候,如果文件本身不存在,就會為用戶自動創建新文件。
在操作輸出流的時候也可以使用write(int i)的方式寫出數據。
import java.io.File ; import java.io.OutputStream ; import java.io.FileOutputStream ; public class OutputStreamDemo02{ public static void main(String args[]) throws Exception{ // 異常拋出,不處理 // 第1步、使用File類找到一個文件 File f= new File("d:" + File.separator + "test.txt") ; // 聲明File對象 // 第2步、通過子類實例化父類對象 OutputStream out = null ; // 准備好一個輸出的對象 out = new FileOutputStream(f) ; // 通過對象多態性,進行實例化 // 第3步、進行寫操作 String str = "Hello World!!!" ; // 准備一個字符串 byte b[] = str.getBytes() ; // 只能輸出byte數組,所以將字符串變為byte數組 for(int i=0;i<b.length;i++){ // 采用循環方式寫入 out.write(b[i]) ; // 每次只寫入一個內容 } // 第4步、關閉輸出流 out.close() ; // 關閉輸出流 } };
以上操作在輸入數據后,以前的數據不存在。因為在IO操作中默認是將其覆蓋。如果要想執行追加功能,必須設置追加操作。
找到FileOutputStream類:
public FileOutputStream(File file,bool append)throws FileNotFoundException
在構造方法中,如果append的值設為true,表示在文件的末尾追加。
import java.io.File ; import java.io.OutputStream ; import java.io.FileOutputStream ; public class OutputStreamDemo03{ public static void main(String args[]) throws Exception{ // 異常拋出,不處理 // 第1步、使用File類找到一個文件 File f= new File("d:" + File.separator + "test.txt") ; // 聲明File對象 // 第2步、通過子類實例化父類對象 OutputStream out = null ; // 准備好一個輸出的對象 out = new FileOutputStream(f,true) ; // 此處表示在文件末尾追加內容 // 第3步、進行寫操作 String str = "Hello World!!!" ; // 准備一個字符串 byte b[] = str.getBytes() ; // 只能輸出byte數組,所以將字符串變為byte數組 for(int i=0;i<b.length;i++){ // 采用循環方式寫入 out.write(b[i]) ; // 每次只寫入一個內容 } // 第4步、關閉輸出流 out.close() ; // 關閉輸出流 } };
上面是在末尾追加,沒換行,如果要換行,在要添加的文子前面,使用“\r\n”完成。
如:String str="\r\n在這里追加";
package Thread1; import java.io.File; import java.io.FileOutputStream; import java.io.OutputStream; public class demo1 { // 所有的異常直接拋出,程序中不再進行處理 public static void main(String args[]) throws Exception{ File f=new File("D:\\outputStream.txt"); OutputStream out=new FileOutputStream(f,true); String str="\r\n在這里追加"; byte[]b=str.getBytes(); out.write(b); out.close(); } };
運行結果:
字節輸入流InputStream
定義:public abstract class inputStream extends Object implements Clossablle;
與OutputStream類似,也是抽象類,必須依靠子類,如果從文件讀取,肯定是FileInputStream;
構造方法:
public FileInputStream(File file) throws FileNotFoundException
要處理異常,參數需要File對象,向上轉型。
以read(byte[] b)形式讀取到數組中。
package Thread1; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; public class demo1 { public static void main(String args[]) throws Exception{ // 異常拋出,不處理 // 第1步、使用File類找到一個文件 File f= new File("d:" + File.separator + "outputStream.txt") ; // 聲明File對象 // 第2步、通過子類實例化父類對象 InputStream input=null; // 准備好一個輸入的對象 input = new FileInputStream(f) ; // 通過對象多態性,進行實例化 // 第3步、進行讀操作 byte b[] = new byte[1024] ; // 所有的內容都讀到此數組之中 input.read(b) ; // 讀取內容 // 第4步、關閉輸出流 input.close() ; // 關閉輸出流 System.out.println("內容為:" + new String(b)) ; // 把byte數組變為字符串輸出 } };
運行結果:
內容為:hello moto在這里追加
在這里追加
1)定義一個byte類型的數組對象,用於讀取從File類的指定文件的內容。
2)InputStream通過子類,利用多態性,向上轉型,實例化對象。
3)InputStream對象調用read(byte b[])方法,把File中的內容讀取給b[]數組。
此刻雖然讀取出來了,但是存在問題,文件沒有這么大,但是開辟了這么大空間,浪費內存。
能不能根據文件大小開辟數組空間?
如果要想知道文件大小,直接使用File類即可。f.length()方法獲得文件的大小,注意同時要類型轉換。
package Thread1; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; public class demo1 { public static void main(String args[]) throws Exception{ // 異常拋出,不處理 // 第1步、使用File類找到一個文件 File f= new File("d:" + File.separator + "outputStream.txt") ; // 聲明File對象 // 第2步、通過子類實例化父類對象 InputStream input = null ; // 准備好一個輸入的對象 input = new FileInputStream(f) ; // 通過對象多態性,進行實例化 // 第3步、進行讀操作 byte b[] = new byte[(int)f.length()] ; // 數組大小由文件決定 int len = input.read(b) ; // 讀取內容 // 第4步、關閉輸出流 input.close() ; // 關閉輸出流\ System.out.println("讀入數據的長度:" + len) ; System.out.println("內容為:" + new String(b)) ; // 把byte數組變為字符串輸出 } };
運行結果:
讀入數據的長度:32
內容為:hello moto在這里追加
在這里追加
new String(b),因為讀取的時候是從字符類型先轉化成byte類型,所以輸出要轉換成String類型。
這里其實是String類的構造方法,相當於實例化了一個String類的對象,生成了一個String 變量。
以上是利用byte數組形式讀取文件。read(byte [] b)
另外一個常用的讀取方法read();字節一個個的讀
abstract int read() 從輸入流中讀取數據的下一個字節
注意:read()方法的返回結果是int類型,所以對於字節需要轉化類型,
package 類集; import java.io.File ; import java.io.InputStream ; import java.io.FileInputStream ; public class InputStreamDemo04{ public static void main(String args[]) throws Exception{ // 異常拋出,不處理 // 第1步、使用File類找到一個文件 File f= new File("d:" + File.separator + "test.txt") ; // 聲明File對象 // 第2步、通過子類實例化父類對象 InputStream input = null ; // 准備好一個輸入的對象 input = new FileInputStream(f) ; // 通過對象多態性,進行實例化 // 第3步、進行讀操作 byte b[] = new byte[(int)f.length()] ; // 數組大小由文件決定 for(int i=0;i<b.length;i++){ b[i] = (byte)input.read() ; // 讀取內容 } // 第4步、關閉輸出流 input.close() ; // 關閉輸出流\ System.out.println("內容為:" + new String(b)) ; // 把byte數組變為字符串輸出 } };
以上操作只適合知道輸入流大小的時候,如果現在不知道大小呢?
package 類集; import java.io.File ; import java.io.InputStream ; import java.io.FileInputStream ; public class InputStreamDemo05{ public static void main(String args[]) throws Exception{ // 異常拋出,不處理 // 第1步、使用File類找到一個文件 File f= new File("d:" + File.separator + "te.text") ; // 聲明File對象 // 第2步、通過子類實例化父類對象 InputStream input = null ; // 准備好一個輸入的對象 input = new FileInputStream(f) ; // 通過對象多態性,進行實例化 // 第3步、進行讀操作 byte b[] = new byte[1024] ; // 數組大小由文件決定 int len = 0 ; int temp = 0 ; // 接收每一個讀取進來的數據 while((temp=input.read())!=-1){ //這里先把字節讀取出來,賦值給temp,如果temp不等於-1,表示還有內容,文件沒有讀完 b[len] = (byte)temp ; len++ ; } // 第4步、關閉輸出流 input.close() ; // 關閉輸出流\ System.out.println("內容為:" + new String(b,0,len)) ; // 把byte數組變為字符串輸出 } };
當不知道讀取的內容有多大的時候,就只能通過以讀取的內容是否為-1為讀完的標志。
這個是常用的字節流操作方法:
InputStream input = null ; // 准備好一個輸入的對象 input = new FileInputStream(f) ; // 通過對象多態性,進行實例化 // 第3步、進行讀操作 byte b[] = new byte[1024] ; // 數組大小由文件決定 int len = 0 ; int temp = 0 ; // 接收每一個讀取進來的數據 while((temp=input.read())!=-1){ //這里先把字節讀取出來,賦值給temp,如果temp不等於-1,表示還有內容,文件沒有讀完 b[len] = (byte)temp ; len++ ; }
字符流操作
字符輸入流:Writer類
字符輸出流:Reader類。
Writer類
常用方法:
字符流比字節流操作好在一點,就是可以直接輸出字符串了。不用跟之前一樣進行轉換操作。
代碼實例:
import java.io.File ; import java.io.Writer ; import java.io.FileWriter ; public class WriterDemo01{ public static void main(String args[]) throws Exception{ // 異常拋出,不處理 // 第1步、使用File類找到一個文件 File f= new File("d:" + File.separator + "test.txt") ; // 聲明File對象 // 第2步、通過子類實例化父類對象 Writer out = null ; // 准備好一個輸出的對象 out = new FileWriter(f) ; // 通過對象多態性,進行實例化 // 第3步、進行寫操作 String str = "Hello World!!!" ; // 准備一個字符串,注意,這里不用跟字節流操作一樣轉換成Byte數組類型。 out.write(str) ; // 將內容輸出,保存文件 // 第4步、關閉輸出流 out.close() ; // 關閉輸出流 } };
使用字符流默認情況下依然覆蓋已有文件,要想追加,則在FileWriter上加一個追加標記即可。
package 類集; import java.io.File ; import java.io.Writer ; import java.io.FileWriter ; public class WriterDemo02{ public static void main(String args[]) throws Exception{ // 異常拋出,不處理 // 第1步、使用File類找到一個文件 File f= new File("d:" + File.separator + "te.text") ; // 聲明File對象 // 第2步、通過子類實例化父類對象 Writer out = null ; // 准備好一個輸出的對象 out = new FileWriter(f,true) ; // 通過對象多態性,進行實例化 // 第3步、進行寫操作 String str = "\r\nLIXINGHUA\r\nHello World!!!" ; // 准備一個字符串 out.write(str) ; // 將內容輸出,保存文件 // 第4步、關閉輸出流 out.close() ; // 關閉輸出流 } };
字符輸入流:Reader()
常用方法
以字符數組形式讀取出數組。
package 類集; import java.io.File ; import java.io.Reader ; import java.io.FileReader ; public class ReaderDemo01{ public static void main(String args[]) throws Exception{ // 異常拋出,不處理 // 第1步、使用File類找到一個文件 File f= new File("d:" + File.separator + "te.text") ; // 聲明File對象 // 第2步、通過子類實例化父類對象 Reader input = null ; // 准備好一個輸入的對象 input = new FileReader(f) ; // 通過對象多態性,進行實例化 // 第3步、進行讀操作 char c[] = new char[1024] ; // 所有的內容都讀到此數組之中 int len = input.read(c) ; // 讀取內容 // 第4步、關閉輸出流 input.close() ; // 關閉輸出流 System.out.println("內容為:" + new String(c,0,len)) ; // 把字符數組變為字符串輸出 } };
輸出結果:
內容為:hello fsdfsdfshello
LIXINGHUA
Hello World!!!
也可以以循環方式,通過文件是否讀到底判斷。
import java.io.File ; import java.io.Reader ; import java.io.FileReader ; public class ReaderDemo02{ public static void main(String args[]) throws Exception{ // 異常拋出,不處理 // 第1步、使用File類找到一個文件 File f= new File("d:" + File.separator + "test.txt") ; // 聲明File對象 // 第2步、通過子類實例化父類對象 Reader input = null ; // 准備好一個輸入的對象 input = new FileReader(f) ; // 通過對象多態性,進行實例化 // 第3步、進行讀操作 char c[] = new char[1024] ; // 所有的內容都讀到此數組之中 int temp = 0 ; // 接收每一個內容 int len = 0 ; // 讀取內容 while((temp=input.read())!=-1){ // 如果不是-1就表示還有內容,可以繼續讀取 c[len] = (char)temp ; len++ ; } // 第4步、關閉輸出流 input.close() ; // 關閉輸出流 System.out.println("內容為:" + new String(c,0,len)) ; // 把字符數組變為字符串輸出 } };
字符流與字節流的區別
字節和字符使用非常相似,有哪些不同呢?
1,字節流不會用到緩存,字符流會用到緩存。
通過代碼驗證字節流使用到了緩存。
字節流操作:
package 類集; import java.io.File ; import java.io.OutputStream ; import java.io.FileOutputStream ; public class OutputStreamDemo05{ public static void main(String args[]) throws Exception{ // 異常拋出,不處理 // 第1步、使用File類找到一個文件 File f= new File("d:" + File.separator + "te.text") ; // 聲明File對象 // 第2步、通過子類實例化父類對象 OutputStream out = null ; // 准備好一個輸出的對象 out = new FileOutputStream(f) ; // 實例化 // 第3步、進行寫操作 String str = "Hello World!!!" ; // 准備一個字符串 byte b[] = str.getBytes() ; // 只能輸出byte數組,所以將字符串變為byte數組 out.write(b) ; // 寫入數據 // 第4步、關閉輸出流 // out.close() ; // 關閉輸出流 } };
結果在指定目錄成功輸出內容。
在使用字節流操作中,即使沒有關閉,最終也可以輸出。
字符流操作:
package 類集; import java.io.File ; import java.io.Writer ; import java.io.FileWriter ; public class WriterDemo03{ public static void main(String args[]) throws Exception{ // 異常拋出,不處理 // 第1步、使用File類找到一個文件 File f= new File("d:" + File.separator + "te.text") ; // 聲明File對象 // 第2步、通過子類實例化父類對象 Writer out = null ; // 准備好一個輸出的對象 out = new FileWriter(f) ; // 通過對象多態性,進行實例化 // 第3步、進行寫操作 String str = "Hello World!!!" ; // 准備一個字符串 out.write(str) ; // 將內容輸出,保存文件 // 第4步、關閉輸出流 // out.close() ; // 此時,沒有關閉 } };
運行結果:為空。
以上操作沒有輸出任何內容,所有的內容現在保存在緩沖區當中,如果執行關閉的時候,會強制性的刷新緩沖區,所以可以把內容輸出。
abstract void close() 關閉此流,但要先刷新它。
如果現在沒有關閉的話,也可以強制調用刷新方法。
abstract void flush() 刷新該流的緩沖。
package 類集; import java.io.File ; import java.io.Writer ; import java.io.FileWriter ; public class WriterDemo03{ public static void main(String args[]) throws Exception{ // 異常拋出,不處理 // 第1步、使用File類找到一個文件 File f= new File("d:" + File.separator + "te.text") ; // 聲明File對象 // 第2步、通過子類實例化父類對象 Writer out = null ; // 准備好一個輸出的對象 out = new FileWriter(f) ; // 通過對象多態性,進行實例化 // 第3步、進行寫操作 String str = "Hello World!!!" ; // 准備一個字符串 out.write(str) ; // 將內容輸出,保存文件 // 第4步、關閉輸出流 out.flush() ; // 強制性清空緩沖區中的內容 // out.close() ; } };
此時成功將內容寫入指定路徑下文件中。
開發中是使用字節流好還是字符流好。
在所有的硬盤中保存文件或進行傳輸的時候,都是以字節的方式進行的,包括圖片,視頻等多媒體也是以字節進行。
而字符是只有在內存中才會形成的,所以使用字節的時候是最多的。
操作范例:文件拷貝
操作的時候可以按照如下格式進行:
java Copy 源文件 目標文件
如果要采用以上的格式,則肯定要使用初始化參數的形式,輸入兩個路徑。所以此時必須對輸入的參數個數進行驗證,判斷其是否為2,
是使用字節流還是字符流呢,肯定使用字節流,因為萬一拷貝的是圖片呢?
要完成拷貝程序,有兩種方式:
實現一,將源文件中內容全部讀取進來,之后一次性寫入到目標文件。
實現二,邊讀邊寫。
很明顯第二種方式更好。
package 類集; import java.io.* ; public class Copy{ public static void main(String args[]){ if(args.length!=2){ // 判斷是否是兩個參數 System.out.println("輸入的參數不正確。") ; System.out.println("例:java Copy 源文件路徑 目標文件路徑") ; System.exit(1) ; // 系統退出 } File f1 = new File(args[0]) ; // 源文件的File對象 File f2 = new File(args[1]) ; // 目標文件的File對象 if(!f1.exists()){ System.out.println("源文件不存在!") ; System.exit(1) ; } InputStream input = null ; // 准備好輸入流對象,讀取源文件 OutputStream out = null ; // 准備好輸出流對象,寫入目標文件 try{ input = new FileInputStream(f1) ; }catch(FileNotFoundException e){ e.printStackTrace() ; } try{ out = new FileOutputStream(f2) ; }catch(FileNotFoundException e){ e.printStackTrace() ; } if(input!=null && out!=null){ // 判斷輸入或輸出是否准備好 int temp = 0 ; try{ while((temp=input.read())!=-1){ // 開始拷貝 out.write(temp) ; // 邊讀邊寫 } System.out.println("拷貝完成!") ; }catch(IOException e){ e.printStackTrace() ; System.out.println("拷貝失敗!") ; } try{ input.close() ; // 關閉 out.close() ; // 關閉 }catch(IOException e){ e.printStackTrace() ; } } } }