Java I/O 學習(一)

Java.io package

通過數據流,序列化和文件系統(tǒng)提供系統(tǒng)輸入和輸出超营。

File

概述

一種文件或目錄的路徑名抽象表示鸳玩。

用戶界面和操作系統(tǒng)使用以來系統(tǒng)的路徑字符串來命名文件和目錄。

File類表達一種與系統(tǒng)無關的分層路徑名視圖抽象演闭,這種抽象主要作用是以不依賴操作系統(tǒng)的方式處理很多文件和路徑名依賴操作系統(tǒng)的復雜問題不跟。這種抽象路徑由兩部分組件:

  1. 一個可選的依賴系統(tǒng)的首字符串,例如磁盤驅動說明符米碰,"/"Unix根目錄窝革,"\\"表示Windows UNC路徑名;
  2. 一個空的或者多個"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é)來存儲字符。

  1. 字符Unicode編碼小于或等于0x7F哭懈,該字符編碼大小為一個字節(jié)灾馒,且首位為0標示為一個字節(jié)存儲;
  2. 字符Unicode編碼大于0x7F且小于或等于0x7FF遣总,該字符編碼大小為二個字節(jié)你虹,且前三位是110標示為兩個字節(jié)中第一位;
  3. 字符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打印輸出:

屏幕快照 2017-02-12 下午5.57.24.png

上圖中打印了字符串琉预,字符串代碼單元數量以及代碼點數量董饰,寫入文件后轉換的字節(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異常,原因有以下幾種:

  1. 如果模式是"r"累驮,給定的文件不是固定存在酣倾,會拋出。
  2. 如果模式是"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。

可以參考花1K內存實現高效I/O的RandomAccessFile類源织。

內存映射文件概念:由于文件過大翩伪,無法直接放入內存中進行I/O操作,所以一般都是通過緩沖區(qū)來提高效率谈息。而有了內存映射概念缘屹,可以認為通過一次與磁盤的I/O操作,把文件放入內存黎茎,存儲在一個數組中進行訪問囊颅,大大提高了效率。

問題

boolean類型數據占幾個字節(jié)傅瞻,值是多少踢代?

boolean類型數據占一個字節(jié),true的字節(jié)值為1嗅骄,false的字節(jié)值為0胳挎。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市溺森,隨后出現的幾起案子慕爬,更是在濱河造成了極大的恐慌,老刑警劉巖屏积,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件医窿,死亡現場離奇詭異,居然都是意外死亡炊林,警方通過查閱死者的電腦和手機姥卢,發(fā)現死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人独榴,你說我怎么就攤上這事僧叉。” “怎么了棺榔?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵瓶堕,是天一觀的道長。 經常有香客問我症歇,道長郎笆,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任当船,我火速辦了婚禮题画,結果婚禮上默辨,老公的妹妹穿的比我還像新娘德频。我一直安慰自己,他們只是感情好缩幸,可當我...
    茶點故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布壹置。 她就那樣靜靜地躺著,像睡著了一般表谊。 火紅的嫁衣襯著肌膚如雪钞护。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天爆办,我揣著相機與錄音难咕,去河邊找鬼。 笑死距辆,一個胖子當著我的面吹牛余佃,可吹牛的內容都是我干的。 我是一名探鬼主播跨算,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼爆土,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了诸蚕?” 一聲冷哼從身側響起步势,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎背犯,沒想到半個月后坏瘩,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡漠魏,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年倔矾,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蛉幸。...
    茶點故事閱讀 39,727評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡破讨,死狀恐怖丛晦,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情提陶,我是刑警寧澤烫沙,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站隙笆,受9級特大地震影響锌蓄,放射性物質發(fā)生泄漏。R本人自食惡果不足惜撑柔,卻給世界環(huán)境...
    茶點故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一瘸爽、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧铅忿,春花似錦剪决、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至峻凫,卻和暖如春渗鬼,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背荧琼。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工譬胎, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人命锄。 一個月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓堰乔,卻偏偏與公主長得像,于是被迫代替她去往敵國和親累舷。 傳聞我的和親對象是個殘疾皇子浩考,可洞房花燭夜當晚...
    茶點故事閱讀 44,619評論 2 354

推薦閱讀更多精彩內容

  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現被盈,斷路器析孽,智...
    卡卡羅2017閱讀 134,652評論 18 139
  • 1. Java基礎部分 基礎部分的順序:基本語法,類相關的語法只怎,內部類的語法袜瞬,繼承相關的語法,異常的語法身堡,線程的語...
    子非魚_t_閱讀 31,623評論 18 399
  • 1 IONo18 1.1IO框架 【 IO:Input Output 在程序運行的過程中邓尤,可能需要對一些設備進...
    征程_Journey閱讀 960評論 0 1
  • Ubuntu的發(fā)音 Ubuntu,源于非洲祖魯人和科薩人的語言澈魄,發(fā)作 oo-boon-too 的音景鼠。了解發(fā)音是有意...
    螢火蟲de夢閱讀 99,257評論 9 467
  • linux資料總章2.1 1.0寫的不好抱歉 但是2.0已經改了很多 但是錯誤還是無法避免 以后資料會慢慢更新 大...
    數據革命閱讀 12,159評論 2 33