核心概述:如何獲取和遍歷本地文件及目錄信息?如何使用讀寫本地文件巍沙?本篇我們將學習File類來獲取本地文件信息盼理,學習遞歸來遍歷文件及目錄净神,學習Java中的IO流來實現(xiàn)對本地文件的讀寫竿报。
第一章:File類
1.1-概述(了解)
java.io.File
類是文件和目錄路徑名的抽象表示铅乡,主要用于文件和目錄的創(chuàng)建、查找和刪除等操作烈菌。File類將文件阵幸,文件夾和路徑封裝成了對象,提供大量的方法來操作這些對象芽世。
1.2-File類的靜態(tài)成員(了解)
靜態(tài)成員
-
static String pathSeparator
與系統(tǒng)有關(guān)的路徑分隔符挚赊。- Window操作系統(tǒng),分隔符是分號济瓢。
- Linux操作系統(tǒng)咬腕,分隔符是冒號。
-
static String separator
與系統(tǒng)有關(guān)的默認名稱分隔符葬荷。- Window操作系統(tǒng),名稱分割符號為 \纽帖。
- Linux操作系統(tǒng)宠漩,名稱分隔符號為 /。
示例
public class Test01 {
public static void main(String[] args) {
System.out.println(File.pathSeparator); // 輸出結(jié)果:";"
System.out.println(File.separator); // 輸出結(jié)果:"/"
// 注意:不同的操作系統(tǒng)獲取的分隔符是不一樣的
}
}
1.3-File類的構(gòu)造方法(重要)
構(gòu)造方法
-
public File(String pathname)
:通過將給定的路徑名字符串轉(zhuǎn)換為抽象路徑名來創(chuàng)建新的 File實例懊直。 -
public File(String parent, String child)
:從父路徑名字符串和子路徑名字符串創(chuàng)建新的 File實例扒吁。 -
public File(File parent, String child)
:從父抽象路徑名和子路徑名字符串創(chuàng)建新的 File實例。
示例
public class Test02 {
public static void main(String[] args) {
// 創(chuàng)建File對象-方式1
File file1 = new File("D:\\JavaCode\\BasicCode\\a");
System.out.println(file1); // 輸出結(jié)果: D:\JavaCode\BasicCode\a
// 創(chuàng)建File對象-方式2
File file2 = new File("D:\\JavaCode\\BasicCode\\a","1.txt");
System.out.println(file2); // 輸出結(jié)果:D:\JavaCode\BasicCode\a\1.txt
// 創(chuàng)建File對象-方式3
File file3 = new File(file1,"1.txt");
System.out.println(file3); // 輸出結(jié)果:D:\JavaCode\BasicCode\a\1.txt
}
注意
- 一個File對象代表硬盤中實際存在的一個文件或者目錄室囊。
- 無論該路徑下是否存在文件或者目錄雕崩,都不影響File對象的創(chuàng)建魁索。
1.4-File對象獲取功能相關(guān)方法(重要)
方法
-
public String getAbsolutePath()
:返回此File的絕對路徑名字符串。 -
public String getPath()
:將此File轉(zhuǎn)換為路徑名字符串盼铁。 -
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轉(zhuǎn)換為路徑名字符串鹏控。
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()); // 輸出結(jié)果:0
File file3 = new File("D:\\JavaCode\\BasicCode\\aa");
System.out.println(file3.length()); // 輸出結(jié)果:0
File file2 = new File("D:\\JavaCode\\BasicCode\\a\\logo01.png");
System.out.println(file2.length()); // 輸出結(jié)果:11610 字節(jié)
}
private static void show3() {
File file1 = new File("D:\\JavaCode\\BasicCode\\a");
System.out.println(file1.getName()); // 輸出結(jié)果:a
File file2 = new File("1.txt"); // 輸出結(jié)果: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()); // 輸出結(jié)果:D:\JavaCode\BasicCode\1.txt
File file2 = new File("1.txt"); // 輸出結(jié)果: 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()); // 輸出結(jié)果:D:\JavaCode\BasicCode\1.txt
File file2 = new File("1.txt"); // 輸出結(jié)果:D:\JavaCode\BasicCode\1.txt
System.out.println(file2.getAbsoluteFile());
}
}
1.5-絕對路徑和相對路徑(了解)
概念
- 絕對路徑:從盤符開始的路徑肤寝,這是一個完整的路徑当辐。
- 相對路徑:相對于項目目錄的路徑,這是一個便捷的路徑鲤看,開發(fā)中經(jīng)常使用缘揪。
示例
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對象的判斷功能相關(guān)方法(重要)
方法
-
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()); // 輸出結(jié)果: true
// - `public boolean isDirectory()` :此File表示的是否為目錄澡刹。
System.out.println(file.isDirectory()); // 輸出結(jié)果: true
// - `public boolean isFile()` :此File表示的是否為文件呻征。
System.out.println(file.isFile()); // 輸出結(jié)果:false
}
}
1.7-File對象的創(chuàng)建刪除功能的方法(重要)
方法
-
public boolean createNewFile()
:當且僅當具有該名稱的文件尚不存在時,創(chuàng)建一個新的空文件罢浇。 -
public boolean delete()
:刪除由此File表示的文件或目錄陆赋。 -
public boolean mkdir()
:創(chuàng)建由此File表示的目錄。 -
public boolean mkdirs()
:創(chuàng)建由此File表示的目錄嚷闭,包括任何必需但不存在的父目錄攒岛。
示例
public static void main(String[] args) throws IOException {
// 1. `public boolean createNewFile()` :當且僅當具有該名稱的文件尚不存在時,創(chuàng)建一個新的空文件胞锰。
File file1 = new File("a"); // 已經(jīng)存在的文件
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()` :創(chuàng)建由此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() `:創(chuàng)建由此File表示的目錄嗅榕,包括任何必需但不存在的父目錄顺饮。
System.out.println(file6.mkdirs()); // true
}
1.8-File對象的目錄遍歷相關(guān)方法(重要)
方法
-
public File[] listFiles()
返回一個File數(shù)組,表示該File目錄中的所有的子文件或目錄凌那、 -
public File[] listFiles(FileFilter filter)
返回一個File數(shù)組兼雄,表示該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);
}
/*
輸出結(jié)果:
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)
方法作為參數(shù)佃乘, 接口中只有一個方法囱井。
方法:boolean accept(File pathname)
:測試pathname是否應該包含在當前File目錄中,符合則返回true趣避。如果方法返回true庞呕,表示需要此路徑,否則此路徑將被忽略鹅巍。
示例代碼:過濾出該目錄中所有的.java文件
接口作為參數(shù)千扶,需要傳遞子類對象,重寫其中方法骆捧。我們選擇匿名內(nèi)部類方式澎羞,比較簡單。
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結(jié)尾,是就返回true
return pathname.getName().toLowerCase().endsWith("java");
}
});
for(File file : files){
System.out.println(file);
}
}
第二章:遞歸
2.1-遞歸概述(了解)
什么是遞歸
遞歸枫攀,函數(shù)(方法)自身調(diào)用自身的編程技巧括饶。
遞歸的分類:
- 直接遞歸稱為方法自身調(diào)用自己。
- 間接遞歸可以A方法調(diào)用B方法来涨,B方法調(diào)用C方法图焰,C方法調(diào)用A方法。
遞歸注意事項
- 遞歸一定要有條件限定蹦掐,保證遞歸能夠停止下來技羔,否則會發(fā)生棧內(nèi)存溢出。
- 在遞歸中雖然有限定條件卧抗,但是遞歸次數(shù)不能太多藤滥。否則也會發(fā)生棧內(nèi)存溢出。
- 構(gòu)造方法,禁止遞歸
2.2-遞歸練習1(練習)
需求
需求:計算數(shù)字1 ~ n的和
分析
num的累和 = num + (num-1)的累和社裆,所以可以把累和的操作定義成一個方法拙绊,遞歸調(diào)用。
代碼
// 計算數(shù)字1 ~ n的和
public static void main(String[] args) {
int sum = getSum(3);
System.out.println(sum);
}
private static int getSum(int n) {
// 判斷遞歸結(jié)束條件
if(n==1) {
return 1;
}
// 遞歸任務
return n + getSum(n-1);
}
圖解
2.3-遞歸練習2(練習)
需求
階乘所有小于及等于該數(shù)的正整數(shù)的積泳秀。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();
// 循環(huán)遍歷子文件和子目錄
for (File f : files) {
if(f.isDirectory()){
readFile(f);
}else {
System.out.println(f);
}
}
}
2.5-遞歸練習4(練習)
需求
給一個指定的目錄标沪,遞歸實現(xiàn)過濾出該目錄中所有的以及嵌套目中.java文件
代碼實現(xiàn)方式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();
// 循環(huán)遍歷子文件和子目錄
for (File f : files) {
if(f.isDirectory()){
readFile(f);
}else {
if(f.getName().toLowerCase().endsWith(".java")){
System.out.println(f);
}
}
}
}
代碼事項方式2-文件過濾器
分析:
FileFilter接口作為參數(shù),需要傳遞子類對象嗜傅,重寫其中方法谨娜。我們選擇匿名內(nèi)部類方式,比較簡單磺陡。
-
accept 方法,參數(shù)為File,表示當前File下所有的子文件和子目錄币他。保留住則返回true坞靶,過濾掉則返回 false。保留規(guī)則:
- 要么是.java文件蝴悉。
- 要么是目錄彰阴,用于繼續(xù)遍歷。
-
通過過濾器的作用拍冠, listFiles(FileFilter) 返回的數(shù)組元素中尿这,子文件對象都是符合條件的,可以直接打
印庆杜。
代碼:
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");
}
});
// 循環(huán)遍歷子文件和子目錄
for (File f : files) {
if(f.isFile()){
System.out.println(f);
}else {
readFile(f);
}
}
}
第三章:認識IO流
3.1-什么是IO(了解)
生活中射众,你肯定經(jīng)歷過這樣的場景。當你編輯一個文本文件晃财,忘記了 ctrl+s 叨橱,可能文件就白白編輯了。當你電腦上插入一個U盤断盛,可以把一個視頻罗洗,拷貝到你的電腦硬盤里。那么數(shù)據(jù)都是在哪些設(shè)備上的呢钢猛?鍵盤伙菜、內(nèi)存、硬盤命迈、外接設(shè)備等等贩绕。
我們把這種數(shù)據(jù)的傳輸,可以看做是一種數(shù)據(jù)的流動躺翻,按照流動的方向丧叽,以內(nèi)存為基準,分為 輸入input 和 輸出output 公你,即流向內(nèi)存是輸入流踊淳,流出內(nèi)存的輸出流。
Java中I/O操作主要是指使用 java.io 包下的內(nèi)容陕靠,進行輸入迂尝、輸出操作。輸入也叫做讀取數(shù)據(jù)剪芥,輸出也叫做作寫出數(shù)據(jù)垄开。
3.2-IO的分類(了解)
根據(jù)數(shù)據(jù)的流向分為:輸入流和輸出流。
- 輸入流 :把數(shù)據(jù)從 其他設(shè)備 上讀取到 內(nèi)存 中的流税肪。
- 輸出流 :把數(shù)據(jù)從 內(nèi)存 中寫出到 其他設(shè)備 上的流溉躲。
根據(jù)數(shù)據(jù)的類型分為:字節(jié)流和字符流榜田。
- 字節(jié)流 :以字節(jié)為單位,讀寫數(shù)據(jù)的流锻梳。
- 字符流 :以字符為單位箭券,讀寫數(shù)據(jù)的流。
3.3-IO的頂級父類(了解)
第四章:字節(jié)流
4.1-一切皆為字節(jié)流(了解)
一切文件數(shù)據(jù)(文本疑枯、圖片辩块、視頻等)在存儲時,都是以二進制數(shù)字的形式保存荆永,都是一個一個的字節(jié)废亭,那么傳輸時一 樣如此。所以具钥,字節(jié)流可以傳輸任意文件數(shù)據(jù)豆村。在操作流的時候,我們要時刻明確氓拼,無論使用什么樣的流對象你画,底層傳輸?shù)氖冀K為二進制數(shù)據(jù)。
提示:8個二進制位為1個字節(jié)桃漾,0000-0000 是1個字節(jié)坏匪。
4.2-字節(jié)輸出流OutputStream(重要)
概述
java.io.OutputStream 抽象類是表示字節(jié)輸出流的所有類的超類,將指定的字節(jié)信息寫出到目的地撬统。它定義了字節(jié)輸出流的基本共性功能方法适滓。
方法
-
public void close()
:關(guān)閉此輸出流并釋放與此流相關(guān)聯(lián)的任何系統(tǒng)資源。 -
public void write(byte[] b)
:將 b.length字節(jié)從指定的字節(jié)數(shù)組寫入此輸出流恋追。 -
public void write(byte[] b, int off, int len)
:從指定的字節(jié)數(shù)組寫入 len字節(jié)凭迹,從偏移量 off開始輸出到此輸出流。 -
public abstract void write(int b)
:將指定的字節(jié)輸出流苦囱。
注意事項
close方法嗅绸,當完成流的操作時,必須調(diào)用此方法撕彤,釋放系統(tǒng)資源鱼鸠。
4.3-FileOutputStream類(重要)
概述
OutputStream
有很多子類,我們從最簡單的一個子類開始羹铅。
java.io.FileOutputStream
類是文件輸出流蚀狰,用于將數(shù)據(jù)寫出到文件。
構(gòu)造方法
-
public FileOutputStream(File file)
:創(chuàng)建文件輸出流以寫入由指定的 File對象表示的文件职员。 -
public FileOutputStream(String name)
: 創(chuàng)建文件輸出流以指定的名稱寫入文件麻蹋。
當你創(chuàng)建一個流對象時,必須傳入一個文件路徑焊切。該路徑下扮授,如果沒有這個文件芳室,會創(chuàng)建該文件。如果有這個文件刹勃,會清空這個文件的數(shù)據(jù)渤愁。
public class FileOutputStreamConstructor {
public static void main(String[] args) throws IOException{
// 使用File對象創(chuàng)建流對象
File file = new File("a.txt");
FileOutputStream fos = new FileOutputStream(file);
// 使用文件名稱創(chuàng)建流對象
//FileOutputStream fos = new FileOutputStream("b.txt");
}
}
寫出字節(jié)數(shù)據(jù)
public class Test07 {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("a.txt");
// 【write(int b)】
// 寫入的十進制會轉(zhuǎn)換成二進制存儲到a.txt
// 讀取文件時,文件會按照指定的編碼格式轉(zhuǎn)換為對應的內(nèi)容
fos.write(97); // 寫入→97→110 0001→內(nèi)存→讀取→110 0001→ASCII→a
fos.write(98);
// 【 write(byte[] b)】
byte[]bs = {97,98,99,101,102};
fos.write(bs);
// 【字符串轉(zhuǎn)換字節(jié)數(shù)組getBytes()】
fos.write("你好".getBytes());
// 【write(byte[] b, int off, int len)指定長度的字節(jié)數(shù)組】
fos.write("xyz".getBytes(),0,2);
fos.close();
}
}
數(shù)據(jù)追加續(xù)寫
問題:經(jīng)過以上的演示深夯,每次程序運行,創(chuàng)建輸出流對象诺苹,都會清空目標文件中的數(shù)據(jù)咕晋。如何保留目標文件中數(shù)據(jù),還能 繼續(xù)添加新數(shù)據(jù)呢收奔?
解決方案:
-
public FileOutputStream(File file, boolean append)
: 創(chuàng)建文件輸出流以寫入由指定的 File對象表示的文件掌呜。 -
public FileOutputStream(String name, boolean append)
: 創(chuàng)建文件輸出流以指定的名稱寫入文件。 - 參數(shù)boolean append: true 表示追加數(shù)據(jù)坪哄, false 表示清空原有數(shù)據(jù)质蕉。
public class Test08 {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("a.txt",true);
fos.write("我是新追加的數(shù)據(jù)".getBytes());
fos.close();
}
}
write方法源碼解析
- 我調(diào)用write方法寫出數(shù)據(jù)的時候,JDK源代碼中最終調(diào)用的方法是writeBytes()方法翩肌。
-
private native void writeBytes(byte b[], int off, int len, boolean append)throws IOException
- 方法是本地方法是和操作系統(tǒng)交互的方法模暗。
- 操作系統(tǒng)本身就具有IO功能,因此JVM是調(diào)用操作系統(tǒng)中的功能實現(xiàn)數(shù)據(jù)的讀寫念祭!
寫出換行
系統(tǒng)中的換行:
- Windows系統(tǒng)里兑宇,每行結(jié)尾是 回車+換行 ,即
\r\n
粱坤; - Unix系統(tǒng)里隶糕,每行結(jié)尾只有 換行 ,即
\n
站玄; - Mac系統(tǒng)里枚驻,每行結(jié)尾是 回車 ,即
\r
株旷。從 Mac OS X開始與Linux統(tǒng)一再登。
代碼:
FileOutputStream fos = new FileOutputStream("b.txt",true);
for (int i = 0; i < 10; i++) {
fos.write(("\r\n第" + i +"行數(shù)據(jù):" + i*100).getBytes() );
}
fos.close();
4.4-字節(jié)輸入流InputStream(重要)
概述
java.io.InputStream
抽象類是表示字節(jié)輸入流的所有類的超類,可以讀取字節(jié)信息到內(nèi)存中灾常。它定義了字節(jié)輸入 流的基本共性功能方法霎冯。
方法
public void close()
:關(guān)閉此輸入流并釋放與此流相關(guān)聯(lián)的任何系統(tǒng)資源。public abstract int read()
: 從輸入流讀取數(shù)據(jù)的下一個字節(jié)钞瀑。-
public int read(byte[] b)
: 從輸入流中讀取一些字節(jié)數(shù)沈撞,并將它們存儲到字節(jié)數(shù)組 b中 。-
使用數(shù)組讀取雕什,每次讀取多個字節(jié)缠俺,減少了系統(tǒng)間的IO操作次數(shù)显晶,從而提高了讀寫的效率,建議開發(fā)中使
用壹士。
-
注意:close方法磷雇,當完成流的操作時,必須調(diào)用此方法躏救,釋放系統(tǒng)資源唯笙。
4.5-FileInputStream類(重要)
java.io.FileInputStream
類是文件輸入流,從文件中讀取字節(jié)盒使。
構(gòu)造方法
-
FileInputStream(File file)
: 通過打開與實際文件的連接來創(chuàng)建一個 FileInputStream 崩掘,該文件由文件系 統(tǒng)中的 File對象 fifile命名。 -
FileInputStream(String name)
: 通過打開與實際文件的連接來創(chuàng)建一個 FileInputStream 少办,該文件由文件 系統(tǒng)中的路徑名 name命名苞慢。
當你創(chuàng)建一個流對象時,必須傳入一個文件路徑英妓。該路徑下挽放,如果沒有該文件,會拋出FileNotFoundException
public class FileInputStreamConstructor {
public static void main(String[] args) throws IOException{
// 使用File對象創(chuàng)建流對象
File file = new File("a.txt");
FileInputStream fos = new FileInputStream(file);
// 使用文件名稱創(chuàng)建流對象
FileInputStream fos = new FileInputStream("b.txt");
}
}
讀取字節(jié)數(shù)據(jù)
讀取字節(jié):read
方法,每次可以讀取一個字節(jié)的數(shù)據(jù)蔓纠,提升為int類型辑畦,讀取到文件末尾,返回-1
贺纲,代碼使用演示:
文件:a.txt
abcd
讀群焦搿:a.txt文件
public class Test09 {
public static void main(String[] args) throws IOException {
// 使用文件名稱創(chuàng)建流對象
FileInputStream fis = new FileInputStream("a.txt");
// 讀取數(shù)據(jù),返回一個字節(jié)
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是兩個字節(jié),即-
和1
懈叹。每個文件都會被操作系統(tǒng)賦予一個結(jié)束的標識乖杠,JVM調(diào)用操作系統(tǒng)功能實現(xiàn)文件讀取的,因此操作系統(tǒng)讀取到文件結(jié)束標識后澄成,會將表示返回到JVM中胧洒,而JVM接收到文件結(jié)束標識后,返回read()方法-1墨状。
使用循環(huán)改進:
public static void main(String[] args) throws IOException{
// 使用文件名稱創(chuàng)建流對象
FileInputStream fis = new FileInputStream("a.txt");
// 定義變量卫漫,保存數(shù)據(jù)
int b = 0 ;
// 循環(huán)讀取
while ((b = fis.read())!=-1) {
System.out.println((char)b);
}
// 關(guān)閉資源
fis.close();
}
使用字節(jié)數(shù)組讀取
read(byte[] b)
,每次讀取b的長度個字節(jié)到數(shù)組中肾砂,返回讀取到的有效字節(jié)個數(shù)列赎,讀取到末尾時,返回-1
镐确,代碼使用演示:
public static void main(String[] args) throws IOException{
// 使用文件名稱創(chuàng)建流對象.
FileInputStream fis = new FileInputStream("read.txt"); // 文件中為abcde
// 定義變量包吝,作為有效個數(shù)
int len 饼煞;
// 定義字節(jié)數(shù)組,作為裝字節(jié)數(shù)據(jù)的容器
byte[] b = new byte[2];
// 循環(huán)讀取
while (( len= fis.read(b))!=-1) {
// 每次讀取后,把數(shù)組變成字符串打印
System.out.println(new String(b));
}
// 關(guān)閉資源
fis.close();
}
錯誤數(shù)據(jù)d
砖瞧,是由于最后一次讀取時,只讀取一個字節(jié)e
嚷狞,數(shù)組中褂乍,上次讀取的數(shù)據(jù)沒有被完全替換裂允,所以要通過len
十饥,獲取有效的字節(jié),代碼使用演示:
public static void main(String[] args) throws IOException{
// 使用文件名稱創(chuàng)建流對象.
FileInputStream fis = new FileInputStream("read.txt"); // 文件中為abcde
// 定義變量,作為有效個數(shù)
int len 帅刊;
// 定義字節(jié)數(shù)組弟灼,作為裝字節(jié)數(shù)據(jù)的容器
byte[] b = new byte[2];
// 循環(huán)讀取
while (( len= fis.read(b))!=-1) {
// 每次讀取后,把數(shù)組的有效字節(jié)部分掩驱,變成字符串打印
System.out.println(new String(b芒划,0,len));// len 每次讀取的有效字節(jié)個數(shù)
}
// 關(guān)閉資源
fis.close();
}
第五章:IO異常處理
5.1-JDK7之前的處理方式(重要)
處理方式:try-catch-finally
- 在finally中釋放資源
代碼:
public static void main(String[] args) {
// 創(chuàng)建輸出流對象欧穴,向指定的文件中追加寫入數(shù)據(jù)
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優(yōu)化后的 try-with-resource 語句涮帘,該語句確保了每個資源在語句結(jié)束時關(guān)閉拼苍。所謂的資源 (resource)是指在程序完成后,必須關(guān)閉的對象调缨。
try (創(chuàng)建流對象語句疮鲫,如果多個,使用';'隔開) {
// 讀寫數(shù)據(jù)
} catch (IOException e) {
e.printStackTrace();
}
代碼:
public static void main(String[] args) {
// 創(chuàng)建輸出流對象,向指定的文件中追加寫入數(shù)據(jù)
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 的改進,對于引入對象的方式伤哺,支持的更加簡潔燕侠。被引入的對象,同樣可以自動關(guān)閉立莉, 無需手動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 {
// 創(chuàng)建輸出流對象绢彤,向指定的文件中追加寫入數(shù)據(jù)
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-需求
使用字節(jié)流可以進行任何文件的復制杖虾,因為字節(jié)流操作的是組成文件的最小單元-字節(jié)。
實現(xiàn)圖片文件的復制
6.2-實現(xiàn)代碼
public static void main(String[] args) throws IOException {
long s = System.currentTimeMillis();
// 創(chuàng)建輸入流對象-用來讀取本地文件
FileInputStream fis = new FileInputStream("D:\\test.jpg");
// 創(chuàng)建輸出流對象-用來寫入本地文件
FileOutputStream fos = new FileOutputStream("IODemo\\test_copy.jpg");
// 創(chuàng)建字節(jié)數(shù)組,一次從本地讀取多個字節(jié)
byte[]bts = new byte[1024];
int len = 0; // 表示讀取的有效字節(jié)個數(shù)
// 循環(huán)讀取本地數(shù)據(jù)
while ((len=fis.read(bts))!=-1){
// 把實際讀取的字節(jié)寫入本地文件
fos.write(bts,0,len);
}
// 關(guān)閉輸出流資源
fos.close();
// 關(guān)閉輸入流資源
fis.close();
long e = System.currentTimeMillis();
System.out.println("復制成功!");
System.out.println("共耗時" + (e-s)+"毫秒");
}
第七章:字節(jié)緩沖流
7.1-概述(了解)
緩沖流:針對基礎(chǔ)流對象進行高效處理的流對象肢专。或者為基礎(chǔ)流增加功能嚷往。
字節(jié)緩沖流:BufferedInputStream
,BufferedOutputStream
緩沖流的基本原理柠衅,是在創(chuàng)建流對象時皮仁,會創(chuàng)建一個內(nèi)置的默認大小的緩沖區(qū)數(shù)組,通過緩沖區(qū)讀寫,減少系統(tǒng)IO次數(shù)贷祈,從而提高讀寫的效率趋急。
7.2-字節(jié)緩沖流的使用(重要)
BufferedOutputStream繼承OutputStream,write()方法不必從新學習势誊。
BufferedInputStream繼承InputStream呜达,read()方法不必從新學習。
構(gòu)造方法
-
public BufferedInputStream(InputStream in)
:創(chuàng)建一個 新的緩沖輸入流粟耻。 -
public BufferedOutputStream(OutputStream out)
: 創(chuàng)建一個新的緩沖輸出流查近。
// 創(chuàng)建字節(jié)緩沖輸入流
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("bis.txt"));
// 創(chuàng)建字節(jié)緩沖輸出流
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("bos.txt"));
注意:在使用緩沖流時,必須傳遞基礎(chǔ)流挤忙。
效率測試
查詢API霜威,緩沖流讀寫方法與基本的流是一致的,我們通過復制大文件(375MB)册烈,測試它的效率戈泼。
基礎(chǔ)流:
public static void main(String[] args) throws IOException {
// 記錄開始時間
long start = System.currentTimeMillis();
// 創(chuàng)建流對象
FileInputStream fis = new FileInputStream("jdk8.exe");
FileOutputStream fos = new FileOutputStream("copy.exe")
// 讀寫數(shù)據(jù)
int b = 0;
while ((b = fis.read()) != -1) {
fos.write(b);
}
// 記錄結(jié)束時間
long end = System.currentTimeMillis();
System.out.println("普通流復制時間:"+(end - start)+" 毫秒");
}
// 基礎(chǔ)流復制時間:1分鐘以上
緩沖流
public static void main(String[] args) throws FileNotFoundException {
// 記錄開始時間
long start = System.currentTimeMillis();
// 創(chuàng)建流對象
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("jdk8.exe"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy.exe"));
// 讀寫數(shù)據(jù)
int len = 0;
while ((len = bis.read()) != -1) {
bos.write(len);
}
// 記錄結(jié)束時間
long end = System.currentTimeMillis();
System.out.println("緩沖流使用數(shù)組復制時間:"+(end - start)+" 毫秒");
}
// 緩沖流復制時間:6969 毫秒,約7秒鐘
緩沖流+字節(jié)數(shù)組
public static void main(String[] args) throws IOException {
// 記錄開始時間
long start = System.currentTimeMillis();
// 創(chuàng)建流對象
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"));
// 讀寫數(shù)據(jù)
int len = 0;
byte[]b = new byte[1024];
while ((len = bis.read(b)) != -1) {
bos.write(b,0,len);
}
// 記錄結(jié)束時間
long end = System.currentTimeMillis();
System.out.println("緩沖流使用數(shù)組復制時間:"+(end - start)+" 毫秒");
}
// 緩沖流使用數(shù)組復制時間:796 毫秒,約0.7秒