天天看點

【JavaSE學習筆記】IO流03_位元組緩沖流、遞歸

IO流03

A.引入位元組緩沖流
1)計算機識别漢字
計算機是何如識别中文這樣的字元的,一個中文對應兩個位元組

第一個位元組:肯定是否負數

第二個位元組:可是負數,也可以是正數,對實際是沒有影響的

import java.util.Arrays;

public class Demo01 {
	public static void main(String[] args) {
		String s = "我愛你中國";

		byte[] bys = s.getBytes();

		System.out.println(Arrays.toString(bys));
		// [-50, -46, -80, -82, -60, -29, -42, -48, -71, -6]
	}
}
           
2)需求
在目前項目有一個a.txt檔案,然後我要将a.txt檔案的内容複制到目前檔案下的b.txt檔案
複制檔案:

源檔案:目前項目下的a.txt檔案---->讀資料------>FlieInputStream

目的地檔案:目前項目下的b.txt檔案------>寫資料------>FileOutputStream

【JavaSE學習筆記】IO流03_位元組緩沖流、遞歸
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class Demo01 {
	public static void main(String[] args) throws IOException {
		// 封裝源檔案
		FileInputStream fis = new FileInputStream("a.txt");
		// 為了友善示範,在main方法直接抛出異常

		// 封裝目的檔案
		FileOutputStream fos = new FileOutputStream("b.txt");

		// 一次讀取一個位元組
		int by = 0;
		while ((by = fis.read()) != -1) {
			// 讀一個寫一個
			fos.write(by);
		}

		// 釋放資源:後開先關
		fos.close();
		fis.close();
	}
}
           
【JavaSE學習筆記】IO流03_位元組緩沖流、遞歸

上一章使用FileInputStream一次讀取一個位元組,控制台出現中文亂碼

而現在這種情況,沒有出現中文亂碼

上一章的寫法:存在強制類型轉換:把位元組轉換成了字元

而一個字元對應2個位元組,出現中文亂碼;

程式中:每次讀一個位元組,就寫一個位元組

并沒有出現類型轉換的問題,是以不會出現中文亂碼;

3)需求
将上面的需求,使用位元組數組複制
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class Demo02 {
	public static void main(String[] args) throws IOException {
		// 封裝源檔案
		FileInputStream fis = new FileInputStream("a.txt");

		// 目的
		FileOutputStream fos = new FileOutputStream("b.txt");

		byte[] bys = new byte[1024];

		int len = 0;
		while ((len = fis.read(bys)) != -1) {
			fos.write(bys, 0, len);
		}

		// 釋放資源
		fos.close();
		fis.close();
	}
}
           
4)擴充

上面的複制過程非常快!幾乎就是一瞬間的事情

如果将上面的目标換成記憶體大的mov\mp4\exe\jpg檔案

自行嘗試計算下程式運作時間(用System.currentTimeMillis()方法)

定義一個位元組數組的時候,讀取的速度比一次讀取一個位元組快

為了提高讀取的速度,java就給我們提供了位元組緩沖輸入流和位元組緩沖輸出流

高效流

B.位元組緩沖輸出流BufferedOutputStream
1)構造方法

public BufferedOutputStream(OutputStream out):推薦使用第一種

public BufferedOutputStream(OutputStream out, int size):指定一個緩沖區大小

第二種不用,因為第一種構造方法預設的緩沖區大小足夠存儲資料

import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class Demo03 {
	public static void main(String[] args) throws IOException {
		BufferedOutputStream bos = 
				new BufferedOutputStream(new FileOutputStream("box.txt"));
		
		// 寫資料
		bos.write("hello".getBytes());
		
		// 釋放資源
		bos.close();
	}
}
           

位元組緩沖輸出流中為什麼不直接将檔案的路徑或者檔案夾的路徑作為參數,為什麼是OutputSteram?

緩沖流隻是為了高效而設計的,隻是構造了一個緩沖區,要進行讀寫操作還需要使用基本的流對象!

OutputStream:本身是抽象類,不能直接執行個體化

BufferedOutputStream(new FileOutputStream("路徑抽象表現形式"))

又屬于java一種設計模式:裝飾者設計模式

2)其餘用法跟OutputStream用法一樣
C.位元組緩沖輸入流BufferedInputStream
1)概述

在同一個對象中:讀資料隻能是一種方式

要麼一次讀取一個位元組,要麼就是一次讀取一個位元組數組

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;

