1. 程式人生 > >java day20 IO流 位元組流

java day20 IO流 位元組流

20.01 IO流概述及其分類

  • 1.概念
    • IO流用來處理裝置之間的資料傳輸
    • Java對資料的操作是通過流的方式
    • Java用於操作流的類都在IO包中
    • 流按流向分為兩種:輸入流,輸出流。
    • 流按操作型別分為兩種:
      • 位元組流 : 位元組流可以操作任何資料,因為在計算機中任何資料都是以位元組的形式儲存的
      • 字元流 : 字元流只能操作純字元資料,比較方便。
  • 2.IO流常用父類(在io包下)
    • 位元組流的抽象父類:
      • InputStream 位元組輸入流的頂層基類
      • OutputStream
    • 字元流的抽象父類:
      • Reader
      • Writer
  • 3.IO程式書寫
    • 使用前,匯入IO包中的類
    • 使用時,進行IO異常處理
    • 使用後,釋放資源

20.02 FileInputStream 讀檔案

  • read()一次讀取一個位元組
  •   FileInputStream fis = new FileInputStream("aaa.txt");	//建立一個檔案輸入流物件,並關聯aaa.txt
      int b;													//定義變數,記錄每次讀到的位元組
      while((b = fis.read()) != -1) {							//將每次讀到的位元組賦值給b並判斷是否是-1
      	System.out.println(b);								//列印每一個位元組
      }
      
      fis.close();											//關閉流釋放資源
    

20.03 read()方法返回值為什麼是int

  • read()方法讀取的是一個位元組,為什麼返回是int,而不是byte
  •   因為位元組輸入流可以操作任意型別的檔案,比如圖片音訊等,這些檔案底層都是以二進位制形式的儲存的,如果每次讀取都返回byte,有可能在讀到中間的時候遇到111111111
      那麼這11111111是byte型別的-1,我們的程式是遇到-1就會停止不讀了,後面的資料就讀不到了,所以在讀取的時候用int型別接收,如果11111111會在其前面補上
      24個0湊足4個位元組,那麼byte型別的-1就變成int型別的255了這樣可以保證整個資料讀完,而結束標記的-1就是int型別
    

20.04 FileOutputStream 寫檔案

  • write()一次寫出一個位元組
  • FileOutputStream在建立物件的時候,如果沒有這個檔案,則會幫我們創建出來,如果有這個檔案,就會先將檔案清空
  •   FileOutputStream fos = new FileOutputStream("bbb.txt");	//如果沒有bbb.txt,會創建出一個(丟擲異常 IOException)
      //fos.write(97);						//雖然寫出的是一個int數,但是在寫出的時候會將前面的24個0去掉,所以寫出的是一個byte
      fos.write(98);
      fos.write(99);
      fos.close();
    

20.05 FileOutputStream追加

  • A:案例演示
    • FileOutputStream的構造方法寫出資料如何實現資料的追加寫入
  •   FileOutputStream fos = new FileOutputStream("bbb.txt",true);	//如果沒有bbb.txt,會創建出一個
      //fos.write(97);						//雖然寫出的是一個int數,但是在寫出的時候會將前面的24個0去掉,所以寫出的一個byte
      fos.write(98);
      fos.write(99);
      fos.close();
    

20.06 拷貝圖片

  • FileInputStream讀取

  • FileOutputStream寫出

      FileInputStream fis = new FileInputStream("致青春.mp3");	//建立輸入流物件,關聯致青春.mp3
      FileOutputStream fos = new FileOutputStream("copy.mp3");//建立輸出流物件,關聯copy.mp3
      
      int b;
      while((b = fis.read()) != -1) {
      	fos.write(b);
      }
      
      fis.close();
      fos.close();
    

20.07 拷貝音訊檔案畫原理圖

  • A:案例演示
    • 位元組流一次讀寫一個位元組複製音訊
  • 弊端:效率太低 (不推薦)
    在這裡插入圖片描述

20.08 位元組陣列拷貝之available()方法

  • A:案例演示

    • int read(byte[] b):一次讀取一個位元組陣列,讀取到b位元組陣列中,返回有效位元組數
    • write(byte[] b):一次寫出一個位元組陣列
    • available()獲取讀的檔案所有的位元組個數
  • 弊端:有可能會記憶體溢位 (不推薦)

      FileInputStream fis = new FileInputStream("致青春.mp3");
      FileOutputStream fos = new FileOutputStream("copy.mp3");
      byte[] arr = new byte[fis.available()];					//根據檔案大小做一個位元組陣列
      fis.read(arr);											//將檔案上的所有位元組讀取到陣列中
      fos.write(arr);											//將陣列中的所有位元組一次寫到了檔案上
      fis.close();
      fos.close();
    

20.09 定義小陣列

  • write(byte[] b)
  • write(byte[] b, int off, int len)寫出有效的位元組個數
