Java.io package
通過數據流,序列化和文件系統(tǒng)提供系統(tǒng)輸入和輸出超营。
File
概述
一種文件或目錄的路徑名抽象表示鸳玩。
用戶界面和操作系統(tǒng)使用以來系統(tǒng)的路徑字符串來命名文件和目錄。
File類表達一種與系統(tǒng)無關的分層路徑名視圖抽象演闭,這種抽象主要作用是以不依賴操作系統(tǒng)的方式處理很多文件和路徑名依賴操作系統(tǒng)的復雜問題不跟。這種抽象路徑由兩部分組件:
- 一個可選的依賴系統(tǒng)的首字符串,例如磁盤驅動說明符米碰,"/"Unix根目錄窝革,"\\"表示Windows UNC路徑名;
- 一個空的或者多個"name"的序列吕座。
在抽象路徑中
name
可以表示以下幾種含義:第一個name
可能是目錄名虐译,或者是hostname(Windows UNC的一部分);后續(xù)的每一個name
(除了最后一個)都表示目錄名吴趴;最后一個name
表示目錄名或者文件名漆诽。
路徑分類
無論是字符串形式,還是File的抽象表示,路徑總是分為絕對路徑和相對路徑拴泌。
絕對路徑名是完整的魏身,包含文件名和它的完整路徑以及磁盤驅動說明符,是依賴操作系統(tǒng)的蚪腐。不需要其他信息來定位它所表示的文件箭昵。
而相對路徑必須根據從其他路徑獲取的信息來解釋。默認情況java.io包下的類都是一句當前用戶目錄來解決相對路徑回季。這個目錄時根據系統(tǒng)屬性user.dir來命名家制,而且通常是JVM被調用的目錄(執(zhí)行java className命令的目錄就是當前目錄)。
使用絕對路徑不利于代碼平臺移植泡一,所以盡量使用相對路徑颤殴。相對路徑目錄分隔符是斜杠(/)。
路徑中的首字符串
這個prefix概念是依賴系統(tǒng)的鼻忠,例如表示Unix根目錄涵但,Windows的磁盤驅動器說明符和根目錄...
在Unix系統(tǒng)中,"/"表示跟目錄帖蔓;在Windows中矮瘟,磁盤字母+:
形式,如果是絕對路徑塑娇,后面還會跟"\"澈侠。
路徑字符串中的符號
分隔符
依賴系統(tǒng)的路徑字符串和File路徑抽象表示之間的轉換都涉及到分隔符。其作用是將路徑中各name分隔埋酬,形成分層哨啃。不同操作系統(tǒng)的分隔符都是不一樣的。
依賴系統(tǒng)特定的分隔符可以通過給System.getProperty()
傳入關鍵詞file.separator獲取(更多關鍵詞可以去查看文檔)写妥。
除了目錄分隔符還有路徑分隔符和行分隔符拳球,它們都是依賴系統(tǒng)的(關鍵詞分別是:path.separator和line.separator)。
其他符號
符號 | 描述 |
---|---|
"." | 表示當前目錄 |
".." | 表示上一層目錄 |
"../../" | 表示上一層目錄的上一層目錄 |
"/" | 表示根目錄 |
"~/" | 表示用戶目錄的根目錄(表示當前虛擬目錄下) |
注意表格中使用的分隔符(/)可以替換成任一系統(tǒng)特定的分隔符耳标。
參考CSDN問題貼醇坝、絕對路徑和相對路徑邑跪、當前路徑和上級目錄的表示方法
FileSystem Hierarchy
FileSystem Hierarchy的主要作用是規(guī)定了操作系統(tǒng)各層次目錄的作用次坡。
好處在于軟件可以預測已安裝文件和文件夾的位置;用戶可以預測已安裝文件和文件夾的位置画畅。這就好比MacOS目錄有/Library,/Application,/Users...一個Android項目也有相應的路徑分別存放Java代碼和資源文件砸琅。
詳細規(guī)定可以看文件系統(tǒng)層次結構。
empty abstract name
文檔中介紹File的一種抽象形式empty abstract name轴踱,它不包含任何prefix和name sequence症脂。那么表示哪個文件或目錄呢?
public class TestPathCharacter {
public static void main(String[] args) {
File file = new File("");
if(!file.exists()) {
System.out.println("no such file or directory");
//打印了,說明無法定位到指定文件或目錄
}
//猜想表示當前用戶目錄
//File f = new File(file, "/TestPathString.java"); //執(zhí)行后打印異常诱篷,無法找到相應文件
File f = new File("./", "TestPathString.java");//執(zhí)行后正常打印壶唤,證明不需要使用絕對路徑
if(!f.exists()) {
System.out.println("no such file or directory");
}
Scanner input = null;
try {
input = new Scanner(f);
String line = null;
while(input.hasNextLine()) {
line = input.nextLine();
System.out.println(line);
}
}catch(IOException e) {
e.printStackTrace();
}finally{
input.close();
}
}
}
執(zhí)行結果可以看注釋,證明不表示任何文件或目錄棕所。
Path文檔中empty path定位到的是file system默認的目錄闸盔。
與java.nio.file互通性
java.nio.file包定義了讓JVM訪問文件,文件屬性和文件系統(tǒng)的接口和類琳省。這些API克服了File類的限制迎吵。調用一個File對象的toPath()可以得到一個Path去定位相應文件。返回得到的Path配合Files使用针贬,提供了更高效和更廣泛的額外文件操作击费,文件屬性和有利于診斷文件操作時錯誤的I/O異常的訪問。
Path是一個可以用于在file system中定位文件得到類桦他。它一般代表依賴系統(tǒng)的文件路徑蔫巩。一個Path對象表示一個分層的,由被系統(tǒng)特定的分隔符分隔的目錄和文件名元素的序列組成快压。也有可能會包含一個標示著系統(tǒng)層次的根組件批幌。
FileSystem是一個提供file system的接口,同時是對象訪問file system中的文件和其他對象的工廠嗓节。通過FileSystems.getFileSystem()文檔介紹荧缘,自己對file system初步理解:
This method iterates over the installed providers to locate the provider that is identified by the URI scheme of the given URI.
給一個對象提供外部訪問接口,也就是提供程序拦宣〗卮郑可以比作Android系統(tǒng)中為了安全考慮程序之間數據共享使用ContentProvider,根據特定的uri去定位文件鸵隧。而這個ContentProvider就是該程序的file system绸罗。
java.nio.file包中還提供了幾個名稱類似的類,它們的對應關系:
類名 | 描述 |
---|---|
Path | 一個可以用于在file system中定位文件得到類豆瘫。它一般代表依賴系統(tǒng)的文件路徑珊蟀。 |
Paths | 只包含通過轉換路徑字符串或URI返回Path的靜態(tài)方法 |
FileSystem | 一個提供file system的接口 |
FileSystems | 是file system的工廠方法,用于獲取或者構建file system |
Files | 只包含對文件外驱,目錄或其他類型文件操作的靜態(tài)方法 |
FileSystems.getDefault()返回的file system是指JVM可用的育灸。而工作目錄就是當前用戶目錄,被系統(tǒng)屬性命名(System.getProperty(user.dir))昵宇,這就允許了和java.io.File的互通性磅崭。
API理解
大部分File的方法直接看文檔就可以,一下幾個自己理解有誤瓦哎,實驗了一下砸喻。
- mkdir()&mkdirs()柔逼,兩者都是創(chuàng)建目錄,注意不會創(chuàng)建文件割岛。但是使用mkdir()時愉适,File對象指示的路徑中有不存在的目錄,導致創(chuàng)建失敗癣漆。此時可以用mkdirs()儡毕。
- getParent(),返回File對象的父目錄字符串扑媚。
- list()&listFiles()&listRoots()腰湾,list()和listFiles()都是返回當前目錄下所有文件和目錄,前者返回字符串數組疆股,后者返回File數組费坊;listRoots()返回系統(tǒng)可用的系統(tǒng)盤。
- getPath()&getAbsolutePath()&getCanonicalPath()旬痹,getPath()返回構建File對象時路徑的字符串形式(完全不改動)附井;getAbsolutePath()返回File對象指示的絕對路徑;getCanonicalPath()返回File對象的絕對路徑两残,但是不包括路徑中的符號(除了分隔符)永毅。
FilenameFilter,用于過濾文件人弓。
RandomAccessFile
文檔概述
RandomAccessFile實例支持隨機訪問文件沼死。隨機訪問文件的行為類似存儲在file system中大字節(jié)數組。該隱含的數組中有一種光標或索引崔赌,稱為文件指針意蛀。讀取操作是在指針處讀取開始讀取字節(jié),并且推進指針至下一個字節(jié)健芭。如果使用"rw"模式構建的對象县钥,寫操作一樣可用。寫入操作過程和讀取類似慈迈。一旦寫入數據超過隱含的數組長度后若贮,數組被擴展。文件指針可以被getFilePointer()
獲取當前位置痒留,通過seek()
修改位置谴麦。
作用
java.io包下的其它流都是只讀或只寫。而且它們的外部文件都是順序的狭瞎,意思是讀操作時從文首到文末细移、寫操作時覆蓋或者從文末追加搏予,無法更新文件(可以刪除重新創(chuàng)建)熊锭。而RandomAccessFile可以做到。
二進制IO中的字符與字符串
DataOutput
該接口規(guī)定了從任何Java基本類型數據到一系列字節(jié)的轉換,然后將這些字節(jié)寫入二進制流中碗殷。同時也提供字符串對象轉換成改進版UTF-8格式精绎,并且將這些字節(jié)寫出。
DataInput定義了讀取基本數據類型和字符串的方法锌妻。
- writeChar(int)代乃,將UTF-16字節(jié)寫入輸出流中;
- writeChars(String)仿粹,將字符串中所有字符的UTF-16字節(jié)寫入輸出流中搁吓;
- writeBytes(String),將字符串中所有字符的UTF-16低字節(jié)寫入輸出流吭历,高字節(jié)拋棄堕仔。該方法適用于純ASCII碼字符組成的字符串。
- writeUTF(String)晌区,將字符串中字符轉換成改進版UTF-8格式摩骨,然后前兩個字節(jié)存儲轉換后的字節(jié)數,后面接上轉換后的字節(jié)系列朗若。
改進版UTF-8編碼規(guī)則
改進版的UTF-8方案采用一個字節(jié)恼五,二個字節(jié)或三個字節(jié)來存儲字符。
- 字符Unicode編碼小于或等于0x7F哭懈,該字符編碼大小為一個字節(jié)灾馒,且首位為0標示為一個字節(jié)存儲;
- 字符Unicode編碼大于0x7F且小于或等于0x7FF遣总,該字符編碼大小為二個字節(jié)你虹,且前三位是110標示為兩個字節(jié)中第一位;
- 字符unicode編碼大于0x7FF彤避,該字符編碼大小為三個字節(jié)傅物,且前四位是1110標示為三個字節(jié)中第一個。
實戰(zhàn)
public class TestDataOutput {
public static void main(String[] args) {
String s = "我是\uD835\uDD46";
String result = null;
System.out.println(s);
System.out.println("字符串字符個數: " + s.length());
System.out.println("字符串code point個數: " + s.codePointCount(0, s.length()));
RandomAccessFile file = null;
byte[] b = null;
try {
try {
file = new RandomAccessFile("../file/TestUTF8Stream.txt", "rw");
file.setLength(0);
file.writeUTF(s);
int count = (int)(file.length());
System.out.println("隨機訪問文件字節(jié)數有:" + count + "個.");
b = new byte[count];
file.seek(0);
file.read(b, 0, count);
for(byte i : b) {
System.out.println(i & 0xff);
}
file.seek(0);
result = file.readUTF();
System.out.println(result);
}finally {
file.close();
}
}catch(FileNotFoundException e) {
e.printStackTrace();
}catch(IOException e) {
e.printStackTrace();
}
}
}
Console打印輸出:
上圖中打印了字符串琉预,字符串代碼單元數量以及代碼點數量董饰,寫入文件后轉換的字節(jié)系列個數,分別打印該系列圆米,最后使用readUTF()
讀取寫入文件的字符串卒暂。
這里遇到三個誤區(qū):
第一,當寫完數據后娄帖,直接從文件中獲取所有字節(jié)系列也祠,是無法獲取到的。這是因為此時文本指針在文本末尾近速。同理诈嘿,使用readUTF()
前也需要調用seek(0)
堪旧。
第二,當文件關閉后對文件重新寫入數據(程序重啟)奖亚,會覆蓋文本內的內容淳梦。這里需要注意了如果第二次輸入的數據字節(jié)長度小于第一次,只會覆蓋第二次輸入數量的字節(jié)昔字,剩下的會保留爆袍。為了避免誤操作,可以在每一次寫入時調用setLength(0)
清空作郭。
第三陨囊,使用RandomAccessFile構造函數實例化對象,如果給定的是文件路徑字符串或者File夹攒,沒有該文件自動創(chuàng)建谆扎。但是給定的是包含未創(chuàng)建的目錄或者目錄路徑字符串或者File,文件/目錄不會自動創(chuàng)建芹助,并且拋出FileNotFounException異常堂湖。
RandomAccessFile中讀寫操作調用需要對應起來。也就是在文本寫入時状土,哪里調用了
writeUTF()
就在讀取過程中的相應順序調用readUTF()
无蜂。
只支持文件,且沒有只寫模式
RandomAccessFile類只能夠為文件創(chuàng)建訪問流蒙谓,目錄等其他類型對象不可以斥季。
RandomAccessFile構造器有兩個:
- RandomAccessFile(File file, String mode)
- RandomAccessFile(String name, String mode)
都會拋出FileNotFoundException異常,原因有以下幾種:
- 如果模式是"r"累驮,給定的文件不是固定存在酣倾,會拋出。
- 如果模式是"rw"開頭谤专,給定的文件不是一個存在且可寫的固定文件躁锡,若不存在又無法創(chuàng)建或者創(chuàng)建,打開文件時出現錯誤置侍,會拋出映之。
File類可以抽象的表示文件或者目錄路徑,操作時無需處理系統(tǒng)依賴問題蜡坊。FileSystem是file system接口杠输,且提供了對象訪問file system中文件或其他對象方法(不僅僅只有文件和目錄)。而Path則是FileSystem中定位文件或其他對象的路徑秕衙,與系統(tǒng)相關蠢甲。
模式
RandomAccessFile模式常用的有兩種:
- "r",只讀模式据忘。如果使用該模式實例去調用任何write重載方法會拋出IOException異常鹦牛。
- "rw"搞糕,讀寫模式。如果文件不存在能岩,會去嘗試創(chuàng)建一個新文件寞宫。
還有兩種模式萧福,看文檔不是很理解:
- "rws"
- "rwd"
同樣是支持讀寫操作拉鹃,還有別的作用,不太理解鲫忍。
注意不支持只寫模式膏燕。
方法
RandomAccessFile的方法主要是向文本記錄數據。每一條數據大小不必相等悟民,但是它們大小和位置必須可知坝辫。這樣做的目的,一方面為了后期讀取時射亏,必須按照寫入時順序調用相應方法近忙;另一方面可以通過seek()方法準確定位到想要的數據。所以智润,她只能操作文件及舍。
RandomAccessFile雖然是二進制流,但是和InputStream和OutputStream沒有繼承關系窟绷。它直接繼承Object锯玛,實現DataOutput和DataInput接口的方法。除了這兩個接口的讀寫方法兼蜈,還實現了特有方法攘残。
- getFilePointer(),返回從文本頭部到指針處(下一個可讀字節(jié))字節(jié)偏移量为狸。
- seek()歼郭,設置文件指針的偏移量,到下一個可讀或寫的字節(jié)辐棒。偏移量從文首開始計算实撒。偏移量可以超出文末,但是不會改變文本長度屬性涉瘾。
- length()知态,返回文本長度,即字節(jié)數立叛。
- setLength()负敏,設置文本長度,一般傳入0可以用于清空文本秘蛇。
效率
RandomAccessFile的讀寫方法都有native關鍵詞其做。說明每調用一次讀或寫方法顶考,都需要與磁盤進行一次I/O操作。如果讀取大文件妖泄,效率非常低驹沿。
在文檔中介紹RandomAccessFile行為好比內存中有一個巨大數組存儲文件字節(jié),其實并沒有蹈胡。
在java.io包中解決辦法是給流添加一個緩沖區(qū)渊季,減少與磁盤I/O操作次數,從而提高效率罚渐。例如BufferedInputStream却汉。
但是這樣沒有了RandomAccessFile的特性-隨機訪問文件『刹ⅲ可以使用jdk 1.4 nio中的內存映射替換RandomAccessFile合砂,或者擴展RandomAccessFile實現帶有Buffer的RandomAccessFile。
內存映射文件概念:由于文件過大翩伪,無法直接放入內存中進行I/O操作,所以一般都是通過緩沖區(qū)來提高效率谈息。而有了內存映射概念缘屹,可以認為通過一次與磁盤的I/O操作,把文件放入內存黎茎,存儲在一個數組中進行訪問囊颅,大大提高了效率。
問題
boolean類型數據占幾個字節(jié)傅瞻,值是多少踢代?
boolean類型數據占一個字節(jié),true的字節(jié)值為1嗅骄,false的字節(jié)值為0胳挎。