public class Demo04 {
	public static void main(String[] args) throws IOException {
		BufferedInputStream bis = 
				new BufferedInputStream(new FileInputStream("bis.txt"));
		// 讀取資料:一次讀取一個位元組
		// int by = 0 ;
		// while((by=bis.read()) !=-1){
		// System.out.println((char)by);
		// }

		// 一次讀一個位元組數組
		byte[] bys = new byte[1024];
		int len = 0;
		while ((len = bis.read(bys)) != -1) {
			System.out.println(new String(bys, 0, len));
		}

		bis.close();
	}
}
           
2)其餘用法和InputStream一樣
D.位元組流中讀取MP4視訊這樣的效率問題!

需求:複制一個視訊(在此我複制eclipse安裝目錄,大小将近44.7M)

從E盤複制到項目目錄下

【JavaSE學習筆記】IO流03_位元組緩沖流、遞歸
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

/*
 * 位元組流:按照字元數組指派:共耗時855ms
 * 位元組緩沖流:按照字元數組指派:共耗時167ms
 */
public class Demo05 {
	public static void main(String[] args) throws IOException {

		// 開始工作的毫秒值
		long start = System.currentTimeMillis();

		// 封裝方法method1為位元組流,按照位元組數組複制
		// method1("E:\\eclipse-inst-win64.exe", "eclipse-inst-win64.exe");

		// 封裝方法method2為位元組緩沖流,按照位元組數組複制
		method2("E:\\eclipse-inst-win64.exe", "eclipse-inst-win64.exe");

		// 複制完畢的毫秒值
		long end = System.currentTimeMillis();

		System.out.println("複制完畢!共耗時" + (end - start) + "ms");
	}

	// 位元組流
	public static void method1(String src, String dest) throws IOException {
		// 封裝源路徑
		FileInputStream fis = new FileInputStream(src);

		// 封裝目的
		FileOutputStream fos = new FileOutputStream(dest);

		byte[] bys = new byte[1024];
		int len = 0;
		while ((len = fis.read(bys)) != -1) {
			fos.write(bys, 0, len);
		}

		fos.close();
		fis.close();
	}

	// 位元組緩沖流
	public static void method2(String src, String dest) throws IOException {
		// 封裝源路徑
		BufferedInputStream bis = 
				new BufferedInputStream(new FileInputStream(src));
		
		// 封裝目的
		BufferedOutputStream bos= 
				new BufferedOutputStream(new FileOutputStream(dest));
		
		byte[] bys = new byte[1024];
		int len = 0;
		while ((len = bis.read(bys)) != -1) {
			bos.write(bys, 0, len);
		}
		
		bos.close();
		bis.close();
	}
}
           
【JavaSE學習筆記】IO流03_位元組緩沖流、遞歸

可以看出,位元組緩沖流效率遠大于位元組流

當然按位元組數組複制速度也遠大于一個位元組一個位元組複制

由于一個位元組複制速度太慢,在此就不測試了

E.遞歸
1)方法本身調用方法的一種現象!
方法嵌套:不屬于遞歸
2)注意事項

遞歸必須是出口條件,還要定義方法

遞歸的次數不宜過多,否則則會出現記憶體溢出

構造方法中不能中出現遞歸

3)舉例
從前有座山,山裡有座廟,廟裡有個老和尚和小和尚,老和尚給小\小和尚講故事,故事是:
從前有座山,山裡有座廟,廟裡有個老和尚和小和尚,老和尚給小\小和尚講故事,故事是:
從前有座山,山裡有座廟,廟裡有個老和尚和小和尚,老和尚給小\小和尚講故事,故事是:
【JavaSE學習筆記】IO流03_位元組緩沖流、遞歸
4)練習
求5的階乘:

5! = 5 *4!

如果實作這個需求:

定義一個方法

出口條件

規律

public class Demo01 {
	public static void main(String[] args) {
		/**
		 * 兩個明确:
		 * 		明确傳回值類型:int類型
		 * 		參數類型:int  ,int i
		 * 
		 * 出口條件是什麼:
		 * 		if(i==1){return 1}
		 * 
		 *規律
		 * 		if(i!=1){return i *jieCheng(i-1) }
		 * */
		System.out.println("5的階乘是:" + jieCheng(5));
	}

	public static int jieCheng(int i) {
		// 出口條件
		if (i == 1) {
			return 1;
		} else {
			return i * jieCheng(i - 1);
		}
	}
}
           
【JavaSE學習筆記】IO流03_位元組緩沖流、遞歸
5)練習