// xxx.txt中是abc

	public static void demo1() throws FileNotFoundException, IOException {
		FileInputStream fis = new FileInputStream("xxx.txt");
		byte[] arr = new byte[2];
		int a = fis.read(arr);						// 將檔案上的位元組讀取到位元組陣列中,一次讀2個位元組
		
		System.out.println(a);						// 讀到的有效位元組個數
		for (byte b : arr) {						// 第一次獲取到檔案中a和b
			System.out.println(b);
		}
		System.out.println("-----------------------"); // 再次讀
		int c = fis.read(arr);
		System.out.println(c);					    // 1
		for (byte b : arr) {
			System.out.println(b);					// c,b,在位元組陣列中,c把a覆蓋了,但是b沒有被覆蓋
		}
		fis.close();
	}
// 2個位元組一次的讀取,效率變大了
	public static void demo2() throws FileNotFoundException, IOException {
		FileInputStream fis = new FileInputStream("xxx.txt"); // abc
		FileOutputStream fos = new FileOutputStream("yyy.txt"); 
		
		byte[] arr = new byte[2];  
		int len;
		while((len = fis.read(arr)) != -1) {    // 沒有有效位元組個數時,返回的是-1
			fos.write(arr, 0, len);
			// fos.write(arr);  // yyy.txt 一開始為空,後來變成了abcb
		}
		
		fis.close();
		fos.close();
	}

###20.10_IO流(定義小陣列的標準格式)

  • A:案例演示
    • 位元組流一次讀寫一個位元組陣列複製圖片和視訊
// 定義比較大的位元組陣列,提高拷貝效率(標準格式)
	public static void main(String[] args) throws IOException {
		//demo1();
		//demo2();
		FileInputStream fis = new FileInputStream("致青春.mp3");
		FileOutputStream fos = new FileOutputStream("copy.mp3");
		
		byte[] arr = new byte[1024 * 8]; // 1024的整數倍
		int len;
		while((len = fis.read(arr)) != -1) {				//如果忘記加arr,返回的就不是讀取的位元組個數,而是位元組的碼錶值
			fos.write(arr,0,len);
		}
		
		fis.close();
		

20.11 BufferedInputStream和BufferOutputStream拷貝

  • A:緩衝思想

    • 位元組流一次讀寫一個數組的速度明顯比一次讀寫一個位元組的速度快很多,
    • 這是加入了陣列這樣的緩衝區效果,java本身在設計的時候,
    • 也考慮到了這樣的設計思想(裝飾設計模式後面講解),所以提供了位元組緩衝區流
    • 對FileInputStream和FileOutputStream進行包裝,讓他們變的更加強大
  • B.BufferedInputStream 讀取資料到記憶體

    • BufferedInputStream內建了一個緩衝區(陣列)
    • 從BufferedInputStream中讀取一個位元組時
    • BufferedInputStream會一次性從檔案中讀取8192個, 存在緩衝區中, 返回給程式一個
    • 程式再次讀取時, 就不用找檔案了, 直接從緩衝區中獲取
    • 直到緩衝區中所有的都被使用過, 才重新從檔案中讀取8192個
  • C.BufferedOutputStream 寫入檔案

    • BufferedOutputStream也內建了一個緩衝區(陣列)
    • 程式向流中寫出位元組時, 不會直接寫到檔案, 先寫到緩衝區中
    • 直到緩衝區寫滿, BufferedOutputStream才會把緩衝區中的資料一次性寫到檔案裡
  • D.拷貝的程式碼

      FileInputStream fis = new FileInputStream("致青春.mp3");			//建立檔案輸入流物件,關聯致青春.mp3
      BufferedInputStream bis = new BufferedInputStream(fis);			//建立緩衝區對fis裝飾
      FileOutputStream fos = new FileOutputStream("copy.mp3");		//建立輸出流物件,關聯copy.mp3
      BufferedOutputStream bos = new BufferedOutputStream(fos);		//建立緩衝區對fos裝飾
      
      int b;
      while((b = bis.read()) != -1) {		
      	bos.write(b);
      }
      
      bis.close();						//只關裝飾後的物件即可
      bos.close();
    

    在這裡插入圖片描述

  • E.小陣列的讀寫和帶Buffered的讀取哪個更快?

    • 定義小陣列如果是8192個位元組大小和Buffered比較的話
    • 定義小陣列會略勝一籌,因為讀和寫操作的是同一個陣列
    • 而Buffered操作的是兩個陣列

20.12 flush和close方法的區別

  • flush()方法
    • 用來重新整理緩衝區的,重新整理後可以再次寫出
    • 作用:需要實時重新整理的時候用,看完之後還可以在寫。
  • close()方法
    • 具備重新整理的功能,在關閉流之前,就會先重新整理一次緩衝區,將緩衝區的位元組全都重新整理到檔案上,再關閉,close方法刷完之後就能寫了

20.13 位元組流讀寫中文

  • 位元組流讀取中文的問題
    • 一箇中文代表2個位元組,標點是1個位元組
    • 位元組流在讀中文的時候有可能會讀到半個中文,造成亂碼
  • 位元組流寫出中文的問題
    • 位元組流直接操作的位元組,所以寫出中文必須將字串轉換成位元組陣列
    • 寫出回車換行 write("\r\n".getBytes());

// 位元組流讀取:有標點的時候,無法保證不亂碼
	public static void demo1() throws FileNotFoundException, IOException {
		FileInputStream fis = new FileInputStream("yyy.txt");
		byte[] arr = new byte[4];
		int len;
		while((len = fis.read(arr)) != -1) {
			System.out.println(new String(arr,0,len)); // String類的方法,讀取有效位元組個數內的位元組
		}
		
		fis.close();
	}
// 位元組流寫中文
	public static void main(String[] args) throws IOException {
		//demo1();
		FileOutputStream fos = new FileOutputStream("zzz.txt");
		fos.write("我讀書少,你不要騙我".getBytes());  // 寫出中文必須將字串轉換成位元組陣列 
		fos.write("\r\n".getBytes());
		fos.close();
	}

20.14 流的標準處理異常程式碼1.6版本及其以前

  • try finally巢狀

      FileInputStream fis = null;
      FileOutputStream fos = null;
      try {
      	fis = new FileInputStream("aaa.txt");
      	fos = new FileOutputStream("bbb.txt");
      	int b;
      	while((b = fis.read()) != -1) {
      		fos.write(b);
      	}
      } finally {
      	try {
      		if(fis != null)
      			fis.close();
      	}finally {
      		if(fos != null)
      			fos.close();
      	}
      }
    

20.15 流的標準處理異常程式碼1.7版本

  • try close
	try(
			 // 寫在小括號裡,FileInputStream和FileOutputStream具備自動關閉的功能
			FileInputStream fis = new FileInputStream("aaa.txt");
			FileOutputStream fos = new FileOutputStream("bbb.txt");
			MyClose mc = new MyClose();
		){
			int b;
			while((b = fis.read()) != -1) {
				fos.write(b);
			}
		}
		
class MyClose implements AutoCloseable {
	public void close() {
		System.out.println("我關了");
	}
}
  • 原理
    • 在try()中建立的流物件必須實現了AutoCloseable這個介面,如果實現了,在try後面的{}(讀寫程式碼)執行後就會自動呼叫,流物件的close方法將流關掉

20.16 圖片加密

  • 給圖片加密

  • 異或運算規則是:兩個數轉為二進位制,然後從高位開始比較,如果相同則為0,不相同則為1。

      BufferedInputStream bis = new BufferedInputStream(new FileInputStream("a.jpg"));
      BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("b.jpg"));
      
      int b;
      while((b = bis.read()) != -1) {
      	bos.write(b ^ 123); // 加密時候異或一次,解密時候異或一次,2次異或等於原來
      }
      
      bis.close();
      bos.close();
    

