1. 程式人生 > >20 IO(位元組流)

20 IO(位元組流)

20.01_IO流(IO流概述及其分類)

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

20.02_IO流(FileInputStream)

  • read()一次讀取一個位元組,用int型別接收
public static void main(String[] args) throws IOException {
		FileInputStream fis = new FileInputStream("xxx.txt");	//建立流物件
		int b;
		while((b = fis.read()) != -1) {   //讀不到位元組時,返回-1
			System.out.println(b);
		}
		
		fis.close();
	}

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

  • read()方法讀取的是一個位元組,為什麼返回是int,而不是byte

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

負數以其正值的補碼形式表達,即-1會以1的補碼形式儲存,1的二進位制數是00000001,其反碼為11111110,補碼為11111111,故-1在記憶體中儲存為11111111

20.04_IO流(FileOutputStream)

  • write()一次寫出一個位元組
	public static void demo1() throws FileNotFoundException, IOException {
		FileOutputStream fos = new FileOutputStream("yyy.txt");		//建立位元組輸出流物件,如果沒有就自動建立一個,如果有這個檔案就會先將檔案清空
		fos.write(97);	//a			//雖然寫出的是一個int數,但是到檔案上的是一個位元組,會自動去除前三個8位
		fos.write(98);  //b
		fos.write(99);  //c
		fos.close();
	}

20.05_IO流(FileOutputStream追加)

  • A:案例演示
    • FileOutputStream的構造方法寫出資料如何實現資料的追加寫入
	public static void main(String[] args) throws IOException {
		FileOutputStream fos = new FileOutputStream("yyy.txt",true);	//如果想追加就在第二個引數傳true
		fos.write(97);
		fos.write(98);
		
		fos.close();
	}

20.06_IO流(拷貝圖片)

  • FileInputStream讀取
  • FileOutputStream寫出
public static void demo1() throws FileNotFoundException, IOException {
		FileInputStream fis = new FileInputStream("雙元.jpg");		//建立輸入流物件,關聯雙元.jpg
		FileOutputStream fos = new FileOutputStream("copy.jpg");	//建立輸出流物件,關聯copy.jpg
		
		int b;
		while((b = fis.read()) != -1) {								//在不斷的讀取每一個位元組
			fos.write(b);											//將每一個位元組寫出
		}
		
		fis.close();												//關流釋放資源
		fos.close();
	}

20.07_IO流(拷貝音訊檔案畫原理圖)

  • A:案例演示
    • 位元組流一次讀寫一個位元組複製音訊
  • 弊端:效率太低

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

  • A:案例演示

    • int read(byte[] 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_IO流(定義小陣列)

  • write(byte[] b)
  • write(byte[] b, int off, int len)寫出有效的位元組個數
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) {//len代表寫入的有效位元組長度
			fos.write(arr,0,len);//第一次寫入ab,第二次寫入c
		}
		
		fis.close();
		fos.close();
	}

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

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

20.11_IO流(BufferedInputStream和BufferOutputStream拷貝)

  • A:緩衝思想

    • 位元組流一次讀寫一個數組的速度明顯比一次讀寫一個位元組的速度快很多,
    • 這是加入了陣列這樣的緩衝區效果,java本身在設計的時候,
    • 也考慮到了這樣的設計思想(裝飾設計模式後面講解),所以提供了位元組緩衝區流
  • 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_IO流(flush和close方法的區別)

  • flush()方法
    • 用來重新整理緩衝區的,重新整理後可以再次寫出
  • close()方法
    • 用來關閉流釋放資源的的,如果是帶緩衝區的流物件的close()方法,不但會關閉流,還會再關閉流之前重新整理緩衝區,關閉後不能再寫出

20.13_IO流(位元組流讀寫中文)

  • 位元組流讀取中文的問題
    • 位元組流在讀中文的時候有可能會讀到半個中文,造成亂碼
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));//可能出現亂碼
		}
		
		fis.close();
	}
  • 位元組流寫出中文的問題
    • 位元組流直接操作的位元組,所以寫出中文必須將字串轉換成位元組陣列
    • 寫出回車換行 write("\r\n".getBytes());
public static void main(String[] args) throws IOException {
		FileOutputStream fos = new FileOutputStream("zzz.txt");
		fos.write("我讀書少,你不要騙我".getBytes());//將字串轉換成位元組陣列
		fos.write("\r\n".getBytes());//換行
		fos.close();
	}

20.14_IO流(流的標準處理異常程式碼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 { //try finally 的巢狀,能關一個就關一個
      		if(fos != null)
      			fos.close();
      	}
      }
    

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

  • try close

      try(
      	FileInputStream fis = new FileInputStream("aaa.txt");//具備自動關閉的功能
      	FileOutputStream fos = new FileOutputStream("bbb.txt");//具備自動關閉的功能
      ){
      	int b;
      	while((b = fis.read()) != -1) {
      		fos.write(b);
      	}
      }
    
  • 原理

    • 在try()中建立的流物件必須實現了AutoCloseable這個介面,如果實現了,在try後面的{}(讀寫程式碼)執行後就會自動呼叫,流物件的close方法將流關掉

20.16_IO流(圖片加密)

  • 給圖片加密

將寫出的位元組異或上一個數,這個數就是金鑰,解密的時候再次異或就可以了

	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);//一個數與另外一個數異或兩次還等於它本身,異或一次加密,異或第二次解密
	}
	
	bis.close();
	bos.close();

20.17_IO流(拷貝檔案)

  • 在控制檯錄入檔案的路徑,將檔案拷貝到當前專案下
/**
	 * 在控制檯錄入檔案的路徑,將檔案拷貝到當前專案下
	 * 
	 * 分析:
	 * 
	 * 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_IO流(錄入資料拷貝到檔案)

  • 將鍵盤錄入的資料拷貝到當前專案下的text.txt檔案中,鍵盤錄入資料當遇到quit時就退出
	public static void main(String[] args) throws IOException {
		//1,建立鍵盤錄入物件
		Scanner sc = new Scanner(System.in);
		//2,建立輸出流物件,關聯text.txt檔案
		FileOutputStream fos = new FileOutputStream("text.txt");
		System.out.println("請輸入資料:");
		//3,定義無限迴圈
		while(true) {
			String line = sc.nextLine();					//將鍵盤錄入的資料儲存在line中
			//4,遇到quit退出迴圈
			if("quit".equals(line)) {
				break;
			}
			//5,如果不quit,就將內容寫出
			fos.write(line.getBytes());						//字串寫出必須轉換成位元組陣列
			fos.write("\r\n".getBytes());
		}
		//6,關閉流
		fos.close();
	}