第十五章 輸入/輸出

File類

  • File類能去新建刪除重命名目錄和文件,不能訪問文件內(nèi)容本身
  • 默認情況以用戶的工作路徑來解釋相對路徑,就是運行jvm時所在的路徑
  • 使用文件路徑字符串來創(chuàng)建File實例
  • File類提供了操作文件和目錄的方法,用File對象來調(diào)用,訪問文件名相關(guān)方法,文件檢測,獲取常規(guī)文件信息,文件和目錄操作的相關(guān)方法
  • 使用相對路徑的FIle對象獲取父路徑時可能引起錯誤,因為該方法返回將File對象對應的目錄名文件名里最后一個子目錄名子文件名刪除后的結(jié)果
  • windows路徑字符串分隔符可以用\也可以用/
  • File類的list()方法接受一個FilenameFilter參數(shù),這個函數(shù)式接口包含一個accept(File dir, String name)方法用于對指定File的所有文件或目錄進行迭代

理解IO流

  • Java把不同的輸入輸出源抽象表述為流,stream是從起源到接收的有序數(shù)據(jù)
  • 程序運行所在內(nèi)存的角度分輸入流和輸出流,操作的數(shù)據(jù)單元分字節(jié)流字符流,流的角色分節(jié)點流處理流
  • 可以從一個特定的IO設備讀寫數(shù)據(jù)的流叫節(jié)點流,又叫低級流,程序直接連接到實際的數(shù)據(jù)源,和實際的節(jié)點連接
  • 處理流是對一個已存在的流進行連接或封裝,通過封裝后的流來實現(xiàn)數(shù)據(jù)讀寫,又叫高級流
  • 處理流的好處只要使用相同的處理流,程序就可以用相同的代碼訪問不同的數(shù)據(jù)源,隨著處理流所包裝的節(jié)點流的變化,程序?qū)嶋H所訪問的數(shù)據(jù)源也發(fā)生變化
  • 處理流包裝節(jié)點流是一種典型的裝飾器模式,可以消除不同節(jié)點流實現(xiàn)差異,也可以方便方法輸入輸出數(shù)據(jù),程序不用理會輸入輸出節(jié)點的種類,只需要包裝成處理流就可以使用相同的代碼來讀寫不同輸入輸出設備的數(shù)據(jù)
  • 處理流性能提高,增加了緩沖的方式來提高輸入輸出效率,提供了一系列便捷方式一次輸入輸出批量數(shù)據(jù),而不是一個字節(jié)或字符
  • 四個抽象基類InputStream、Reader、OutputStream、Writer
  • 輸入流使用隱式指針表示開始讀取的地方,輸出流隱式記錄指針表示即將放入的位置,都會隨著讀寫移動

字節(jié)流和字符流

  • InputStrean Reader是所有輸入流的抽象基類,本身不能創(chuàng)建實例,作為所有輸入流的模板,提供了多種read()方法
  • 用于讀取文件的節(jié)點流FileInputStream FileReader
  • 程序打開的IO資源不屬于內(nèi)存里的資源需要顯式關(guān)閉,可以放在try()里面自動關(guān)閉
  • InputStrean Reader還提供了操作記錄指針的方法 mark markSupported reset skip
  • OutputStream Writer和上面的相似
  • 關(guān)閉輸出流除了資源回收還會將緩沖區(qū)的數(shù)據(jù)flush到物理節(jié)點中
  • Windows平臺換行符是\r\n Unix是\n

輸入輸出流體系

  • 處理流隱藏底層設備上節(jié)點流的差異,提供更方便的輸入輸出方法
  • 處理流的構(gòu)造參數(shù)不是一個物理節(jié)點而是一個已經(jīng)存在的流,而所有節(jié)點流都是直接以物理節(jié)點作為構(gòu)造器參數(shù)的
  • 通常需要輸出文本內(nèi)容都應該將輸出流包裝成PrintStream后進行輸出
  • 關(guān)閉最上層的處理流時系統(tǒng)會自動關(guān)閉被其包裝的節(jié)點流
  • 輸入輸出流體系


  • 通常字節(jié)流功能比字符流功能強,但是處理文本內(nèi)容會不方便
  • 輸入輸出為文本內(nèi)容時應該考慮字符流,二進制內(nèi)容應該考慮字節(jié)流
  • 字符流可以使用字符串作為物理節(jié)點,實現(xiàn)從字符串讀取內(nèi)容,或?qū)?nèi)容寫入字符串,StringWriter使用StringBuffer作為輸出節(jié)點,因為String是不可變的字符串對象
  • 轉(zhuǎn)換流實現(xiàn)了將字節(jié)流轉(zhuǎn)換成字符流
  • 輸入文本時,可將InputStrean類的實例,可以轉(zhuǎn)換成字符輸入流,再包裝成處理流,方便使用
  • 推回輸入流PushbackInoutStream PushbackReader提供了unread方法實現(xiàn)講讀取的內(nèi)容推回到推回緩沖區(qū),read是先讀取推回緩沖區(qū)中的內(nèi)容
  • 創(chuàng)建PushbackInoutStream PushbackReader時需要指定推回緩沖區(qū)的大小,默認為1,如果推回內(nèi)容大于指定大小,會出現(xiàn)異常

