File&&位元組流
File類
是什麼?
是檔案和目錄路徑名的抽象表達形式 ,Java中把檔案或者目錄(資料夾)都封裝成File物件。建立File類物件時,系統並不會去驗證此檔案或資料夾是否存在。可以理解為只是一個字串(抽象路徑)
注意事項:
- 建立時:File類本身不會去檢查檔名是否存在
- 操作的時候,會檢查檔案是否存在。
- 用\\表示層級
- 建立物件多用絕對路徑
構造方法:
都是為了建立一個抽象路徑
查API文件即可
成員方法:
查API文件即可
建立:
createNewFile()在指定位置建立一個空檔案,成功就返回true,如果已存在就不建立,然後返回false。
mkdir() 在指定位置建立一個單級資料夾。
mkdirs() 在指定位置建立一個多級資料夾。
renameTo(File dest)如果目標檔案與原始檔是在同一個路徑下,那麼renameTo的作用是重新命名, 如果目標檔案與原始檔不是在同一個路徑下,那麼renameTo的作用就是剪下,而且還不能操作資料夾。刪除:
delete() 刪除檔案或者一個空資料夾,不能刪除非空資料夾,馬上刪除檔案,返回一個布林值。
deleteOnExit()jvm退出時刪除檔案或者資料夾,用於刪除臨時檔案,無返回值。
判斷:
exists() 檔案或資料夾是否存在。
isFile() 是否是一個檔案,如果不存在,則始終為false。
isDirectory() 是否是一個目錄,如果不存在,則始終為false。
isHidden() 是否是一個隱藏的檔案或是否是隱藏的目錄。
isAbsolute() 測試此抽象路徑名是否為絕對路徑名。
獲取:
getName() 獲取檔案或資料夾的名稱,不包含上級路徑。
getAbsolutePath()獲取檔案的絕對路徑,與檔案是否存在沒關係
length() 獲取檔案的大小(位元組數),如果檔案不存在則返回0L,如果是資料夾也返回0L。
getParent() 返回此抽象路徑名父目錄的路徑名字串;如果此路徑名沒有指定父目錄,則返回null。
lastModified()獲取最後一次被修改的時間。資料夾相關:
static File[] listRoots()列出所有的根目錄(Window中就是所有系統的碟符)
list() 返回目錄下的檔案或者目錄名,包含隱藏檔案。對於檔案這樣操作會返回null。
listFiles() 返回目錄下的檔案或者目錄物件(File類例項),包含隱藏檔案。對於檔案這樣操作會返回null。
list(FilenameFilter filter)返回指定當前目錄中符合過濾條件的子檔案或子目錄。對於檔案這樣操作會返回null。
listFiles(FilenameFilter filter)返回指定當前目錄中符合過濾條件的子檔案或子目錄。對於檔案這樣操作會返回null。
注意事項:
-
單極目錄,可以用mkdir方法建立資料夾。呼叫mkdirs方法的file物件可以是檔案,也可以是資料夾
-
建立多級目錄,呼叫mkdirs方法的file物件不能是檔案,必須是資料夾
高階獲取功能
-
public String[] list() 返回一個字串陣列,這些字串指向此抽象的路徑名錶示的目錄中的所有檔案
和資料夾的名字如果該路徑表示的是檔案,會返回null
如果路徑表示的資料夾沒有讀取的許可權,也會返回null -
File[] listFiles(FileFilter filter) 獲取這個資料夾下,滿足filter過濾器的條件的檔案
public interface FileFilter 過濾器是個介面,不能直接例項化,需要重寫方法 boolean accept(File pathname)
裡面的物件是以絕對路徑儲存的,只能獲取一層的。
過濾器建立和使用步驟:
方法1:通過介面實現類來建立
- 先建立一個FileFilter介面的實現子類
- 重寫accept方法
- 此過濾器在listFiles方法中,會將該資料夾中遍歷的每個File物件作為pathname。作為accept方法的形參。
- 接下來就是根據你的需求去重寫具體的方法體
- 滿足你的過濾條件就返回true,反之。
- 重寫完成,在呼叫listFiles方法時候,就會將滿足過濾器(return true)的File物件放入,File陣列中。
方法2:通過匿名類實現上例子:
File[] listF = derectory.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
boolean flag = false;
if (pathname.isFile()) {
String fileName = pathname.getName();
flag = fileName.startsWith("123");
}
return flag;
}
});
小試牛刀:
list()方法配合delete 實現遞迴刪除資料夾
思路:
- 獲取目錄的下的所有File物件(包括檔案和資料夾)
- 判斷,如果是一個空目錄或者file物件不是一個目錄而是檔案
- 直接刪除
- 程式執行到這裡,那麼一定是一個目錄,且不是空目錄
- 遍歷獲取的file陣列
- 如果這個file物件仍然是一個目錄,遞迴刪除該目錄
- 如果這個file物件是檔案,直接刪除
- 最後不要忘記刪除已經是空目錄的當前目錄
File[] files = dir.listFiles();
if(files == null || files.length == 0){
dir.delete();
return;
}
for(int i = 0; i < files.length; i++){
if(files(i).isDirectory){
delete(files[i]);
}else{
files[i].delete();
}
dir.delete();
}
把檔案a移動到資料夾b中
a.renameTo(new File(b.getPath() + "/" a.getName()))
IO流
原則:先建立的流後關閉,後建立的流先關閉,否則可能會報錯
注意事項:
流操作中的 Read()方法是阻塞方法!一個阻塞式的方法,也就是死皮賴臉型的。我向你要資料,你說什麼?沒有!沒有我就不走了,我就在這等著,其他人也給我一邊候著,只有等我要到了東西才有你們說話的份。 所以每當程式執行至temp = Input.read(data); 都會等待,直到InputStream中有資料為止,它就會馬上據為己有。真是個心機婊。
為啥要叫IO流:
IO流用來處理裝置之間(記憶體、硬碟、網路∙∙∙)的資料傳輸
Java對資料的操作是通過流的方式
Java用於操作流的物件都在IO包中(input/output)
IO操作:
- Output:把記憶體中的資料儲存到持久化裝置上這個動作稱為輸出(寫)Output操
- Input:把持久裝置上的資料讀取到記憶體中的這個動作稱為輸入(讀)Input操作
特點:
如水流一樣,單向,不可再回(Java有些流可以回但也有限制)
IO流分類(部分):
按照資料流向劃分 (站在記憶體、程式的角度)
輸入流
從硬碟讀入資料到記憶體
輸出流
從記憶體寫出資料到硬碟
按照資料型別劃分(一般都是按此劃分)
位元組流
按位元組進行讀取(可以處理任意檔案,任意型別的資料),把位元組原封不動
地一個個進行搬運傳輸過去,弊端是可能出現亂碼
字元流
相當於位元組流 + 編碼表
什麼情況下使用哪種流呢?
不清楚用哪個就用位元組流,清楚就用字元流。
FileInputStream位元組輸入流
寫入資料的步驟:
-
建立位元組輸出流物件
建立之前,jvm到作業系統上去找我們指定的這個檔案沒有找到,會幫我們建立
找到了,會先清空裡面內容 -
write方法寫資料
-
關閉資源
FileputStream位元組輸入流
FileOutputStream位元組輸出流
讀出資料的步驟:
-
建立位元組輸入流物件
建立之前,jvm到作業系統上去找我們指定的這個檔案沒有找到,會幫我們建立
找到了,會先清空裡面內容 -
read方法寫資料
-
關閉資源
檔案讀寫異常處理方式
方式一:
try catch finally
- try外面建立字元輸出流
- try裡面讀寫
- finally 字元輸出流釋放
方式二:
try-with-resources
try(建立字元輸出流){}
catch{}
finally{}
該種方式,try種的流物件會在finally執行前結束
在 try-with-resources 定義子句中建立的物件(在括號內的物件)必須實現 java.lang.AutoCloseable 介面,這個介面有一個方法:close()。
當 try() 括號裡面的 IO 出現異常或者執行結束,會自動呼叫 close() 關閉物件。
private native void close0() throws IOException; static { initIDs(); }
疑問:對檔案讀寫的整個流程的步驟?檔案的讀寫是不是都要以一個數組作為中間去讀出,和寫入?
不用,可以以int型資料,或者byte陣列來接收。看自己選擇。
具體:
- 首先建立輸入流,和輸出流
- 選擇中間暫存的資料的方式是單個位元組還是以位元組陣列來儲存
- 具體如下
// 單個位元組的讀出寫入
int readData;
long start = System.currentTimeMillis();
while ((readData = in.read()) != -1){
out1.write(readData);
}
long end = System.currentTimeMillis();
System.out.println("耗時" + (end - start));
// 多個位元組的讀入寫出
int readCount;
byte[] bytes = new byte[1024];
long start2 = System.currentTimeMillis();
while ((readCount = in.read(bytes)) != -1){
out2.write(bytes, 0, readCount);
}
long end2 = System.currentTimeMillis();
System.out.println("耗時" + (end2 - start2));
in.close();
out1.close();
out2.close();
緩衝xxx流:
構造方法和成員方法什麼的看API
問題:指定緩衝區大小有什麼作用?位元組緩衝流是如何讀取或寫入資料的?
硬碟和jvm記憶體中間有一個埠作為緩衝區
答:
-
位元組緩衝流首先從因硬碟直接預設讀出8kb到緩衝區。
-
然後再記憶體中的位元組陣列去接收
-
通過調節位元組陣列的大小能夠提高從埠到記憶體的傳輸速度
硬碟到埠一次可以傳8kb,埠到記憶體一次一個位元組
用緩衝xx流讀寫資料步驟
- 建立緩衝輸入流 (輸入流也一併建立)
- 建立緩衝輸出流 (輸出流一併建立)
- 選擇按位元組或者位元組陣列儲存和輸入
- 關閉
為什麼這麼快?緩衝位元組流一定比普通的位元組流更快嗎?
bufferIO流操作再計算機中的大致流程:
流程:
以file為輸⼊流建立物件,將file⽂件中8kb全部先存⼊已經開闢好的預設8kb快取空間,然後in快取輸⼊流調⽤read⽅法,緩衝區的按⾃⼰想要的⼤⼩讀取出來放⼊⼀個位元組數 組中,(是否最⼤為8162byte?)然後以⽬標⽂件建立的快取輸出流物件調⽤write ⽅ 法,講位元組陣列中的資料寫⼊到緩衝區,當緩衝區寫⼊到8kb,以後緩衝區中的數 據, ⼀次性交給系統記憶體。重複以上操作直⾄全部寫⼊⽬標⽂件。兩個輸⼊流物件釋 放。 相對於之前的普通流的操作,程式與系統記憶體之間的通訊次數減少。效率增⾼。
問: 如果普通輸⼊流 以超⼤字元陣列(⼤於8kb)做緩衝呢?是否能⽐緩衝流更快
答:經過測試發現,有時候普通輸⼊流更快。所謂做緩衝,就相當於是普通輸⼊流以⽐ 較⼤的字元陣列做緩衝差不多的效果。底層同樣是⼀次性就讀出了很多。甚⾄經過測試,同樣⼤⼩的陣列作為中間緩衝。普通輸⼊流都更快。綜上所述,我暫時認定, buffer字元流只是為了讓緩衝區預設就有⼀個很⼤的空間。⽽且中間暫存資料的陣列, 由於和緩衝區都在jvm記憶體中。相互之間讀寫速度⾮常快,以⾄於,在⽤buffer流操作中 那暫存資料的陣列⼤⼩的改變不太影響整個程式的速度。(因為只有緩衝區滿了才進⾏ jvm記憶體和外部裝置的互動)
終上:
讀寫速度起決定作⽤的是緩衝區的⼤⼩。
⼀般位元組流:以暫存資料位元組陣列作為緩衝區
緩衝位元組流:預設開闢了緩衝區(8162byte) 另外緩衝位元組流中⽤於兩個緩衝區互動的位元組陣列⼤⼩對程式的影響不⼤。
buffer或許在緩衝區這塊封裝了普通流,讓其有了預設緩衝區,不用再搞一個巨大的暫存位元組陣列。底層執行的原理還是差不多的。為了減少jvm與系統的互動。
注意點:
flush():close():有啥用? 為什麼要用?
可以沒有flush但是必須要有close, 不然緩衝區中的資料無法重新整理到硬碟中
-
flush方法,將包裝流中的資料重新整理到節點流中
-
close方法,會自動呼叫flush方法,並且將節點流的close也關閉
細枝末節:
-
讀入不用flush
-
如何實現資料的追加寫入?
FileOutputStream(File file, boolean append)
建立檔案輸出流以寫入由指定的
File`物件表示的檔案。