20.17 拷貝檔案

  • 在控制檯錄入檔案的路徑,將檔案拷貝到當前專案下
package com.heima.test;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Scanner;

public class Test2 {

	/**
	 * 在控制檯錄入檔案的路徑,將檔案拷貝到當前專案下
	 * 
	 * 分析:
	 * 
	 * 1,定義方法對鍵盤錄入的路徑進行判斷,如果是檔案就返回
	 * 2,在主方法中接收該檔案
	 * 3,讀和寫該檔案
	 * @throws IOException 
	 */
	public static void main(String[] args) throws IOException {
		File file = getFile();					//獲取檔案
		BufferedInputStream  bis = new BufferedInputStream(new FileInputStream(file));
		BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file.getName()));
		
		int b;
		while((b = bis.read()) != -1) {
			bos.write(b);
		}
		
		bis.close();
		bos.close();
	}

	/*
	 * 定義一個方法獲取鍵盤錄入的檔案路徑,並封裝成File物件返回
	 * 1,返回值型別File
	 * 2,引數列表無
	 */
	public static File getFile() {
		Scanner sc = new Scanner(System.in);				//建立鍵盤錄入物件
		System.out.println("請輸入一個檔案的路徑:");
		while(true) {
			String line = sc.nextLine();					//接收鍵盤錄入的路徑
			File file = new File(line);						//封裝成File物件,並對其進行判斷
			if(!file.exists()) {
				System.out.println("您錄入的檔案路徑不存在,請重新錄入:");
			}else if(file.isDirectory()) {
				System.out.println("您錄入的是資料夾路徑,請重新錄入:");
			}else {
				return file;
			}
		}
	}
}

20.18 錄入資料拷貝到檔案

  • 將鍵盤錄入的資料拷貝到當前專案下的text.txt檔案中,鍵盤錄入資料當遇到quit時就退出

      Scanner sc = new Scanner(System.in);
      FileOutputStream fos = new FileOutputStream("text.txt");
      System.out.println("請輸入:");
      while(true) {
      	String line = sc.nextLine();
      	if("quit".equals(line))
      		break;
      	fos.write(line.getBytes());
      	fos.write("\r\n".getBytes());
      }
      
      fos.close();