重定向標準輸入輸出

  • System類中提供了三個重定向標準輸入輸出的方法 setErr setIn setOut
System.setOut(ps);
System.out.println("...");//會輸出到ps指定的地方

Java虛擬機讀寫其他進程的數(shù)據(jù)

  • Runtime對象的exec()方法獲取Process對象,此對象代表由該Java程序啟動的子進程
  • Process類提供了三個方法 getErrorStream getInputStream getOutoutStrean 獲取子進程的節(jié)點流,這里的輸入輸出是對于程序而言的

RandomAccessFile

  • 支持隨機訪問,可以直接跳轉(zhuǎn)到任何地方讀寫數(shù)據(jù)
  • 如果程序需要在已存在的文件后追加內(nèi)容,應該使用RandomAccessFile,但是仍然不能向指定位置插入內(nèi)容,同樣會覆蓋原有內(nèi)容
  • 但是只能讀寫文件,不能讀寫其他IO節(jié)點
  • RandonAccessFile包含兩個方法操作記錄指針,getFilePoint seek ,也包含同InputStream和OutputStrean的read和write方法,另外提供readXxx WriteXxx方法來輸入輸出
  • 兩個構(gòu)造器,一個使用文件名String參數(shù),一個使用File參數(shù),還需要指定訪問模式mode參數(shù),r rw rws rwd

