1. 程式人生 > 實用技巧 >第十四章、IO流

第十四章、IO流

IO流

1. 位元組流

定義:是按照位元組進行操作的,每次讀取一個位元組
位元組流抽象基類:
	InputStream(位元組輸入流)/OutputStream(位元組輸出流)
子類:
    (1).FileInputStream
        public int read()
             從此輸入流中讀取一個數據位元組。
        public int read(byte[] b)
             從此輸入流中將最多 b.length 個位元組的資料讀入一個 byte 陣列中。
        public int read(byte[] b,int off,int len)
             從此輸入流中將最多 len 個位元組的資料讀入一個 byte 陣列中。off:目標陣列 b 中的起始偏移量。

	(2).FileOutputStream
		public void write(int b)
		public void write(byte[] b)
		public void write(byte[] b,int off,int len)
		    指定 byte 陣列中從偏移量 off 開始的 len 個位元組寫入此檔案輸出流。

	(3).BufferedInputStream:位元組緩衝輸入流
	(4).BufferedOutputStream:位元組緩衝輸出流
  • FileInputStream(位元組輸入流)使用方法

    package demo1;
    
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.InputStream;
    		
    public class InputStreamDemo {
    	public static void main(String[] args) throws Exception {
    		
    		// 1.建立位元組輸入流物件
    		InputStream is = new FileInputStream("鬥破蒼穹.txt");
    		
    		// 2.對檔案進行讀取
    		
    		System.out.println("----------第一種讀取方式-----------");
    		
    		// read()方法  當檔案讀取到末尾的時候返回-1
    		
    		long startTime = System.currentTimeMillis();int num = 0;
    		while((num = is.read()) != -1){
    			System.out.print((char)num);
    		}
    		long endTime = System.currentTimeMillis();
    		System.out.println(endTime - startTime);
    				
    		System.out.println("----------第二種讀取方式-----------");
    		
    		long startTime = System.currentTimeMillis();
    		// 緩衝區  比作一個可以放1024本書的書架
    		byte[] by = new byte[1024];
    		
    		int num = 0;
    		String str = new String();
    		while((num = is.read(by)) != -1){
    			// 將讀取到的位元組序列轉換成字元序列
    			str = new String(by,0,num);
    		}
    		System.out.println(str);
    		
    		long endTime = System.currentTimeMillis();
    		System.out.println(endTime - startTime);
    		
    		System.out.println("----------第三種讀取方式-----------");
    		
    		long startTime = System.currentTimeMillis();
    		// 緩衝區  比作一個可以放1024本書的書架
    		byte[] by = new byte[1024];
    		
    		int num = 0;
    		String str = new String();
    		while((num = is.read(by,0,by.length)) != -1){
    			// 將讀取到的位元組序列轉換成字元序列
    			str = new String(by,0,num);
    		}
    		System.out.println(str);
    		
    		long endTime = System.currentTimeMillis();
    		System.out.println(endTime - startTime);
            
            // 關閉流並釋放資源
            is.close();
    	}
    }
    
  • FileOutputStream(位元組輸出流)使用方法

    package demo1;
    
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.OutputStream;
    
    public class OutputStreamDemo {
    	public static void main(String[] args) throws Exception {
    		// 建立位元組流輸出物件   如果存在就不會建立檔案
    		// OutputStream os = new FileOutputStream("鬥破蒼穹.txt"); // 預設寫入資料時會覆蓋原來的資料
    		
    		// 向檔案中追加內容
    		OutputStream os = new FileOutputStream("鬥破蒼穹.txt",true); // 追加
    		
    		// 第一種方式寫入:public void write(int b)
    		os.write(97);
    		
    		// 第二種方式寫入:public void write(byte[] b)
    		for (int i = 0; i < 3; i++) {
    			os.write("helloworld\n".getBytes());
    		}
    		
    		// 第三種方式寫入:public void write(byte[] b,int off,int len)
    		os.write("helloworld".getBytes(), 0, 3);
    		
    		// 釋放資源
    		// 關閉此檔案輸出流並釋放與此流有關的所有系統資源
    		os.close();
    		
    		// 為什麼一定要close呢?
    		// A.讓流物件變成垃圾,這樣就可以被垃圾回收機制回收了
    		// B.通知系統去釋放該檔案相關資源
    	}
    }
    
  • FileInputStream/FileOutputStream案例

    package demo1;
    
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    
    /*
     * 1.實現對.txt結尾的檔案進行復制
     * 2.實現對音訊檔案進行復制
     * 
     * 
     * 		檔案複製的過程
     * 			1.先讀取檔案
     * 			2.再向指定目錄輸出檔案
     */
    public class InToOutDemo {
    	public static void main(String[] args) throws Exception {
    		// 1.先讀取檔案
    		FileInputStream fis = new FileInputStream("鬥破蒼穹.txt");
    		
    		// 2.再向指定目錄輸出檔案
    		FileOutputStream fos = new FileOutputStream("E:\\豆豆.txt");
    		
    		byte[] by = new byte[1024];
    		int num = 0;
    		
    		// 讀取檔案
    		while((num = fis.read(by)) != -1){
    			// 美都區一次向指定檔案中寫入一次
    			fos.write(by,0,num);
    			fos.flush(); // 重新整理緩衝區 
    		}
    		
    		// 關閉流
    		fis.close();
    		fos.close();
    	}
    }
    
  • BufferedInputStream(位元組緩衝輸入流)使用方法

    package demo2;
    
    import java.io.BufferedInputStream;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    
    public class bufferedInputStreamDemo {
    	public static void main(String[] args) throws Exception {
    		BufferedInputStream bis = new BufferedInputStream(new FileInputStream("鬥破蒼穹.txt"));
    		
    		byte[] by = new byte[1024];
    		int num = 0;
    		
    		// 讀取資料
    		while((num = bis.read(by)) != -1){
    			System.out.println(new String(by, 0, num));
    		}
    		
    		// 釋放資源
    		bis.close();
    	}
    }
    
  • BufferedOutputStream(位元組緩衝輸出流)使用方法

    package demo2;
    
    import java.io.BufferedOutputStream;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    
    public class bufferedOutputStreamDemo {
    	public static void main(String[] args) throws Exception {
    		// 複雜構造方法寫法(屬於脫褲子放屁)
    		FileOutputStream fos = new FileOutputStream("鬥破蒼穹.txt");
    		BufferedOutputStream bos = new BufferedOutputStream(fos);
    		
    		// 簡單寫法
    		BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("鬥破蒼穹.txt"));
    		bos.write("hello world".getBytes());
    		
    		// 釋放資源
    		bos.close();
    	}
    }
    
  • BufferedInputStream/BufferedOutputStream案例

    package demo2;
    
    import java.io.BufferedInputStream;
    import java.io.BufferedOutputStream;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    
    /*
     * 需求:實現對音訊檔案複製
     * 
     * 		位元組流四種方式複製檔案
     * 			基本位元組流一次讀寫一個位元組:38549毫秒 
     * 			基本位元組流一次讀寫一個位元組陣列:61毫秒
     * 			高效位元組流一次讀寫一個位元組:329毫秒
     * 			高效位元組流一次讀寫一個位元組陣列:37毫秒
     */
    public class CopyMp4Demo {
    	public static void main(String[] args) throws Exception {
    		long startTime = System.currentTimeMillis();
    		method1("D:\\復活-喬任樑.mp3", "復活.mp3");
    		method2("D:\\復活-喬任樑.mp3", "復活.mp3");
    		method3("D:\\復活-喬任樑.mp3", "復活.mp3");
    		method4("D:\\復活-喬任樑.mp3", "復活.mp3");
    		long endTime = System.currentTimeMillis();
    		System.out.println("總耗時:"+(endTime - startTime));
    		
    	}
    	
    	// 基本位元組流一次讀寫一個位元組
    	public static void method1(String src,String dest) throws Exception{
    		FileInputStream fis = new FileInputStream(src);
    		FileOutputStream fos = new FileOutputStream(dest);
    		
    		int num = 0;
    		while((num = fis.read()) != -1){
    			fos.write(num);
    		}
    		
    		fis.close();
    		fos.close();
    	}
    	
    	// 基本位元組流一次讀寫一個位元組陣列:
    	public static void method2(String src,String dest) throws Exception{
    		FileInputStream fis = new FileInputStream(src);
    		FileOutputStream fos = new FileOutputStream(dest);
    		
    		int num = 0;
    		byte[] by = new byte[1024];
    		
    		while((num = fis.read(by)) != -1){
    			fos.write(by,0,num);
    		}
    		
    		fis.close();
    		fos.close();
    	}
    	
    	// 高效位元組流一次讀寫一個位元組:
    	public static void method3(String src,String dest) throws Exception{
    		BufferedInputStream bis = new BufferedInputStream(new FileInputStream(src));
    		BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(dest));
    		
    		int num = 0;
    		while((num = bis.read()) != -1){
    			bos.write(num);
    		}
    		
    		bis.close();
    		bos.close();
    	}
    	
    	// 高效位元組流一次讀寫一個位元組陣列:
    	public static void method4(String src,String dest) throws Exception{
    		BufferedInputStream bis = new BufferedInputStream(new FileInputStream(src));
    		BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(dest));
    		
    		int num = 0;
    		byte[] by = new byte[1024];
    		while((num = bis.read(by)) != -1){
    			bos.write(by,0,num);
    		}
    		
    		bis.close();
    		bos.close();
    	}
    }
    

