Java基礎教程——字節流


IO流

水流 特點
連續性 逝者如斯夫,不舍晝夜;
方向性 一江春水向東流。水往低處流。百川東到海,何時復西歸?少壯不努力,老大徒傷悲!
源頭盡頭 唯有源頭活水來;覆水難收

Java里的IO也有這樣的特點。

IO:數據從硬盤流向內存(Input),或者從內存流向硬盤(Output)。

IO流分類:

按照流的流向來分:輸入流、輸出流。
|-輸入流:讀入內存。
|-輸出流:寫出到硬盤等設備。

按操作數據單元分:字節流、字符流
|-字節流:8位
|-字符流:16位

按流的角色分:節點流、處理流。
|-節點流:特定的IO設備(如硬盤),也叫低級流
|-處理流:經過封裝的流,也叫高級流、包裝流,可以消除不同節點流的差異(典型的裝飾器模式)

Java的IO流有40多個類,高抽象層的基類有四個:

字節流 字符流
輸入流 InputStream Reader
輸出流 OutputStream Writer

字節流

字節流:100100011010100010

可以操作所有文件,包括文本文件、視頻、音頻、壓縮文件等等都可以用字節流讀寫。

兩個重要的抽象類:

(1)抽象類 java.io.OutputStream,是輸出字節流的所有類的超類

|--API方法摘要:
|--|--void write(byte[] b) 將 b.length 個字節從指定的 byte 數組寫入此輸出流。
|--|--abstract void write(int b) 將指定的字節寫入此輸出流。
|--|--void close() 關閉此輸出流並釋放與此流有關的所有系統資源。

(2)抽象類 java.io.InputStream,是輸入字節流的所有類的超類

|--API方法摘要:
|--|--abstract int read()從輸入流中讀取數據的下一個字節。
|--|--int read(byte[] b) 從輸入流中讀取一定數量的字節,並將其存儲在緩沖區數組 b 中。
|--|--void close() 關閉此輸入流並釋放與該流關聯的所有系統資源。

FileOutputStream寫文件

// 文件輸出流。完整地說,是文件字節輸出流。
public class FileOutputStream extends OutputStream

步驟:
1.創建流對象
2.調用write方法,把數據寫入文件
3.釋放資源