對象序列化

  • 對象序列化的目標是將對象保存到磁盤中,或允許在網(wǎng)絡中直接傳輸對象
  • 對象序列化機制允許將內(nèi)存中的對象轉(zhuǎn)換成與平臺無關(guān)的二進制流,使得對象可以脫離程序的運行而獨立存在
  • 序列化是將一個Java對象寫入IO流中,反序列化是從IO流中恢復對象
  • 序列化對象必須要讓它的類是可序列化的,必須實現(xiàn)兩個接口之一,Serializable Externalizable
  • Serializable是一個標記接口,不需要實現(xiàn)任何方法,很多類都實現(xiàn)了此接口,建議創(chuàng)建的每個JavaBean類都實現(xiàn)了Serializable
  • 序列化步驟
  • 創(chuàng)建ObjectOutputStream,是個處理流
  • 調(diào)用writeObject方法輸出可序列化對象
  • 反序列化步驟
  • 創(chuàng)建ObjectInputStream輸入流
  • 調(diào)用readObject方法讀取流中對象,返回Object類型的對象,如果知道類型,可以強制轉(zhuǎn)換成真實類型
  • 反序列化讀取的是對象,所以還需要提供對象所屬的class文件,否則會出現(xiàn)異常
  • 反序列化機制無須通過構(gòu)造器來初始化Java對象
  • 當在一個文件序列化了多個對象,反序列化讀取時應該按照實際寫入的順序讀取
  • 可序列化類的父類必須要么有無參數(shù)的構(gòu)造器要么也是可序列化的,否則反序列化時會拋出異常
  • 如果父類不可序列化但有無參數(shù)構(gòu)造器,則父類中定義的成員變量值不會序列化到二進制流中
  • 可序列化的類中引用類型變量的引用類也必須是可序列化的,遞歸序列化
  • 特殊的序列化算法
  • 所有保存到磁盤中的對象都有一個序列化編號
  • 程序企圖序列化一個對象時會檢查該對象是否已經(jīng)被序列化過,只有在本次虛擬機中沒有被序列化過,才會序列化輸出
  • 已經(jīng)序列化過的,程序只是輸出一個序列化編號,而不是重新序列化該對象
  • 當程序序列化一個可變對象時,序列化過一次之后即使改變了實例變量的值,也不會輸出序列化
  • transient關(guān)鍵字只能修飾實例變量,使序列化時不理會該實例變量,反序列化恢復時不能獲取到該實例變量的值
  • 實現(xiàn)自定義序列化


    自定義重寫readObject()方法哪些實例變量需要序列化,怎樣序列化,重寫writeObject()需要反序列化哪些實例變量和怎樣反序列化

  • readObject()和writeObject()兩個方法對應,重寫時應該注意相反處理,操作實例變量的順序也應該一致,以便正確恢復
  • 默認情況readObject()調(diào)用out.defaultWriteObject來各保存實例變量,writeObject()調(diào)用in.defaultReadObject來恢復對象的非瞬態(tài)實例變量
  • 當序列化流不完整時,readObjectNoData()可以用來正確的初始化反序列化的對象
  • 重寫writeReplace()可以在序列該對象時將該對象替換成其他對象,由序列化機制調(diào)用,可以擁有訪問權(quán)限,可以被子類繼承


  • 序列化機制保證在序列化某對象之前先調(diào)用writeReplace(),如果返回另一個對象,則轉(zhuǎn)為序列化另一個對象
  • 會先調(diào)用writeReplace(),如果返回另一個對象后會調(diào)用那個對象的writeReplace()直到不再返回另一個對象,最后調(diào)用writeObject()
  • readResolve()接著readObject()之后調(diào)用,該返回值會代替原反序列化的對象,原來的對象被立即丟棄,在單例類和早期的枚舉類中有效,都應該提供readResolve()
  • 反序列化可以用來克隆對象,不需要構(gòu)造器的新建對象
  • readResolve()同樣可以使用任意訪問權(quán)限控制符,對于不是final類的重寫readResolve()最好用private修飾該方法
  • 實現(xiàn)Externalizable接口的自定義序列化機制完全由程序員決定存儲和恢復對象數(shù)據(jù)
  • Externalizable接口提供了readExternal writeExternal,強制自定義序列化,和readObject() writeObject類似
  • 實現(xiàn)Externalizable接口的類的對象序列化反序列化操作同Serializable,一樣調(diào)用ObjectOutputStream的writeObject()和ObjectInputStream的readObject()
  • 實現(xiàn)Externalizable序列化類必須提供public的無參數(shù)構(gòu)造器
  • Externalizable性能略好,但是編程復雜度增加
  • 對象序列化注意點
  • 對象的類名和實例變量會被序列化,方法、類變量、transient修飾的實例變量不會被序列化
  • 在實例變量前加static可以實現(xiàn)和transient一樣的效果,但是不能這樣用
  • 必須保證可序列化的類的實例變量的類型也是可序列化的
  • 反序列化對象時必須有序列化對象的class文件
  • 反序列化按實際寫入順序讀取
  • 序列化機制允許為序列化類提供一個private static final 的serialVersionUID值,標識類的序列化版本
  • 最好在每個要序列化的類中加入serialVersionUID,數(shù)值自己定義
  • 不顯示定義的話,JVM會根據(jù)類的相關(guān)信息計算,修改后會導致與之前計算結(jié)果不一致,從而造成反序列化因類版本不兼容而失敗。
  • 不同JVM計算方法也可能不同,,不利于程序在不同JVM上移植,導致反序列化失敗
  • 對象流中的對象比新類中包含更多的實例變量,多出的實例變量值會被忽略,序列化版本兼容
  • 如果新類包含更多的實例變量,序列化版本也兼容,但反序列化得到的新對象的值都是null或0

NIO

NIO概述

  • 面相流的輸入輸出系統(tǒng)一次只能處理一個字節(jié),效率不高
  • NIO采用內(nèi)存映射文件的方式處理輸入輸出,將文件或文件的一段區(qū)域映射到內(nèi)存中,像訪問內(nèi)存一樣來訪問文件,提高了效率
  • Channel Buffer是NIO中兩個核心對象
  • Channel提供map()方法,直接將一塊數(shù)據(jù)映射到內(nèi)存中
  • 發(fā)送到Channel的數(shù)據(jù)必須首先放到Buffer中,Buffer可以去讀取Channel的數(shù)據(jù),也可以使用Channel的map()將數(shù)據(jù)映射成Buffer
  • NIO還有Unicode字符串映射成字節(jié)序列,Charset逆映射,支持非阻塞式輸入輸出的Selector