2. 字元流

定義:是按照字元進行操作的,每次讀取一個字元
字元流抽象基類:
	Reader(字元輸入流)/Writer(字元輸出流)
子類:
    (1).FileReader
       	public int read()
			讀取單個字元
		public int read(char[] b)
			將字元讀入陣列
		public int read(char[] b,int off,int len)
			將字元讀入陣列的某一部分。

	(2).FileWriter
		public void write(String str)
         public void write(String str,int off,int len)
         public void write(int c)
         public void writer(char[] cbuf)
         public void write(char[] b,int off,int len)
         	指定 char 陣列中從偏移量 off 開始的 len 個位元組寫入此檔案輸出流。

	(3).BufferedReader:字元緩衝輸入流
	(4).BufferedWriter:字元緩衝輸出流
  • FileReader(字元輸入流)使用方法

    package demo3;
    
    import java.io.FileReader;
    import java.io.IOException;
    import java.io.Reader;
    
    public class ReaderDemo {
    	public static void main(String[] args) throws IOException {
    		Reader re = new FileReader("鬥破蒼穹.txt");
    		
    		int num = 0;
    		char[] ch = new char[1024];
    		
    		while((num = re.read(ch)) != -1){
    			
    			String str = new String(ch,0,num);
    			System.out.println(str);
    		}
    		re.close();
    	}
    }
    
  • FileWriter(字元輸出流)使用方法

    package demo3;
    
    import java.io.FileWriter;
    import java.io.IOException;
    import java.io.Writer;
    
    public class WriterDemo {
    	public static void main(String[] args) throws IOException {
    		
    		// 1.建立字元流輸出流
    		Writer w = new FileWriter("鬥破蒼穹.txt");
    		w.write("第一章    世界之大,唯我獨尊\n");
    		w.write("第二章    別bb了,趕緊上車\n");
    		
    		// 注意:如果不關閉流或者不重新整理緩衝區,那麼資料會一直存在記憶體中
    		w.flush();
    		w.close();		
    	}
    }
    
  • FileReader/FileWriter案例

    package demo3;
    
    import java.io.FileReader;
    import java.io.FileWriter;
    import java.io.IOException;
    import java.io.Reader;
    import java.io.Writer;
    
    /*
     * 	字元流實現檔案複製
     */
    public class ReadToWriteDemo {
    	public static void main(String[] args) throws IOException {
    		
    		// 1.利用字元流讀取檔案
    		Reader r = new FileReader("鬥破蒼穹.txt");
    		// 2.利用字元流寫入檔案
    		Writer w = new FileWriter("鬥破2.txt");
    		
    		int num = 0;
    		// 讀取 每次讀取一個字元(2個位元組)
    		while((num = r.read()) != -1){
    			// 寫入指定檔案
    			w.write(num);
    			w.flush(); // 重新整理緩衝區
    		}
    		
    		r.close();
    		w.close();
    	}
    }
    
  • BufferedWriter/BufferedReader使用方法

    package demo4;
    
    import java.io.BufferedReader;
    import java.io.BufferedWriter;
    import java.io.FileReader;
    import java.io.FileWriter;
    import java.io.IOException;
    
    /*
     * 利用你緩衝區市西南對文字檔案的複製
     * 
     * 		BufferedWriter基本用法
     * 		BufferedReader基本用法
     * 			字元緩衝流複製文字檔案
     * 
     * 特殊功能
     * 		BufferedWriter
     * 			void newLine()
     * 		BufferedReader
     * 			void readLine()
     * 		字元緩衝流特殊功能複製文字檔案
     */
    public class BufferedWriterToBufferedReader {
    	public static void main(String[] args) throws IOException {
    		// 建立字元緩衝輸入流  讀取檔案
    		BufferedReader br = new BufferedReader(new FileReader("鬥破蒼穹.txt"));
    		// 建立字元緩衝輸入流  寫入檔案
    		BufferedWriter bw = new BufferedWriter(new FileWriter("新鬥破蒼穹.txt"));
    		
    		String str = null;
    		// newLine()  按行讀取,依次讀取一個文字行
    		// 讀到末尾使返回null
    		while((str = br.readLine()) != null){
    			// 寫入指定檔案
    			bw.write(str);
    			// 換行
    			bw.newLine();
    			// 重新整理快取區
    			bw.flush();
    		}
    		// 釋放資源
    		br.close();
    		bw.close();
    	}
    }
    

