File類:使用Java進行操作文件,通過一些方法進行操作。比如創建文件,刪除文件,判斷是否存在,文件大小,文件的目錄等等,還有文件夾的一些操作。
IO流:根據類別可以進行分類。
按照流向:輸入流Input 輸出流Output
按照字節個數:字節流和字符流
字節流:
InputStream:抽象類,無法直接使用,通過其子類FileInputStream,從文件中獲取字節。
OutputStream:抽象類,無法直接使用,通過其子類FileOutputStream,向文件寫入字節。
字符流:
Reader:抽象類,無法直接使用,通過其子類的子類FileReader從文件中獲取字符。
Writer:抽象類,無法直接使用,通過其子類的子類FileWriter向文件寫入字符。
以上通過其子類的形式從文件中讀取數據或向文件中寫入數據,這種形式太局限。
比如,FileInputStream和FileOutputStream是操縱字節數據,當操縱字符數據時會有報錯的可能,而且每次寫入輸出只是單個,所以並不能拿出來單獨使用;FileReader和FileWriter可以操縱字符數據,且文件的編碼應該和Java所定的編碼匹配,當遇到編碼不符的情況,則會亂碼。
所以我們不推薦這四種單一的形式。那么,我們如何使得文件中的字符數據順利讀入,Java中的字符數據也可順利輸出到文件呢?
針對上面的問題,我們使用了另外兩種類,即InputStreamReader和OutputStreamWriter,這兩種是Reader和Writer的子類,也是FIleReader和FileWriter 的父類。它們是字節到字符和字符到字節的橋梁,即可以將文件中的帶有字符的字節數據順利讀入到Java中,將Java中的數據成功寫入到文件中。在使用的過程中,我們需要指定編碼,即文件和Java中指定的編碼相匹配,這樣編碼相同了,字符也可以和字節互轉了,我們可以成功的操縱數據了。
以上FileInputStream、FileOutputStream、FileReader、FileWriter這幾種,每次只能操作一個字節或字符,具局限性(縱然我們可以通過自定義字節字符緩沖來實現多量獲取),可是數據的效率還是不高,所以我們使用到了另外的四個類,即BufferedInputStream、BufferedOutputStream、BufferedReader和BufferedWriter,這是針對字節流和字符流而存在的高效緩沖區,名曰緩沖流,它們存在於內存中。使用了它們兩個,那數據傳輸可就跑快快啦。在使用緩沖流之前,和硬盤中文件的交互是一次一個的,硬盤本身的效率就低,IO交互頻繁,效率肯定低了。緩沖流的意思,就是先將硬盤中的數據放置在內存中,當緩沖流滿了后,一次性讀取出來,效率可見很高了。
而這四種緩沖流,或者這兩類緩沖流,用法也是不一樣的。BufferInputStream和BufferedOutputStream二者,只能操作字節,無法將字節數據轉換為字符讀入到Java程序,更無法將字符寫入到文件中;而BufferReader和BufferWriter卻可以操縱字符或者字節,在傳輸的時候再搭配InputStreamReader和OutputStreamWriter,附以指定編碼,將字節轉為字符讀入或將字符轉為字節寫入文件,InputStreamReader和OutputStreamWriter又成為字節和字符之間的橋梁。。
所以,我們一般通過這種形式來實現輸入輸出,即緩沖流+轉換流+字節流的形式。
代碼如下:
讀入文件:
BufferedInputStream bis=new BufferedInputStream(new FileInputStream(文件路徑));
BufferedReader br=new BufferedReader(new InputStreamReader(new FileInputStream(文件路徑)), 編碼);
寫文件的話呢,將InputStream和Reader改為Writer即可。
在使用以上兩種的話,針對不同的情況來使用就可以啦,但是先要明白傳輸的數據是什么樣子的。
總結,我們在數據和文件進行交互的時候,文件中存儲的都是字節,而文件中存在的字符,實際上也是字節的形式。在進行數據讀取時,需要將帶有字符的字節經過轉碼變成可識別的數據;而進行寫入文件時,也許將字符轉為字節,然后經過轉碼為文件可識別的數據,然后將字節進行傳輸,最終文件中存儲的還是字節。
所以上面所講的,FileReader和FileWriter因為二者是直接操縱字符, 而遇到字符編碼不匹配則會報錯,我們在開發過程中,不會單一的使用FileReader和FileWriter,如果在字節和字符的橋梁中使用這二者是沒法使用的,只能單獨使用這二者,那么遇到編碼問題可能會出錯的。
補充:在輸出流這里,我們還有一種流成為打印流,即PrintStream,它是屬於OutputStream。在我們使用輸出流的時候,我們一般會使用FileInputStream來指定文件,我們可以將FileInputStream修改為PrintStream,同樣可以輸出到文件中。
所以我們來說一下另外一個流,打印流,簡單概述一下。
那這個PrintStream是個什么東東呢?大家都知道在我們控制台中可以輸出數據吧,System.out.println(“哈哈”); 對的,就是這個樣子,我們是很熟悉的打印數據,我們為什么稱之為打印呢,原因來源於PrintStream。我們在輸入System.in后,它會返回一個類PrintStream,然后我們可以通過println(), 來輸出數據,這個方法是在PrintStream類中的,可以輸出各種各樣的數據,所以我們才每次打印數據那么的方便,來源就是在於PrintStream。我們可以打印數據到控制台,同樣可以打印到文件,所以我們創建PrintStream對象,來指定文件,即可將數據打印到文件中。
上面我們說了幾種流,有IO流、字節流、字符流、字節或字符緩沖流、轉換流、打印流。下面我們來說說對象流,這個流也是比較重要的,我們可以將創建的對象放置在流中進行傳輸,傳輸的形式有兩種,一種是文件傳輸,一種是網絡傳輸,即寫到硬盤文件中,通過網絡傳輸出去。我們在使用對象流時,用到了兩個類,ObjectInputStream和ObjectOutputStream,無疑,從單詞上我們可以看出,將Object對象使用字節流InputStream傳送,最終以字節的形式存在文件中。
方法:
ObjectInputStream ois=new ObjectInputStream(new FileInputStream(文件名));
ObjectOutputStream ois=new ObjectOutputStream(new FileOutputStream(文件名));
然后再調用相應方法獲取即可。
我們將對象存到硬盤中,需要一個關鍵的步驟,序列化,將對象作為一個序列化文件存儲到硬盤中,所以我們需要將用到的對象類實現接口Serializable,然后必須指定一個序列化號serialVersionUID,這個號是唯一的,當我們進行反序列化的時候,才不會報錯。因為在反序列化的過程中,會自動將序列化文件和類進行比對,再和類中的序列號進行比對,當完全無誤后,方可序列化成功。
然后,我們還有一個讀寫於一體的類,RandomAccessFile,這個類中讀取和寫入是一體的,在之前我們學的IO流中,都是IO分開,需要用到兩個類才能輸入輸出數據。這個類不同,只需要調用不同的方法即可訪問文件中的數據。而且,可以隨機訪問文件中指定位置的數據,比如指定下標偏移量(seek方法),跳過多少字節(skipBytes),通過這兩種方式來獲取隨機位置的數據,如果不通過這二者方法,則是無法獲取數據的。
這個類,在多線程下載處使用的多,比如,迅雷下載,能夠看到進度條中一小塊一小塊的,這大概就是在一塊一塊獲得數據吧,哈哈小編了解的不多,可能是這個樣子,還有待研究的。
最后呢,我們在文件中存儲數據和讀取數據,還有一個至為關鍵的,名曰Properties,這個類是一個屬性類,我們在Web地方用的最多了,它是和兩個知識點有關。
首先,Properties是屬於集合的分支,是HashTable類的子類,HashTable又是Map的子類,所以它是集合的類。由於HashTable是同步的,不允許為空,且存儲也是無序,所以線程安全,存的也是鍵值對。其次,Properties又和IO流有關,通過list方法,或者store方法,然后指定輸出流,再指定路徑,即可寫入到文件中。在文件中,存儲也是以鍵值對的形式存放,但是都是String類型。
Properties文件存到硬盤中后,我們時常需要取到該文件中的數據,所以我們需要將其像輸入流一樣載入進來。我們通過其Properties類的load方法,然后在參數中指定輸入流即可,這里,我們指定的輸入流可以是FileReader,也可以是轉換流+字節流。我們在之前說FileReader是一次讀一個字符,而且當字符不匹配會亂碼,因為字符是多個字節,我們無法保證可以將字節拼接起來,因為字符對應的字節數是不定的,所以我們是不推薦這種方式讀入的。可是在這里,由於Properties的特殊性,我們可以使用FileReader直接讀入了,不會出現亂碼的情況,因為它讀入了,不需要拼接,直接輸出即可。
在Properties寫入文件操作時,我們通過字節流寫入文件,則會將當前java中的字符編碼的字節形式寫入到文件中,如果硬盤文件的字符編碼和Java的字符編碼不同,則會出現亂碼;如果我們通過字符流的形式寫入,那無論硬盤文件的編碼是否一致,都不會出現亂碼。這也就是為什么使用list寫入不會亂碼,而通過store形式會有可能亂碼的。
聲明一點,由於Properties是線程安全的,它存放鍵和值的屬性除了String,再也不允許其它類型數據,否則會報錯,導致不安全。
所以Properties是既帶有集合的特點,又可以實現IO流的方法,且它是沒有泛型的,這一點又和Map集合不相同,所以屬於自成一派的。
經過小編這么一通撤,小編也思路清晰許多了呢,歡迎各為大牛前來點評。
媽媽說哪里不好點哪里......