Buffer

  • Buffer是一個抽象類,Buffer常用子類ByteBuffer和CharBuffer,XxxBuffer.allocate(capacity)返回對象
  • ByteBuffer有一個子類MappedByteBuffer由Channel的map()方法返回
  • Buffer三個概念
  • capacity,緩沖區(qū)容量,創(chuàng)建后不能改變
  • limit,界限,第一個不該被讀寫的索引
  • position,位置,下一個被讀寫的位置索引
  • Buffer同樣支持mark,同IO流中的mark
  • 0≤mark≤position≤limit≤capacity
  • 默認開始時,position為0,limit為capacity
  • flip()方法將limit設置為position的位置,并將position設置為0
  • clear()方法設置position為0,limit為capacity,對Buffer執(zhí)行clear()后,對象的數(shù)據(jù)依然存在
  • Buffer常用方法capacity hasRemaining limit mark position remaining reset rewind
  • 訪問數(shù)據(jù)的常用方法 put() get(),分相對和絕對,相對是從position處讀寫數(shù)據(jù),絕對是根據(jù)索引讀寫數(shù)據(jù)
  • ByteBuffer提供allocateDirect()來創(chuàng)建直接Buffer,創(chuàng)建成本較高但讀取效率好

Channel

  • Channel可以將文件的部分或全部映射成Buffer
  • 程序不能直接訪問Channel,只能通過Buffer來交互
  • Channel應該用傳統(tǒng)節(jié)點的getChannel()來返回對應的Channel來創(chuàng)建
  • Channel常用方法,read() write() map()
  • MappedByteBuffer map(FileChannel.MapMode mode, long position, long size);第一個參數(shù)有只讀、讀寫等模式
  • RandomAccessFile的getChannel()返回的是只讀還是讀寫取決于RandomAccessFile打開文件的模式
  • 也可以不一次性map,使用Buffer分批讀取數(shù)據(jù),結(jié)合flip和clear也可以做到

字符集和Charset

  • Charset類提供了字節(jié)序列和字符序列之間的轉(zhuǎn)換關(guān)系,是不可變類
  • forname()創(chuàng)建字符集對象,調(diào)用newDecoder()或newEncoder()返回解碼器編碼器對象,再調(diào)用decode()或encode()進行字節(jié)序列和字符序列的轉(zhuǎn)換
  • 便捷轉(zhuǎn)換方法,使用Charset類的decode encode方法來轉(zhuǎn)換
  • String類的getBytes()也將字符串轉(zhuǎn)換為字節(jié)序列

文件鎖

  • 文件鎖避免多個進程并發(fā)修改同一個文件
  • FileChannel提供lock() tryLock()獲取文件鎖FileLock對象
  • lock()獲取不到文件鎖時會使程序一直阻塞,tryLock()將直接返回null而不會阻塞,直接使用lock() tryLock()獲取排他鎖
  • Lock() tryLock()可以鎖定文件的部分內(nèi)容,參數(shù)shared表示共享鎖,允許多個進程讀取文件,但阻止其他進程獲取該文件的排他鎖
  • release()釋放文件鎖
  • 對于高并發(fā)訪問情形,應該用數(shù)據(jù)庫保存程序信息,而不是使用文件
  • 在某些平臺,文件鎖不是強制性的,一個程序不能獲得文件鎖,也能對文件進行讀寫
  • 某些平臺,不能同步鎖定一個文件并把它映射到內(nèi)存中
  • 文件鎖是JVM所持有的,同一JVM的兩個運行程序不能對同一個文件進行加鎖
  • 某些平臺上關(guān)閉FileChannel時會釋放次JVM的所有鎖

NIO.2

  • Java7對原有的NIO提供了全面的文件IO和文件系統(tǒng)的訪問支持,提供了基于異步Channel的IO
  • NIO.2引入了一個Path接口,提供了Files和Paths工具類
  • Files提供大量便捷的靜態(tài)工具方法操作文件,是一個高度封裝的工具類,完成文件復制讀取文件內(nèi)容寫入文件內(nèi)容,Java8允許使用StreamAPI操作文件和目錄內(nèi)容
  • Paths提供兩個返回Path的靜態(tài)工廠方法
  • Files提供walkFileTree()遍歷文件和子目錄,需要FileVisitor文件訪問器
  • 遍歷時會觸發(fā)FileVisitor的方法,并會返回一個枚舉類值表示后續(xù)行為


  • 可以繼承SimpleFileVisitor(FileVisitor的實現(xiàn)類)來實現(xiàn)自己的文件訪問器,再根據(jù)需要重寫指定方法
  • Path類提供register()方法用WatchService對象監(jiān)聽指定目錄下的指定類型事件的文件變化
  • WatchService提供獲取WatchKey的方法,poll() take(),如果程序需要一直監(jiān)控,應該選擇take方法,如果指定監(jiān)控時間,應該選poll方法
  • Java7在java.nio.file.attribute包下提供讀取修改文件屬性的方法
  • 這些工具類主要分為XxxAttributeView文件屬性視圖,XxxAttributes文件屬性集合,一般通過視圖對象獲取,方法詳見API
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容