3. 位元組流和字元流區別

位元組流在操作時本身不會用到緩衝區(記憶體),是檔案本身直接操作的,而字元流在操作時使用了緩衝區,通過快取區再操作檔案。
下面以兩個寫檔案的操作為主進行比較,位元組和字元流操作完成後都不關閉輸出流。

4. PrintStream流

(1)基本概念
	java.io.PrintStream類用於方便地列印各種格式的資料。
(2)常用的方法
	PrintStream(OutputStream out) 
		- 根據引數指定的引用構造物件
		- 其中OutputStream類是抽象類,實參需要傳遞子類的物件。
	void print(String s) 
		- 用於列印引數指定的字串。
	void println(String x) 
		- 用於列印字串並終止該行。

  • PrintStream流使用方法

    package demo5;
    
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.OutputStream;
    import java.io.PrintStream;
    
    public class TestPrintStream {
    	public static void main(String[] args){
    		// 1.構造PrintStream型別的物件與E:\\a.txt檔案關聯
    		PrintStream ps;
    		try {
    			ps = new PrintStream(new FileOutputStream("E:\\a.txt"));
    			// 2.向輸出流中寫入字串內容
    			ps.print("hello");
    			System.out.println("寫入資料成功!");
    			// 3.關閉流並釋放有關的資源
    			ps.close();
    		} catch (FileNotFoundException e) {
    			e.printStackTrace();
    		}
    	}
    }
    
  • PrintStream流案例

    package demo5;
    
    import java.io.BufferedReader;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.PrintStream;
    import java.util.Date;
    import java.text.SimpleDateFormat;
    
    /*
     * 練習:使用PrintStream和BufferedReader類來生成聊天記錄
     * 要求:不斷提示使用者要輸入傳送的內容,判斷是否為"bye",若是則結束
     * 輸入:若不是"bye",則寫入檔案E:\\a.txt
     */
    
    public class TestChatMsg {
    	public static void main(String[] args) throws IOException{
    		
    		try {
    			// System.in 是InputStream型別的,代表鍵盤輸入
    			BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    			PrintStream ps = new PrintStream(new FileOutputStream("E:\\a.txt"));
    			boolean flg = true;
    			while(true){
    				// 1提示使用者輸入要傳送的內容並記錄到變數中
    				System.out.println("請"+(flg?"張三":"李四")+"輸入要傳送的內容:");
    				String str = br.readLine();
    				
    				// 2.判斷使用者輸入的內容是否為"bye",若是則結束輸入
    				if(str.equalsIgnoreCase("bye")){
    					System.out.println("聊天結束!");
    					break;
    				}
    				
    				// 獲取當前的系統時間
    				Date d = new Date();
    				SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    				String format = sdf.format(d);
    				
    				ps.println(format+(flg?"張三:":"李四:")+str);
    				flg = !flg;
    			}
    			
    			br.close();
    			ps.close();
    		} catch (FileNotFoundException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    	}
    }
    

5. 記憶體操作流

操作位元組陣列
	ByteArrayInputStream
	ByteArrayOutputStream
操作字元陣列
	CharArrayReader
	CharArrayWrite
操作字串
	StringReader
	StringWriter
package demo7;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

/*
 * 演示記憶體操作流
 * 		記憶體操作流一般用於處理臨時檔案,因為臨時檔案不需要儲存,使用後就可以刪除
 */
public class TestByteArray {
	public static void main(String[] args) throws IOException {
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		
		bos.write("hello world".getBytes());
		
		byte[] bs = bos.toByteArray();
		
		ByteArrayInputStream bis = new ByteArrayInputStream(bs);
		
		int num = 0;
		while((num = bis.read()) != -1){
			System.out.println((char)num);
		}
		
		bos.close();
		bis.close();
	}
}

6. 序列化流

序列化流:指把Java物件轉換為位元組序列的過程為物件的
	ObjectOutputStream  
		writeObject(Object obj) 方法可以對引數指定的obj物件進行序列化,把得到的位元組序列寫到一個目標輸出流中。

反序列化流:指把位元組序列恢復為Java物件的過程稱為物件的反序列化
	ObjectInputStream  
		readObject()方法源輸入流讀取位元組序列,再把它們反序列化成為一個物件,並將其返回。
		
使用transient關鍵字宣告不需要序列化的成員變數

注意:只有實現了Serializable或Externalizable介面的類的物件才能被序列化,否則丟擲異常。
  • 實體物件

    package demo8;
    
    import java.io.Serializable;
    
    /*
     * Goods實體類
     */
    public class Goods implements Serializable{
    	private static final long serialVersionUID = -3195823797427025499L;
    	private int id;
    	private String name;
    	// transient 被他修飾的變數不參與序列化
    	private transient double price;
    	
    	public int getId() {
    		return id;
    	}
    
    	public Goods() {
    		super();
    	}
    	public Goods(int id, String name, double price) {
    		super();
    		this.id = id;
    		this.name = name;
    		this.price = price;
    	}
    
    	public void setId(int id) {
    		this.id = id;
    	}
    	public String getName() {
    		return name;
    	}
    	public void setName(String name) {
    		this.name = name;
    	}
    	public double getPrice() {
    		return price;
    	}
    	public void setPrice(double price) {
    		this.price = price;
    	}
    	
    	@Override
    	public String toString() {
    		return "Goods [id=" + id + ", name=" + name + ", price=" + price + "]";
    	}
    }
    
  • 實現類

    package demo8;
    
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    
    /*
     * 演示序列化與反序列化
     * 		
     * 		序列化:ObjectOutputStream
     * 		反序列化:ObjectInputStream
     * 
     * 		需求:
     * 			1.建立Goods實體類
     * 			2.建立序列化流,將Goods物件序列化到專案object.txt檔案中,做到資料持久化儲存
     * 			3.利用反序列化將物件讀取並打印出來
     * 
     * 		注意:使用序列化流被序列化的物件一定要實現序列化介面(Serializable)
     */
    public class TestObject {
    	public static void main(String[] args) throws Exception {
    		
    		Goods goods = new Goods(1001, "葵花寶典", 12.12);
    		
    		// 建立序列化流
    		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.txt"));
    		
    		// 將Goods物件序列化到專案下object.txt中持久化儲存
    		oos.writeObject(goods); // java.io.NotSerializableException:demo8.goods   物件類序列化異常
    		
    		oos.close();
    		
    		// 建立反序列化流
    		ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.txt"));
    		
    		// 讀取檔案中的物件資訊 
    		Goods g = (Goods)ois.readObject();
    		ois.close();
    		System.out.println(g);
    	}
    }
    

7. 練習

  • 在D盤根目錄下建立一個資料夾temp1,並建立300個檔案,檔名從x000.txt到x299.txt,每個檔案裡寫入一個10000以內的隨機數。
  • 刪除D:\temp1下奇數號的檔案,如x001.txt、x003.txt…..x299.txt,留下偶數號的所有檔案。
  • 讀D:\temp1下所有的檔案,計算其中所有數字的和,顯示最終的計算結果。
  • 使用BufferReader和BufferWriter,讀data.txt檔案,並在所有行的資料前加上“行號+空格”後寫到data1.txt檔案中。
  • 建立一個元素是Book的ArrayList物件bookList1,並插入10個物件,資料隨意。對這個ArrayList物件bookList1實現序列化,其中abs屬性不參與序列化,存入檔案D:\serial\book.dat。