import java.io.*;
public class 文件字節輸出流 {
	public static void main(String[] args) {
		File file = new File("文件字節輸出流.txt"); // 創建文件對象
		try {
			// (1)構造:通過文件對象創建文件輸出流對象
			FileOutputStream fos = new FileOutputStream(file);
			// (2)寫入文件
			// (2.1)write(int b) 將"單個字節"寫入到文件中
			for (int i = 49; i < 97; i++) {
				fos.write(i);
				fos.write(' ');
			}
			// (2.2)write(byte[] b) 將"字節數組"中的數據全部寫入文件
			byte[] buffer = "I Love Java,你呢?".getBytes();
			fos.write(buffer);
			// (3)關閉流
			fos.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

追加:

使用構造方法:FileOutputStream(File file, boolean append)

			// 構造:通過文件對象創建文件輸出流對象
			// 附加第二個參數true,指定進行文件追加,默認為不追加
			fos = new FileOutputStream(file, true);

回車換行:

CR(carriage return),回車,\r
LF(line feed),換行,\n

windows:\r\n
linux或unix:\n
mac:\r

FileInputStream讀文件

public class FileInputStream extends InputStream
import java.io.*;
public class 文件字節輸入流 {
	static final int C_CONDITION = 2;
	public static void main(String[] args) {
		try {
			File file = new File("testRead.dat"); // 創建文件對象
			// 【1】創建輸入流對象,相當於打開文件
			FileInputStream fis = new FileInputStream(file);
			if (C_CONDITION == 1) {
				// 【2】.read():讀取單個字節
				for (int i = 0; i < file.length(); i++) {
					// 循環讀取"字節",轉為字符輸出,英文沒問題
					int read = fis.read();
					char ch = (char) read;
					System.out.print(ch);
				}
				System.out.println();
			} else {
				// 【2】.read(byte[] b):讀取文件中的數據到字節數組
				// 根據文件的字節長度創建字節數組
				long len = file.length();
				byte[] buf = new byte[(int) len];
				fis.read(buf);
				// 利用字節數組創建字符串
				String str = new String(buf);
				System.out.println(str);
			}
			// 【3】關閉流
			fis.close();
		} catch (FileNotFoundException fnfe) {
			System.out.println("文件打開失敗。");
		} catch (IOException ioe) {
			ioe.printStackTrace();
		}
	}
}

復制文件

package ahjava.io;
import java.io.*;
public class 復制文件 {
	public static void main(String[] args) {
		字節流復制文件();
	}
	static void 字節流復制文件() {
		File srcFile = new File("testRead.dat"); // 源文件對象
		File destFile = new File("testCopy.java"); // 目標文件對象
		if (destFile.exists()) {
			// 判斷目標文件是否存在,存在則刪除
			destFile.delete();
		}
		// 目標文件不存在才復制
		try {
			destFile.createNewFile();
			// 創建文件輸入/輸出流對象
			FileInputStream fis = new FileInputStream(srcFile);
			FileOutputStream fos = new FileOutputStream(destFile);
			// 創建字節數組,作為臨時緩沖
			byte[] buf = new byte[128];
			System.out.println("開始復制文件...");
			int len = -1;
			// 循環從文件輸入流中讀取數據
			while ((len = fis.read(buf)) != -1) {
				System.out.println(len);
				// 寫入到文件輸出流中
				fos.write(buf, 0, len);
			}
			System.out.println("文件復制成功!");
			fis.close(); // 關閉流
			fos.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

上面的代碼有哪些風險?

改進版:

	static void 字節流復制文件2() {
		// 1.close()提取到finally中
		// 2.提取FilexxxStream對象
		// 3.初始化(否則不准close)
		// 4.close()時加判空語句
		FileInputStream fis = null;
		FileOutputStream fos = null;
		File srcFile = new File("testRead.dat"); // 源文件對象
		File destFile = new File("testCopy.java"); // 目標文件對象
		if (destFile.exists()) {
			// 判斷目標文件是否存在,存在則刪除
			destFile.delete();
		}
		// 目標文件不存在才復制
		try {
			destFile.createNewFile();
			fis = new FileInputStream(srcFile);
			fos = new FileOutputStream(destFile);
			// 創建字節數組,作為臨時緩沖
			byte[] buf = new byte[128];
			System.out.println("開始復制文件...");
			int len = -1;
			// 循環從文件輸入流中讀取數據
			while ((len = fis.read(buf)) != -1) {
				System.out.println(len);
				// 寫入到文件輸出流中
				fos.write(buf, 0, len);
			}
			System.out.println("文件復制成功!");
			// 關閉流
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (fis != null) {
				try {
					fis.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				fis = null;
			}
			if (fos != null) {
				try {
					fos.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				fos = null;
			}
		}
	}

又臭又長!

Java 7異常處理寫法:

try后面增加括號,其中定義流對象,該流對象僅在try中有效,執行完畢自動釋放,不需要寫finally。

try(流對象){
} catch (IOException e) {
}

具體代碼如下:

	static void 字節流復制文件3() {
		File srcFile = new File("testRead.dat"); // 源文件對象
		File destFile = new File("testCopy.java"); // 目標文件對象
		if (destFile.exists()) {
			// 判斷目標文件是否存在,存在則刪除
			destFile.delete();
		}
		// try(流對象)
		try (FileInputStream fis = new FileInputStream(srcFile);
				FileOutputStream fos = new FileOutputStream(destFile)) {
			destFile.createNewFile();
			// 創建字節數組,作為臨時緩沖
			byte[] buf = new byte[128];
			System.out.println("開始復制文件...");
			int len = -1;
			// 循環從文件輸入流中讀取數據
			while ((len = fis.read(buf)) != -1) {
				System.out.println(len);
				// 寫入到文件輸出流中
				fos.write(buf, 0, len);
			}
			System.out.println("文件復制成功!");
			// 關閉流
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

*Java9異常處理寫法

覺得try里的代碼太繁瑣,try()中可以引入外部的變量:

FileInputStream	fis = new FileInputStream(srcFile);
FileOutputStream fos = new FileOutputStream(destFile);
try  (fis;fos){...}
// ↑ 理想狀態,代碼並不能這么寫

然而,fis、fos本身需要拋異常,如果直接try...catch,這兩個變量會變成另外的try中的局部變量;
如果把聲明提取到try之外,try (fis;fos)又需要變量是final的

		FileInputStream fis = null;
		FileOutputStream fos = null;
		try {
			fis = new FileInputStream(srcFile);
			fos = new FileOutputStream(destFile);
		} catch (FileNotFoundException e1) {
			e1.printStackTrace();
		}
		try  (fis;fos) {

CopyFile.java:20: 錯誤: 用作 try-with-resources 資源的變量 fis 既不是最終變量, 也不是實際上的最終變量
try (fis;fos) {
^
CopyFile.java:20: 錯誤: 用作 try-with-resources 資源的變量 fos 既不是最終變量, 也不是實際上的最終變量
try (fis;fos) {

所以,fis和fos需要往外拋出異常,外部調用的地方又需要捕獲異常。比不用此功能還要麻煩。可運行代碼如下:

import java.io.*;
public class CopyFile{
	public static void main(String[] args) {
		try {
			copyJDK9();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
	}
	static void copyJDK9() throws FileNotFoundException {
		File srcFile = new File("testRead.dat");
		File destFile = new File("testCopy.java");
		if (destFile.exists()) {
			destFile.delete();
		}

		FileInputStream	fis = new FileInputStream(srcFile);
		FileOutputStream fos = new FileOutputStream(destFile);
		try  (fis;fos) {
			destFile.createNewFile();
			byte[] buf = new byte[128];
			System.out.println("start copy...");
			int len = -1;
			while ((len = fis.read(buf)) != -1) {
				System.out.println(len);
				fos.write(buf, 0, len);
			}
			System.out.println("success");
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM