JavaSE第20篇:Java之IO流上篇
核心概述:如何獲取和遍歷本地檔案及目錄資訊?如何使用讀寫本地檔案?本篇我們將學習File類來獲取本地檔案資訊,學習遞迴來遍歷檔案及目錄,學習Java中的IO流來實現對本地檔案的讀寫。
目錄
第一章:File類
1.1-概述(瞭解)
java.io.File
類是檔案和目錄路徑名的抽象表示,主要用於檔案和目錄的建立、查詢和刪除等操作。File類將檔案,資料夾和路徑封裝成了物件,提供大量的方法來操作這些物件。
1.2-File類的靜態成員(瞭解)
靜態成員
static String pathSeparator
與系統有關的路徑分隔符。- Window作業系統,分隔符是分號。
- Linux作業系統,分隔符是冒號。
static String separator
與系統有關的預設名稱分隔符。- Window作業系統,名稱分割符號為 \。
- Linux作業系統,名稱分隔符號為 /。
示例
public class Test01 {
public static void main(String[] args) {
System.out.println(File.pathSeparator); // 輸出結果:";"
System.out.println(File.separator); // 輸出結果:"/"
// 注意:不同的作業系統獲取的分隔符是不一樣的
}
}
1.3-File類的構造方法(重要)
構造方法
public File(String pathname)
:通過將給定的路徑名字串轉換為抽象路徑名來建立新的 File例項。public File(String parent, String child)
:從父路徑名字串和子路徑名字串建立新的 File例項。public File(File parent, String child)
:從父抽象路徑名和子路徑名字串建立新的 File例項。
示例
public class Test02 {
public static void main(String[] args) {
// 建立File物件-方式1
File file1 = new File("D:\\JavaCode\\BasicCode\\a");
System.out.println(file1); // 輸出結果: D:\JavaCode\BasicCode\a
// 建立File物件-方式2
File file2 = new File("D:\\JavaCode\\BasicCode\\a","1.txt");
System.out.println(file2); // 輸出結果:D:\JavaCode\BasicCode\a\1.txt
// 建立File物件-方式3
File file3 = new File(file1,"1.txt");
System.out.println(file3); // 輸出結果:D:\JavaCode\BasicCode\a\1.txt
}
注意
- 一個File物件代表硬碟中實際存在的一個檔案或者目錄。
- 無論該路徑下是否存在檔案或者目錄,都不影響File物件的建立。
1.4-File物件獲取功能相關方法(重要)
方法
public String getAbsolutePath()
:返回此File的絕對路徑名字串。public String getPath()
:將此File轉換為路徑名字串。public String getName()
:返回由此File表示的檔案或目錄的名稱。public long length()
:返回由此File表示的檔案的長度。
示例
public class Test03 {
public static void main(String[] args) {
// 1. `public String getAbsolutePath() `:返回此File的絕對路徑名字串。
show1();
// 2. `public String getPath()` :將此File轉換為路徑名字串。
show2();
// 3. `public String getName() `:返回由此File表示的檔案或目錄的名稱。
show3();
// 4. `public long length() `:返回由此File表示的檔案的長度(檔案的大小)
show4();
}
private static void show4() {
// 不存在的資料夾或不存在的檔案或存在的資料夾返回的都是0
File file1 = new File("D:\\JavaCode\\BasicCode\\a");
System.out.println(file1.length()); // 輸出結果:0
File file3 = new File("D:\\JavaCode\\BasicCode\\aa");
System.out.println(file3.length()); // 輸出結果:0
File file2 = new File("D:\\JavaCode\\BasicCode\\a\\logo01.png");
System.out.println(file2.length()); // 輸出結果:11610 位元組
}
private static void show3() {
File file1 = new File("D:\\JavaCode\\BasicCode\\a");
System.out.println(file1.getName()); // 輸出結果:a
File file2 = new File("1.txt"); // 輸出結果:1.txt
System.out.println(file2.getName());
}
private static void show2() {
// 檔案路徑是什麼就返回什麼
File file1 = new File("D:\\JavaCode\\BasicCode\\1.txt");
System.out.println(file1.getPath()); // 輸出結果:D:\JavaCode\BasicCode\1.txt
File file2 = new File("1.txt"); // 輸出結果: 1.txt
System.out.println(file2.getPath());
}
private static void show1() {
File file1 = new File("D:\\JavaCode\\BasicCode\\1.txt");
System.out.println(file1.getAbsoluteFile()); // 輸出結果:D:\JavaCode\BasicCode\1.txt
File file2 = new File("1.txt"); // 輸出結果:D:\JavaCode\BasicCode\1.txt
System.out.println(file2.getAbsoluteFile());
}
}
1.5-絕對路徑和相對路徑(瞭解)
概念
- 絕對路徑:從碟符開始的路徑,這是一個完整的路徑。
- 相對路徑:相對於專案目錄的路徑,這是一個便捷的路徑,開發中經常使用。
示例
public static void main(String[] args) {
// D盤下的bbb.java檔案
File f = new File("D:\\bbb.java");
System.out.println(f.getAbsolutePath());
// 專案下的bbb.java檔案
File f2 = new File("bbb.java");
System.out.println(f2.getAbsolutePath());
}
1.6-File物件的判斷功能相關方法(重要)
方法
public boolean exists()
:此File表示的檔案或目錄是否實際存在。public boolean isDirectory()
:此File表示的是否為目錄。public boolean isFile()
:此File表示的是否為檔案。
示例
public class Test04 {
public static void main(String[] args) {
File file = new File("G:\\typora"); // 真實存在的目錄
// - `public boolean exists()` :此File表示的檔案或目錄是否實際存在。
System.out.println(file.exists()); // 輸出結果: true
// - `public boolean isDirectory()` :此File表示的是否為目錄。
System.out.println(file.isDirectory()); // 輸出結果: true
// - `public boolean isFile()` :此File表示的是否為檔案。
System.out.println(file.isFile()); // 輸出結果:false
}
}
1.7-File物件的建立刪除功能的方法(重要)
方法
public boolean createNewFile()
:當且僅當具有該名稱的檔案尚不存在時,建立一個新的空檔案。public boolean delete()
:刪除由此File表示的檔案或目錄。public boolean mkdir()
:建立由此File表示的目錄。public boolean mkdirs()
:建立由此File表示的目錄,包括任何必需但不存在的父目錄。
示例
public static void main(String[] args) throws IOException {
// 1. `public boolean createNewFile()` :當且僅當具有該名稱的檔案尚不存在時,建立一個新的空檔案。
File file1 = new File("a"); // 已經存在的檔案
System.out.println(file1.createNewFile()); // false
File file2 = new File("b"); // 不存在的檔案
System.out.println(file2.createNewFile()); // true
// 2. `public boolean delete() `:刪除由此File表示的檔案或目錄。
File file3 = new File("c"); // 存在的檔案
System.out.println(file3.delete()); // true
File file4 = new File("b"); // 不存在的檔案
System.out.println(file4.delete()); // true
// 3. `public boolean mkdir()` :建立由此File表示的目錄。
File file5 = new File("e"); // 不存在的檔案目錄
System.out.println(file5.mkdir()); // true
File file6 = new File("e//g/f"); // 多級檔案目錄
System.out.println(file6.mkdir()); // false
// 4. `public boolean mkdirs() `:建立由此File表示的目錄,包括任何必需但不存在的父目錄。
System.out.println(file6.mkdirs()); // true
}
1.8-File物件的目錄遍歷相關方法(重要)
方法
public File[] listFiles()
返回一個File陣列,表示該File目錄中的所有的子檔案或目錄、public File[] listFiles(FileFilter filter)
返回一個File陣列,表示該File目錄中的所有的子檔案或目錄,filter是檔案過濾器,可以過濾不需要的檔案。
示例
public class Test06 {
public static void main(String[] args) {
File file = new File("F:\\JavaCode\\BaseCode");
File[]files = file.listFiles();
for (File item : files) {
System.out.println(item);
}
/*
輸出結果:
F:\JavaCode\BaseCode\.idea
F:\JavaCode\BaseCode\a.txt
F:\JavaCode\BaseCode\Chapter01
F:\JavaCode\BaseCode\d
F:\JavaCode\BaseCode\e
F:\JavaCode\BaseCode\out
*/
}
}
FileFilter介面
java.io.FileFilter
是一個介面,是File的過濾器。 該介面的物件可以傳遞給File類的 listFiles(FileFilter)
方法作為引數, 介面中只有一個方法。
方法:boolean accept(File pathname)
:測試pathname是否應該包含在當前File目錄中,符合則返回true。如果方法返回true,表示需要此路徑,否則此路徑將被忽略。
示例程式碼:過濾出該目錄中所有的.java檔案
介面作為引數,需要傳遞子類物件,重寫其中方法。我們選擇匿名內部類方式,比較簡單。
public static void main(String[] args){
File dir = new File("d:\\demo");
File[] files = dir.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
//判斷如果獲取到的是目錄,直接放行
if(pathname.isDirectory())
return true;
//獲取路徑中的檔名,判斷是否java結尾,是就返回true
return pathname.getName().toLowerCase().endsWith("java");
}
});
for(File file : files){
System.out.println(file);
}
}
第二章:遞迴
2.1-遞迴概述(瞭解)
什麼是遞迴
遞迴,函式(方法)自身呼叫自身的程式設計技巧。
遞迴的分類:
- 直接遞迴稱為方法自身呼叫自己。
- 間接遞迴可以A方法呼叫B方法,B方法呼叫C方法,C方法呼叫A方法。
遞迴注意事項
- 遞迴一定要有條件限定,保證遞迴能夠停止下來,否則會發生棧記憶體溢位。
- 在遞迴中雖然有限定條件,但是遞迴次數不能太多。否則也會發生棧記憶體溢位。
- 構造方法,禁止遞迴
2.2-遞迴練習1(練習)
需求
需求:計算數字1 ~ n的和
分析
num的累和 = num + (num-1)的累和,所以可以把累和的操作定義成一個方法,遞迴呼叫。
程式碼
// 計算數字1 ~ n的和
public static void main(String[] args) {
int sum = getSum(3);
System.out.println(sum);
}
private static int getSum(int n) {
// 判斷遞迴結束條件
if(n==1) {
return 1;
}
// 遞迴任務
return n + getSum(n-1);
}
圖解
2.3-遞迴練習2(練習)
需求
階乘所有小於及等於該數的正整數的積。n的階乘:n! = n * (n‐1) ... 3 * 2 * 1
分析
這與累和類似,只不過換成了乘法運算
- 推理得出:
n! = n * (n‐1)!
程式碼
public static void main(String[] args) {
int result = factorial(3);
System.out.println(result);
}
private static int factorial(int n) {
if(n==1) {
return 1;
}
return n * factorial(n-1);
}
2.4-遞迴練習3(練習)
需求
列印多級目錄及檔案
程式碼
public static void main(String[] args) {
File file = new File("D:\\JavaCode\\BasicCode\\dir");
readFile(file);
}
// 定義讀取目錄檔案的方法
private static void readFile(File file) {
// 獲取子檔案和子目錄
File[]files = file.listFiles();
// 迴圈遍歷子檔案和子目錄
for (File f : files) {
if(f.isDirectory()){
readFile(f);
}else {
System.out.println(f);
}
}
}
2.5-遞迴練習4(練習)
需求
給一個指定的目錄,遞迴實現過濾出該目錄中所有的以及巢狀目中.java檔案
程式碼實現方式1
public static void main(String[] args) {
File file = new File("D:\\JavaCode\\BasicCode");
readFile(file);
}
// 定義讀取目錄檔案的方法
private static void readFile(File file) {
// 獲取子檔案和子目錄
File[]files = file.listFiles();
// 迴圈遍歷子檔案和子目錄
for (File f : files) {
if(f.isDirectory()){
readFile(f);
}else {
if(f.getName().toLowerCase().endsWith(".java")){
System.out.println(f);
}
}
}
}
程式碼事項方式2-檔案過濾器
分析:
-
FileFilter介面作為引數,需要傳遞子類物件,重寫其中方法。我們選擇匿名內部類方式,比較簡單。
-
accept 方法,引數為File,表示當前File下所有的子檔案和子目錄。保留住則返回true,過濾掉則返回 false。保留規則:
- 要麼是.java檔案。
- 要麼是目錄,用於繼續遍歷。
-
通過過濾器的作用, listFiles(FileFilter) 返回的陣列元素中,子檔案物件都是符合條件的,可以直接打
印。
程式碼:
public static void main(String[] args) {
File file = new File("D:\\JavaCode\\BasicCode");
readFile(file);
}
// 定義讀取目錄檔案的方法
private static void readFile(File file) {
// 獲取子檔案和子目錄
File[]files = file.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
// 符合條件的File
return pathname.isDirectory() || pathname.getName().toLowerCase().endsWith(".java");
}
});
// 迴圈遍歷子檔案和子目錄
for (File f : files) {
if(f.isFile()){
System.out.println(f);
}else {
readFile(f);
}
}
}
第三章:認識IO流
3.1-什麼是IO(瞭解)
生活中,你肯定經歷過這樣的場景。當你編輯一個文字檔案,忘記了 ctrl+s ,可能檔案就白白編輯了。當你電腦上插入一個U盤,可以把一個視訊,拷貝到你的電腦硬盤裡。那麼資料都是在哪些裝置上的呢?鍵盤、記憶體、硬碟、外接裝置等等。
我們把這種資料的傳輸,可以看做是一種資料的流動,按照流動的方向,以記憶體為基準,分為 輸入input 和 輸出output ,即流向記憶體是輸入流,流出記憶體的輸出流。
Java中I/O操作主要是指使用 java.io 包下的內容,進行輸入、輸出操作。輸入也叫做讀取資料,輸出也叫做作寫出資料。
3.2-IO的分類(瞭解)
根據資料的流向分為:輸入流和輸出流。
- 輸入流 :把資料從 其他裝置 上讀取到 記憶體 中的流。
- 輸出流 :把資料從 記憶體 中寫出到 其他裝置 上的流。
根據資料的型別分為:位元組流和字元流。
- 位元組流 :以位元組為單位,讀寫資料的流。
- 字元流 :以字元為單位,讀寫資料的流。
3.3-IO的頂級父類(瞭解)
第四章:位元組流
4.1-一切皆為位元組流(瞭解)
一切檔案資料(文字、圖片、視訊等)在儲存時,都是以二進位制數字的形式儲存,都是一個一個的位元組,那麼傳輸時一 樣如此。所以,位元組流可以傳輸任意檔案資料。在操作流的時候,我們要時刻明確,無論使用什麼樣的流物件,底層傳輸的始終為二進位制資料。
提示:8個二進位制位為1個位元組,0000-0000 是1個位元組。
4.2-位元組輸出流OutputStream(重要)
概述
java.io.OutputStream 抽象類是表示位元組輸出流的所有類的超類,將指定的位元組資訊寫出到目的地。它定義了位元組輸出流的基本共性功能方法。
方法
public void close()
:關閉此輸出流並釋放與此流相關聯的任何系統資源。public void write(byte[] b)
:將 b.length位元組從指定的位元組陣列寫入此輸出流。public void write(byte[] b, int off, int len)
:從指定的位元組陣列寫入 len位元組,從偏移量 off開始輸出到此輸出流。public abstract void write(int b)
:將指定的位元組輸出流。
注意事項
close方法,當完成流的操作時,必須呼叫此方法,釋放系統資源。
4.3-FileOutputStream類(重要)
概述
OutputStream
有很多子類,我們從最簡單的一個子類開始。
java.io.FileOutputStream
類是檔案輸出流,用於將資料寫出到檔案。
構造方法
public FileOutputStream(File file)
:建立檔案輸出流以寫入由指定的 File物件表示的檔案。public FileOutputStream(String name)
: 建立檔案輸出流以指定的名稱寫入檔案。
當你建立一個流物件時,必須傳入一個檔案路徑。該路徑下,如果沒有這個檔案,會建立該檔案。如果有這個檔案,會清空這個檔案的資料。
public class FileOutputStreamConstructor {
public static void main(String[] args) throws IOException{
// 使用File物件建立流物件
File file = new File("a.txt");
FileOutputStream fos = new FileOutputStream(file);
// 使用檔名稱建立流物件
//FileOutputStream fos = new FileOutputStream("b.txt");
}
}
寫出位元組資料
public class Test07 {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("a.txt");
// 【write(int b)】
// 寫入的十進位制會轉換成二進位制儲存到a.txt
// 讀取檔案時,檔案會按照指定的編碼格式轉換為對應的內容
fos.write(97); // 寫入→97→110 0001→記憶體→讀取→110 0001→ASCII→a
fos.write(98);
// 【 write(byte[] b)】
byte[]bs = {97,98,99,101,102};
fos.write(bs);
// 【字串轉換位元組陣列getBytes()】
fos.write("你好".getBytes());
// 【write(byte[] b, int off, int len)指定長度的位元組陣列】
fos.write("xyz".getBytes(),0,2);
fos.close();
}
}
資料追加續寫
問題:經過以上的演示,每次程式執行,建立輸出流物件,都會清空目標檔案中的資料。如何保留目標檔案中資料,還能 繼續新增新資料呢?
解決方案:
public FileOutputStream(File file, boolean append)
: 建立檔案輸出流以寫入由指定的 File物件表示的檔案。public FileOutputStream(String name, boolean append)
: 建立檔案輸出流以指定的名稱寫入檔案。- 引數boolean append: true 表示追加資料, false 表示清空原有資料。
public class Test08 {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("a.txt",true);
fos.write("我是新追加的資料".getBytes());
fos.close();
}
}
write方法原始碼解析
- 我呼叫write方法寫出資料的時候,JDK原始碼中最終呼叫的方法是writeBytes()方法。
private native void writeBytes(byte b[], int off, int len, boolean append)throws IOException
- 方法是本地方法是和作業系統互動的方法。
- 作業系統本身就具有IO功能,因此JVM是呼叫作業系統中的功能實現資料的讀寫!
寫出換行
系統中的換行:
- Windows系統裡,每行結尾是 回車+換行 ,即
\r\n
; - Unix系統裡,每行結尾只有 換行 ,即
\n
; - Mac系統裡,每行結尾是 回車 ,即
\r
。從 Mac OS X開始與Linux統一。
程式碼:
FileOutputStream fos = new FileOutputStream("b.txt",true);
for (int i = 0; i < 10; i++) {
fos.write(("\r\n第" + i +"行資料:" + i*100).getBytes() );
}
fos.close();
4.4-位元組輸入流InputStream(重要)
概述
java.io.InputStream
抽象類是表示位元組輸入流的所有類的超類,可以讀取位元組資訊到記憶體中。它定義了位元組輸入 流的基本共性功能方法。
方法
-
public void close()
:關閉此輸入流並釋放與此流相關聯的任何系統資源。 -
public abstract int read()
: 從輸入流讀取資料的下一個位元組。 -
public int read(byte[] b)
: 從輸入流中讀取一些位元組數,並將它們儲存到位元組陣列 b中 。-
使用陣列讀取,每次讀取多個位元組,減少了系統間的IO操作次數,從而提高了讀寫的效率,建議開發中使
用。
-
注意:close方法,當完成流的操作時,必須呼叫此方法,釋放系統資源。
4.5-FileInputStream類(重要)
java.io.FileInputStream
類是檔案輸入流,從檔案中讀取位元組。
構造方法
FileInputStream(File file)
: 通過開啟與實際檔案的連線來建立一個 FileInputStream ,該檔案由檔案系 統中的 File物件 fifile命名。FileInputStream(String name)
: 通過開啟與實際檔案的連線來建立一個 FileInputStream ,該檔案由檔案 系統中的路徑名 name命名。
當你建立一個流物件時,必須傳入一個檔案路徑。該路徑下,如果沒有該檔案,會丟擲FileNotFoundException
public class FileInputStreamConstructor {
public static void main(String[] args) throws IOException{
// 使用File物件建立流物件
File file = new File("a.txt");
FileInputStream fos = new FileInputStream(file);
// 使用檔名稱建立流物件
FileInputStream fos = new FileInputStream("b.txt");
}
}
讀取位元組資料
讀取位元組:read
方法,每次可以讀取一個位元組的資料,提升為int型別,讀取到檔案末尾,返回-1
,程式碼使用演示:
檔案:a.txt
abcd
讀取:a.txt檔案
public class Test09 {
public static void main(String[] args) throws IOException {
// 使用檔名稱建立流物件
FileInputStream fis = new FileInputStream("a.txt");
// 讀取資料,返回一個位元組
int read = fis.read();
System.out.println((char) read); // a
read = fis.read();
System.out.println((char) read); // b
read = fis.read();
System.out.println((char) read); // c
read = fis.read();
System.out.println((char) read); // d
read = fis.read();
System.out.println(read); // -1
}
}
注意:如果檔案中存在-1,我們在讀取檔案時也不會直接讀取到-1,因為-1是兩個位元組,即-
和1
。每個檔案都會被作業系統賦予一個結束的標識,JVM呼叫作業系統功能實現檔案讀取的,因此作業系統讀取到檔案結束標識後,會將表示返回到JVM中,而JVM接收到檔案結束標識後,返回read()方法-1。
使用迴圈改進:
public static void main(String[] args) throws IOException{
// 使用檔名稱建立流物件
FileInputStream fis = new FileInputStream("a.txt");
// 定義變數,儲存資料
int b = 0 ;
// 迴圈讀取
while ((b = fis.read())!=-1) {
System.out.println((char)b);
}
// 關閉資源
fis.close();
}
使用位元組陣列讀取
read(byte[] b)
,每次讀取b的長度個位元組到陣列中,返回讀取到的有效位元組個數,讀取到末尾時,返回-1
,程式碼使用演示:
public static void main(String[] args) throws IOException{
// 使用檔名稱建立流物件.
FileInputStream fis = new FileInputStream("read.txt"); // 檔案中為abcde
// 定義變數,作為有效個數
int len ;
// 定義位元組陣列,作為裝位元組資料的容器
byte[] b = new byte[2];
// 迴圈讀取
while (( len= fis.read(b))!=-1) {
// 每次讀取後,把陣列變成字串列印
System.out.println(new String(b));
}
// 關閉資源
fis.close();
}
錯誤資料d
,是由於最後一次讀取時,只讀取一個位元組e
,陣列中,上次讀取的資料沒有被完全替換,所以要通過len
,獲取有效的位元組,程式碼使用演示:
public static void main(String[] args) throws IOException{
// 使用檔名稱建立流物件.
FileInputStream fis = new FileInputStream("read.txt"); // 檔案中為abcde
// 定義變數,作為有效個數
int len ;
// 定義位元組陣列,作為裝位元組資料的容器
byte[] b = new byte[2];
// 迴圈讀取
while (( len= fis.read(b))!=-1) {
// 每次讀取後,把陣列的有效位元組部分,變成字串列印
System.out.println(new String(b,0,len));// len 每次讀取的有效位元組個數
}
// 關閉資源
fis.close();
}
第五章:IO異常處理
5.1-JDK7之前的處理方式(重要)
處理方式:try-catch-finally
- 在finally中釋放資源
程式碼:
public static void main(String[] args) {
// 建立輸出流物件,向指定的檔案中追加寫入資料
FileWriter fw2 = null;
try{
fw2 = new FileWriter("day07_IO\\b.txt",true);
for (int i = 0; i < 10; i++) {
fw2.write("你好,新的世界!" + i + "\r\n");
}
}catch (IOException e) {
e.printStackTrace();
}finally {
if(fw2!=null){
try {
fw2.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
5.2-JDK7中的新特性(瞭解)
處理方式:JDK7優化後的 try-with-resource 語句,該語句確保了每個資源在語句結束時關閉。所謂的資源 (resource)是指在程式完成後,必須關閉的物件。
try (建立流物件語句,如果多個,使用';'隔開) {
// 讀寫資料
} catch (IOException e) {
e.printStackTrace();
}
程式碼:
public static void main(String[] args) {
// 建立輸出流物件,向指定的檔案中追加寫入資料
try(FileWriter fw2 = new FileWriter("day07_IO\\b.txt",true)){
for (int i = 0; i < 100; i++) {
fw2.write("你好,新的世界!" + i + "\r\n");
}
}catch (IOException e) {
e.printStackTrace();
}
}
5.3-JDK9中的新特性(瞭解)
JDK9中 try-with-resource 的改進,對於引入物件的方式,支援的更加簡潔。被引入的物件,同樣可以自動關閉, 無需手動close
格式
// 被final修飾的物件
final Resource resource1 = new Resource("resource1");
// 普通物件
Resource resource2 = new Resource("resource2");
// 引入方式:直接引入
try (resource1; resource2) {
// 使用物件
} catch (IOException e) {
e.printStackTrace();
}
程式碼
public static void main(String[] args) throws IOException {
// 建立輸出流物件,向指定的檔案中追加寫入資料
FileWriter fw2 = new FileWriter("day07_IO\\b.txt",true);
try(fw2){
for (int i = 0; i < 100; i++) {
fw2.write("你好,新的世界!" + i + "\r\n");
}
}catch (IOException e) {
e.printStackTrace();
}
}
第六章:檔案複製案例
6.1-需求
使用位元組流可以進行任何檔案的複製,因為位元組流操作的是組成檔案的最小單元-位元組。
實現圖片檔案的複製
6.2-實現程式碼
public static void main(String[] args) throws IOException {
long s = System.currentTimeMillis();
// 建立輸入流物件-用來讀取本地檔案
FileInputStream fis = new FileInputStream("D:\\test.jpg");
// 建立輸出流物件-用來寫入本地檔案
FileOutputStream fos = new FileOutputStream("IODemo\\test_copy.jpg");
// 建立位元組陣列,一次從本地讀取多個位元組
byte[]bts = new byte[1024];
int len = 0; // 表示讀取的有效位元組個數
// 迴圈讀取本地資料
while ((len=fis.read(bts))!=-1){
// 把實際讀取的位元組寫入本地檔案
fos.write(bts,0,len);
}
// 關閉輸出流資源
fos.close();
// 關閉輸入流資源
fis.close();
long e = System.currentTimeMillis();
System.out.println("複製成功!");
System.out.println("共耗時" + (e-s)+"毫秒");
}
第七章:位元組緩衝流
7.1-概述(瞭解)
緩衝流:針對基礎流物件進行高效處理的流物件。或者為基礎流增加功能。
位元組緩衝流:BufferedInputStream
,BufferedOutputStream
緩衝流的基本原理,是在建立流物件時,會建立一個內建的預設大小的緩衝區陣列,通過緩衝區讀寫,減少系統IO次數,從而提高讀寫的效率。
7.2-位元組緩衝流的使用(重要)
BufferedOutputStream繼承OutputStream,write()方法不必從新學習。
BufferedInputStream繼承InputStream,read()方法不必從新學習。
構造方法
public BufferedInputStream(InputStream in)
:建立一個 新的緩衝輸入流。public BufferedOutputStream(OutputStream out)
: 建立一個新的緩衝輸出流。
// 建立位元組緩衝輸入流
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("bis.txt"));
// 建立位元組緩衝輸出流
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("bos.txt"));
注意:在使用緩衝流時,必須傳遞基礎流。
效率測試
查詢API,緩衝流讀寫方法與基本的流是一致的,我們通過複製大檔案(375MB),測試它的效率。
基礎流:
public static void main(String[] args) throws IOException {
// 記錄開始時間
long start = System.currentTimeMillis();
// 建立流物件
FileInputStream fis = new FileInputStream("jdk8.exe");
FileOutputStream fos = new FileOutputStream("copy.exe")
// 讀寫資料
int b = 0;
while ((b = fis.read()) != -1) {
fos.write(b);
}
// 記錄結束時間
long end = System.currentTimeMillis();
System.out.println("普通流複製時間:"+(end - start)+" 毫秒");
}
// 基礎流複製時間:1分鐘以上
緩衝流
public static void main(String[] args) throws FileNotFoundException {
// 記錄開始時間
long start = System.currentTimeMillis();
// 建立流物件
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("jdk8.exe"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy.exe"));
// 讀寫資料
int len = 0;
while ((len = bis.read()) != -1) {
bos.write(len);
}
// 記錄結束時間
long end = System.currentTimeMillis();
System.out.println("緩衝流使用陣列複製時間:"+(end - start)+" 毫秒");
}
// 緩衝流複製時間:6969 毫秒,約7秒鐘
緩衝流+位元組陣列
public static void main(String[] args) throws IOException {
// 記錄開始時間
long start = System.currentTimeMillis();
// 建立流物件
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("C:\\Users\\70418\\Desktop\\test\\jdk8.exe"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("C:\\Users\\70418\\Desktop\\test\\copy2.exe"));
// 讀寫資料
int len = 0;
byte[]b = new byte[1024];
while ((len = bis.read(b)) != -1) {
bos.write(b,0,len);
}
// 記錄結束時間
long end = System.currentTimeMillis();
System.out.println("緩衝流使用陣列複製時間:"+(end - start)+" 毫秒");
}
// 緩衝流使用陣列複製時間:796 毫秒,約0.7秒