有一對兔子,從出生後第3個月起每個月都生一對兔子

小兔子長到第三個月後每個月又生一對兔子

假如兔子都不死,問第二十個月的兔子對數為多少?不死神兔

分析:

兔子的對數的規律:

第一個月:1

第二個月:1

第三個月:2

第四個月:3

第五個月:5

第六個月:8

第一個月和和第二個月都是1

從第三個月開始,每個月的資料:是前兩個月之後,前兩個月的資料是已知的都是:1

我們把相鄰倆個月的資料看是:a,b

那麼第一個相鄰的資料:a=1,b=1

第二個相鄰的資料:a=1,b=2

第三個相鄰的資料:a=2,b=3;

第四個相鄰的資料:a=3,b=5

...

下一次的a是上一次是b,下一次的b是上一次的a+b

public class Demo02 {
	public static void main(String[] args) {
		// 1)數組實作
		int[] arr = new int[20];
		arr[0] = 1;
		arr[1] = 1;

		for (int i = 2; i < arr.length; i++) {
			arr[i] = arr[i - 2] + arr[i - 1];
		}
		System.out.println("兔子數:" + arr[19] + "對");

		// 2)中間變量
		int a = 1;
		int b = 1;
		for (int i = 0; i < 18; i++) {
			// 使用中間變量進行
			int temp = a;
			a = b;
			b = temp + b;
		}
		System.out.println("兔子數:" + b + "對");

		// 3)遞歸
		System.out.println("兔子數:" + fib(20) + "對");
	}

	public static int fib(int i) {
		if (i == 1 || i == 2) {
			return 1;
		} else {
			return fib(i - 1) + fib(i - 2);
		}
	}
}
           
6)需求
需求:使用遞歸實作删除帶内容的目錄

目前項目下有一個demo檔案夾

分析:

a.封裝目前目錄:封裝成File對象

b.擷取目前目錄下的所有的檔案以及檔案夾的File數組

c.對File數組進行非空判斷,周遊,擷取每一個file對象

判斷:是否是檔案夾

是:回到(b)

不是:删除

import java.io.File;

public class Demo03 {
	public static void main(String[] args) {
		// 封裝目錄
		File srcFloder = new File("demo");

		// 删除檔案夾的方法
		deleteFloder(srcFloder);
	}

	public static void deleteFloder(File srcFloder) {
		// 擷取目前目錄下的所有檔案以及檔案夾的File數組
		File[] fileArr = srcFloder.listFiles();
		// 非空判斷:當對象不為空的時候,才能進行周遊
		if (fileArr != null) {
			// 使用增強for周遊,擷取每一個file對象
			for (File file : fileArr) {
				// 再次判斷file對象是否是檔案夾
				if (file.isDirectory()) {
					// 是檔案夾:回到這裡進行删除
					deleteFloder(file);
				} else {
					// 删除檔案
					System.out.println(file.getName() + "---" + file.delete());
				}
			}
			System.out.println(srcFloder.getName() + "---" + srcFloder.delete());
		}
	}
}
           
7)需求

請大家把F:\Java目錄下所有的java結尾的檔案的絕對路徑給輸出在控制台

分析:

A:封裝目錄

B:擷取該目錄下的所有的檔案以及檔案夾的File數組

C:給改數組的對象進行非空判斷,周遊

D:判斷該file是否是檔案夾

是:回B

不是:

再次判斷否是以.java結尾的檔案

是:輸出絕對路徑

import java.io.File;

public class Demo04 {
	public static void main(String[] args) {
		// 封裝目錄
		File srcFolder = new File("F:\\Java");

		// 寫一個擷取檔案絕對路徑的方法
		getAllJavaFilePaths(srcFolder);
	}

	public static void getAllJavaFilePaths(File srcFolder) {
		// 擷取所有的檔案以及檔案夾的File數組
		File[] fileArray = srcFolder.listFiles();
		// 非空判斷
		if (fileArray != null) {
			// 周遊
			for (File file : fileArray) {
				// 判斷是否是檔案夾
				if (file.isDirectory()) {
					getAllJavaFilePaths(file);
				} else {
					// 再次判斷是否以.java結尾的檔案
					if (file.getName().endsWith(".java")) {
						System.out.println(file.getName() + "的絕對路徑是:" + file.getAbsolutePath());
					}
				}
			}
		}
	}
}
           
【JavaSE學習筆記】IO流03_位元組緩沖流、遞歸