流的概念
程式中的輸入輸出都是以流形式,流中儲存的實際上都是位元組檔案。
位元組流與字元流
位元組流的操作:
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() ; } } } }