摘要:在包下主要包括輸入輸出兩種流,每種輸入輸出流又可分為字節流和字符流兩大類。輸入輸出是從程序運行所在的內存的角度而言的。的輸入流主要由和作為基類,而輸出流主要由和作為基類。
本章主要參考和摘自瘋狂java講義上面的(java編程思想的后面看過后有新的內容再補充進去吧)。
輸入輸出是所有程序都必需的部分————使用輸入機制允許程序讀取外部數據(包括磁盤、光盤等存儲設備上的數據和用戶輸入的數據)、使用輸出機制允許程序記錄運行狀態,將程序數據輸出到外部(磁盤、光盤等存儲設備當中和控制臺當中)。輸入輸出是從程序運行所在的內存的角度而言的。
Java的IO起初僅通過java.io包下的類和接口來支持,但在java7中在java.nio及其子包下新增了關于IO的api,被稱為NIO2。
在java.io包下主要包括輸入、輸出兩種IO流,每種輸入輸出流又可分為字節流和字符流兩大類。其中字節流以字節為單位來處理輸入、輸出操作,而字符流以字符來處理輸入、輸出操作。
字節流和字符流的分法是基于類型區別上的,但另一方面為了方便理解,可以從功能上將IO流可分為底層結點流和上層處理流,其中結點流用于和數據源直接關聯(但不同的數據源關聯結點流的方式可能存在一定的差異,這里的數據源可以是文件、內存(字符串、數組等)、線程(管道通信)、網絡(套接字通信中用到的)等),處理流則可以對現有的流進行包裝,從而允許程序使用統一的輸入、輸出代碼來讀取不同的物理存儲結點的資源。
注意:處理流這個概念是同時包含了裝飾者和適配器的。裝飾者必須必須能取代被裝飾者,所以裝飾者與被裝飾者必須是同一類型,也就是說二者有共同的父類或者實現了共同的接口,大部分的處理流是屬于裝飾者。但也有著例外存在,如轉換流及其子類(InputStreamReader和OutputStreamWriter),他們是將字節流轉換成了字符流,采用的是一種適配器模式,其本身并沒有增加或者增強任和功能。
java.io.File類可以代表文件和目錄,如果希望在程序中操作文件和目錄,都可以通過File類來完成。這里的操作比如可以是:新建、刪除、重命名文件和目錄。但File不能訪問文件本身,要訪問文件的內容還是需要使用輸入輸出流。
public class FileTest { public static void main(String[] args) throws IOException { // 以當前路徑來創建一個File對象 File file = new File("."); // 直接獲取文件名,輸出一點 System.out.println(file.getName()); // 獲取相對路徑的父路徑可能出錯,下面代碼輸出null System.out.println(file.getParent()); // 獲取絕對路徑 System.out.println(file.getAbsoluteFile()); // 獲取上一級路徑 System.out.println(file.getAbsoluteFile().getParent()); // 在當前路徑下創建一個臨時文件 File tmpFile = File.createTempFile("aaa", ".txt", file); // 指定當JVM退出時刪除該文件 tmpFile.deleteOnExit(); // 以系統當前時間作為新文件名來創建新文件 File newFile = new File(System.currentTimeMillis() + ""); System.out.println("newFile對象是否存在:" + newFile.exists()); // 以指定newFile對象來創建一個文件 newFile.createNewFile(); // 以newFile對象來創建一個目錄,因為newFile已經存在, // 所以下面方法返回false,即無法創建該目錄 newFile.mkdir(); // 使用list()方法來列出當前路徑下的所有文件和路徑 String[] fileList = file.list(); System.out.println("====當前路徑下所有文件和路徑如下===="); for (String fileName : fileList) { System.out.println(fileName); } // listRoots()靜態方法列出所有的磁盤根路徑。 File[] roots = File.listRoots(); System.out.println("====系統所有根路徑如下===="); for (File root : roots) { System.out.println(root); } } }1.訪問文件和目錄 訪問文件名相關的方法 文件檢測相關的方法
注意:Windows的路徑分割符使用反斜線(),而Java程序當中的反斜線表示轉義字符,要表示反斜線的話則需要使用兩條反斜線(如"F:abc est.txt")或者直接使用斜線(/)也可以,java支持將斜線當成與平臺無關的路徑分隔符。
在File類的list(FilenameFilter filter)方法中可以接受一個FilenameFilter參數(函數式接口),通過該參數可以只列出符合條件的文件。FilenameFilter這一函數式接口中的方法是一個accept(File dir,String name)方法,該方法將對指定的File即dir(在list方法中傳入的是this即下面代碼中的file)的所有子目錄進行的迭代。
list(FilenameFilter filter)會循環調用accept,每次傳入this和this所在目錄中不同的文件或者子目錄名,如果這個accept方法返回true.那么list方法會列出該該子目錄或者該文件。
public class FilenameFilterTest { public static void main(String[] args) { File file = new File(".");//.代表當前project的目錄 //如果文件名以.java結尾,或者文件對應一個路徑,則返回true String[] nameList = file.list((dir,name)->name.endWith(".java")||new File(name).isDirectory); for(String name : nameList) { System.out.println(name); } } }二、理解Java的IO流 流的分類 1.輸入流和輸出流
按照流的流向來分,可以分為輸入流和輸出流。輸入輸出是從程序運行所在的內存的角度而言的。
輸入流:只能從中讀取數據,而不能向其中寫入數據。
輸出流:只能向其寫入數據,而不能從中讀取數據。
Java的輸入流主要由InputStream和Reader作為基類,而輸出流主要由OutputStream和Writer作為基類。他們都是一些抽象基類,無法直接創建實例。
字節流和字符流的用法幾乎完全一樣,區別于字節流和字符流所操作的數據單元不同————字節流操作的數據單元是8位的字節,而字符流操作的數據單元是16位的字符。
字節流主要由InputStream和OutputStream作為基類,而字符流則主要由Reader和Writer作為基類。
按照流的角色來分
可以從/向一個特定的IO設備(如磁盤、網絡)讀/寫數據的流,稱為節點流,節點流也被稱為低級流。
處理流則用于對一個已存在的流進行連接或者封裝,通過封裝后的流實現數據讀/寫功能。處理流也被稱為高級流。其實采用的是一種裝飾者的設計模式。
流的概念模型
從圖中可以看到,對于輸入/輸出流中字符流和字節流處理方式是相似的,區別在于處理的輸入/輸出單位(即水滴)不同而已。
輸入流使用隱式的記錄指針來表示當前正準備從哪個“水滴”開始讀取,每當程序從InoutStream或Writer中取出一個或者多個水滴后,記錄指針向后(右)移動;除此外,InputStream和Reader里都提供一些方法來控制記錄指針的移動。
對于圖中的輸出流而言,當執行輸出時,程序相當于依次把“水滴”放入到輸出流的水管中,輸出流同樣采取隱式的記錄指針來標識當前水滴即將放入的位置,每當程序向OutputStream或Writer輸出一個或者多個水滴后,記錄指針自動向后(右)移動。
處理流的功能主要體現在以下兩個方面:
性能的提高:主要以增加緩沖的方式來提高輸入/輸出的效率。
操作的便捷:處理流可能提供了一系列便捷的方法來一次輸入/輸出大批量的內容,而不是輸入/輸出一個或者多個水滴。
???????處理流可以嫁接在任何已存在的流(包括處理流)的基礎之上,這就允許Java應用程序采用相同的代碼、透明的方式來訪問不同的輸入/輸出設備的數據流。
三、字節流和字符流正如上面所說,字符流和字節流的操作方式幾乎完全一樣,區別只在于操作的數據單元不同而已————字節流操作的數據單元是字節,字符流操作的數據單元是字符。
InputStream和Reader InputStream和Reader是所有輸入流的抽象基類,本身并不能創建實例來執行輸入,但它們是所有輸入流的模板,定義的方法是所有輸入流都可以使用的方法。
對比可以發現:兩個基類的功能基本是一樣的。
下面以InputStream和Reader的子類FileInputStream和FileReader來舉例,它們都是節點流————會直接和指定文件相關聯。
使用數組其實就是和我們平時用水杯而不是用小酒盅去飲水機接水一樣,用酒盅每次只能先打開水龍頭接一滴,然后關上水龍頭,然后喝完了再去打開水龍頭去接下一滴。而用水杯就可以一次打開水龍頭之后,接很多很多滴,再關上水龍頭,喝完之后再去接水。可以看出使用水杯減少了喝水的次數的開關水龍頭的次數,其實就是減少了讀取的次數。
public class FileInputStreamTest { public static void main(String[] args) throws IOException { // 創建字節輸入流 FileInputStream fis = new FileInputStream("srcIO系統FileInputStreamTest.java"); byte[] bbuf = new byte[1024];//創建一個長度為1024的“竹筒” int hasRead = 0;//記錄每次實際讀取的字節數 // 使用循環來重復“取水”過程 while ((hasRead = fis.read(bbuf)) > 0 ) { // 取出“竹筒”中水滴(字節),將字節數組轉換成字符串輸入! System.out.print(new String(bbuf , 0 , hasRead )); } fis.close();// 關閉文件輸入流,放在finally塊里更安全 } }
注意:在創建較小長度的字節數組時,需要多次讀取,而文件保存時用的是GBK或者UTF-8,每個中文占兩個或三個字節,如果某次讀取時讀到了不完整的中文字符(1/2,1/3,2/3),就會出現亂碼。但利用下面的字符流就不會出現這種情況,因為在讀取中文的時候會自動轉換為unicode字符集的兩個字節,剛好是一個char能夠放得下的。詳情參考自己的印象筆記:關于utf-8所占用的字節
public class FileReaderTest { public static void main(String[] args) { try(FileReader fr = new FileReader("srcIO系統FileReaderTest.java"))// 創建字符輸入流 { char[] cbuf = new char[32];//創建一個長度為32的“竹筒” int hasRead = 0;//記錄每次實際讀取的字符數 while ((hasRead=fr.read(cbuf)) > 0 )// 使用循環來重復“取水”過程 { // 取出“竹筒”中水滴(字符),將字符數組轉換成字符串輸入! System.out.print(new String(cbuf , 0 , hasRead)); } } catch (IOException ex) { ex.printStackTrace(); } } }
上面的FileInputStreamTest程序最后使用了fis.close()來關閉文件輸入流。因為與JDBC編程一樣,程序里打開的IO資源不屬于內存里的資源,垃圾回收機制無法回收該資源,所以顯式關閉文件IO資源。而在FileReaderTest不需要的原因是,Java7改寫了所有的IO資源類,它們都實現了AutoCloseable接口,因此如果將打開IO資源的聲明與初始化寫在try的括號里面,那么在try語句塊結束后(注意不是在整個try-catch-finnaly結束后),打開的IO資源會被虛擬機自動關閉。
OutputStream和WriterOutputStream和Writer也有著相似的方法:
使用FileInputStream和FileOutputStream復制文件的例子:
public class FileOutputStreamTest { //就是先用輸入流從硬盤讀到內存中,再用輸出流從內存中輸出到硬盤上 public static void main(String[] args) { try(FileInputStream fis = new FileInputStream("srcIO系統FileOutputStreamTest.java");// 創建字節輸入流 FileOutputStream fos = new FileOutputStream("newFile.txt"))// 創建字節輸出流 { byte[] bbuf = new byte[32]; int hasRead = 0; // 循環從輸入流中取出數據 while ((hasRead = fis.read(bbuf)) > 0 ) { fos.write(bbuf , 0 , hasRead);// 每讀取一次,即寫入文件輸出流,讀了多少,就寫多少 } } catch (IOException ioe) { ioe.printStackTrace(); } } }
下面是使用FileWriter進行寫入的例子:字符串最后有 ,這是windows平臺的換行符,通過這種方式就可以讓輸出內容換行,如果是unix/linux等平臺,則使用 就是換行符。
public class FileWriterTest { public static void main(String[] args) { try( FileWriter fw = new FileWriter("poem.txt")) { fw.write("錦瑟 - 李商隱 "); fw.write("錦瑟無端五十弦,一弦一柱思華年。 "); fw.write("莊生曉夢迷蝴蝶,望帝春心托杜鵑。 "); fw.write("滄海月明珠有淚,藍田日暖玉生煙。 "); fw.write("此情可待成追憶,只是當時已惘然。 "); } catch (IOException ioe) { ioe.printStackTrace(); } } }四、輸入輸出流體系
上節介紹了輸入輸出流的4個抽象基類,并介紹了4個訪問文件結點流的用法。借助于處理流,我們可以進一步簡化編程
處理流的用法
下面是處理流PrintStream的例子,它包裝OutputStream,使用處理流之后可以更方便,在這里的PrintStream體現為:相比OutputStream,可以直接打印字符串、并且設定打印格式,還能打印其他各種基本數據類型的變量。此外還有可以設置自動flush的功能,在調用了println方法后或者write寫入了一個數組后或者write寫入了r或者n之后自動flush。詳細可參考api或者進行測試
public class PrintStreamTest { /**用notepad++實時查看文件是否寫入是不準確的的。因為notepad++的機制可能是在你不操作文件的時候, 不定時地去解除對文件的占用。當未解除占用的時候,eclipse里面執行完寫入也是寫不進去的。 所以最好是在單步調試中的每次寫入操作完畢后,手動打開文件查看一下**/ public static void main(String[] args) { try( FileOutputStream fos = new FileOutputStream("test.txt"); PrintStream ps = new PrintStream(fos,true) ) { // 使用PrintStream執行輸出 ps.println("直掛云帆濟滄海"); // 直接使用PrintStream輸出對象 ps.println(new PrintStreamTest()); } catch (IOException ioe) { ioe.printStackTrace(); } } }
提示:由于PrintStream的功能非常強大,通常如果需要輸出文本內容,都應該將輸出流包裝秤PrintStream后進行輸出。在使用處理流包裝了后,關閉輸入輸出流資源時,只需要關閉最外層的處理流即可,系統會自動關閉該處理流包裝的的節點流。其實還是裝飾者設計模式的思想
輸入輸出流體系
上面那張圖做了功能上的分類,
文件結點流
數組結點流
字符串結點流
管道節點流
打印控制處理流
推回輸入處理流
緩沖處理流
對象序列化處理流
轉換處理流(適配器)
數據處理流
???????需要提一下的是轉換處理流內部其實也使用了一個裝飾者的機制,先將字節流用StreamDecoder/StreamEncoder進行了裝飾,然后再進行的適配,這個裝飾的效果之一就是提供了默認大小8192字節的緩沖區,所以可以說適配器也是有著緩沖區功能的。
另外還有就是FileWriter和FileReader,按照上面節點流的定義,查看源碼后,認為不應該算作是節點流,而是轉換處理流。因為它正是在轉換流內部先根據文件名等信息先創建了真正的文件節點流(FileInputStream、FileOutputStream)對象,再進行轉換而已。雖然這點上在概念有分歧但并不影響我們的正常使用。
關于繼承關系則可以看下面這幾張圖,從原圖的基礎上自己加了文字注釋:
通常來說,字節流的功能比字符流的功能更強大,因為計算機里面的所有數據都是二進制的,而字節流可以處理所有的二進制文件——但問題是,如果使用字節流來處理文本文件,則需要使用合適的方式把這些字節轉換成字符。所以,通常的規則是:如果進行輸入/輸出的內容是文本內容,則應該考慮使用字符流;如果輸入/輸出的內容是二進制內容,則應該考慮使用字節流。
此外還有些關于重定向、讀寫其他進程的數據、任意文件訪問類、NIO以及NIO2的相關知識,可以參看瘋狂java一書或者其他文章。本篇筆記就寫到這了,不能再加了,太卡了...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/72368.html
摘要:基于版本基于版本。由于中英行文差異,完全的逐字逐句翻譯會很冗余啰嗦。譯者在翻譯中同時參考了谷歌百度有道翻譯的譯文以及編程思想第四版中文版的部分內容對其翻譯死板,生造名詞,語言精煉度差問題進行規避和改正。 來源:LingCoder/OnJava8 主譯: LingCoder 參譯: LortSir 校對:nickChenyx E-mail: 本書原作者為 [美] Bru...
摘要:新書中文版發布。譯者聲明中文翻譯經過個多月,我們終于完成了翻譯草稿。注意各種問題或者建議可以提,建議使用中文。可用于學習研究目的,不得用于任何商業行為。 Yoshua Bengio 新書《Deep Learning》中文版發布。該書由北京大學張志華老師團隊負責翻譯。本書于學習研究目的,不得用于任何商業行為。下載鏈接:https://github.com/exacity/deeplearnin...
摘要:第四章安全管理制度發布第十條安全管理制度必須以正式文件的形式發布施行。第十一條安全管理制度由信息安全管理小組制訂,信息安全領導小組審批發布。第二十條安全管理制度的修改與廢止須經信息安全領導組織審批確認,信息安全管理部門備案。 字數 3610閱讀 760評論 0贊 3《xxxx安全管理制度匯編》****制度管理辦法****文...
閱讀 3208·2021-11-12 10:36
閱讀 1258·2019-08-30 15:56
閱讀 2442·2019-08-30 11:26
閱讀 550·2019-08-29 13:00
閱讀 3608·2019-08-28 18:08
閱讀 2748·2019-08-26 17:18
閱讀 1892·2019-08-26 13:26
閱讀 2431·2019-08-26 11:39