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();
}
}
}