第15 章 輸入/輸出

15 章 輸入/輸出

使用輸入機制 允許程序記錄運行時讀取外部數據,(磁盤,關盤等存儲介質),用戶輸入

使用輸出允許程序記錄運行狀態,將程序數據輸出到磁盤、關盤等介質

java io流使用了一種裝飾設計模式,它將IO流分成底層字節流和上層處理流,其中節點流和底層物理存儲節點直接關聯,程序在將處理流包裝成處理流,從而包裝程序使用輸入輸出流來訪問不同節點

15.1 File

15.1.1 訪問文件和目錄

File類可以使用文件路徑字符串來創建File實例,該路徑可是相對路徑也可以是絕對路徑

UNIX/Linux/BSD等系統上,如果路徑是( / ) 則表明是絕對路徑

 

15.2 理解javaIO

15.2.1 流的分類

1. 輸入流和輸出流(以內存來劃分, 以服務器來劃分)

輸入流 :只能從中讀取數據不能寫入數據

輸出流 :只能想其中寫入數據,而不能讀取數據

java的輸入流主要由InputStreamReader作爲基類

輸出流主要是用OutputStreamWriter作爲基類

 

2. 字節流和字符流

字符流主要有InputStreamOutputStream作爲基類

字符流主要是Reader Writer 作爲基類

 

3. 節點流和處理流

按照流的角色來分,可以分爲節點流和處理流

可以從一個特定的IO設備(磁盤、網絡)讀/寫數據的流,稱爲節點流(低級流)

處理流則用用對一個已經存在的流進行連接或者封裝。通過封裝後的流來實現數據的讀寫功能(高級流)

15.2.2 流的概念模型

java把所有的有序數據抽象成流模型,簡化了輸入輸出處理,理解了流的概念模型也就瞭解兩類javaIO

 

JAVA 的處理流模型則體現java輸入/輸出流的靈活,處理流的功能主要體現在以下兩個方面

1.功能的提高:主要增加緩衝的方式來提高輸入/輸出的效率

2.操作的便捷:處理流可能提供了一系列的便捷方法來一次輸入/輸出大量流

通過處理流java無需理會輸入輸出的是磁盤還是網絡

 

15.3 字節流和字符流

15.3.1 InputStream Reader

InputStream Reader是所有輸入流的抽象基類,本身不能創建實例來執行輸入,但他們分別有一個讀取文件的輸入流 FileInputStream FileReader

 

15.3.2 OutputStreamWrite

使用javaIO流執行輸出是,不要忘記關閉輸出流

1.關閉輸出流可以保證流的物理資源被關閉

2.可能還可以將輸出流緩存區中的數據flush到物理節點裏

 

15.4 輸入/輸出流體系

15.4.1 處理流的用法

處理流可以隱藏底層設備撒上節點流的差距,並對外提供更加方便的輸入/輸出方法,讓程序員只需關心高級流的操作

使用處理流是的典型思路是  使用處理流來包裝節點流,程序通過處理流來執行輸入輸出的功能,讓節點流與底層的I/0設備,文件交換

 

只要流的構造器參數不是一個物理節點而是一個已將存在的流,那麼這種流就一定是處理流

 

PrintStream類的輸出功能非常強大,通常如果需要輸出文本內容,都應該講輸出流包裝成PrintStream

使用處理流包裝底層的節點流後,只要關閉最上層的處理流即可,系統會自動關閉節點流

 

15.4.2 輸入/輸出流體系

管道流主要是用來實現線程之間的通信問題

緩存流 可以提高輸入輸出效率,增加緩衝流功能後需要使用flush()纔可以將緩衝區的內容寫入實際的物理節點

15.4.3 轉換流

輸入輸出流體系中還提供了兩個轉換流,這兩個轉換流用於實現將字節流轉換成字符流

InputStreamReader將字節輸入流轉換爲字符輸入流,

OutputStreamWriter 將字節輸出流轉換成字符輸出流

 

java 使用 System.in 代表標準輸入,這個標準輸入流式 InputStream類的實例,鍵盤輸入的內容都是文本內容可以使用 InputStreamReader將其轉換成字符流,普通的Reader讀取輸入內容時依然不太方便,可以將Reader再次包裝成BufferedReader 利用BufferedReader可以一次讀取一行內容

 

經常把讀取文本內容的輸入流包裝成BufferedReader,用來方便的讀取輸入流的文本內容

 

15.4.3 推回輸入流

PushbackInputStream PushbackReader 流 這兩個推回輸入流都帶有一個推回緩衝區,當程序調用者兩個流的 unread()方法時,系統會把指定數組的內容推回到該緩衝區 ,而推回輸入流每次調用read()方法總是先從緩衝區讀取,只有完全讀取,但read()還沒裝滿時,纔會從原輸入流讀取

默認緩衝區的長度爲1

15.5 重定向標準輸入輸出流

不在使用鍵盤作爲輸入,不在使用屏幕作爲輸入

System類裏提供了三個重定向方法

15.6 JAVA 虛擬機讀寫其他進程的數據

使用Runtime對象的exec()方法可以運行平臺上的其他程序,該方法產生一個Process對象, Process對象代表由該java程序啓動的java的子進程

子進程讀取程序中的數據 使用的是輸出流

15.7 RandomAccessFile

RandomAccessFilejava輸入輸出體系中最豐富的文件內容訪問類,它提供了衆多方法來訪問文件內容,也可以向文件輸出數據,與普通的輸入輸出流不同的是,RandomAccessFile支持隨機訪問,程序可以直接跳到文件任意地方來讀

如果要想文件後追加內容可以使用RandomAccessFile

RandomAccessFile只能讀取文件,不能讀取其他的io節點

RandomAccessFile對象包含了一個記錄指針,用於標記當前讀寫的位置。

RandomAccessFile不能像文件指定位置插入內容,如果需要插入則可以通過把插入點後的內容輸入緩衝區,插入完畢後再將緩衝區內的內容寫到文件後面

 

多線程斷點的網絡下載工具就可以通過RandomAccessFile類來實現

 

15.8 對象系列化

對象系列化的目標是將對象保存在磁盤中,或允許在網絡中直接傳輸對象。對象系列化機制允許吧內存中的java對象轉換成平臺無關的二進制流 通過網絡將這種二進制流傳輸到另一個網絡節點。其他程序一旦獲得了這種二進制流,都可以將這種二進制流恢復成原來的java對象

15.8.1 系列化的含義和意義

系列化機制允許將實現系列化的java對象轉換爲字節系列,這些字節系列可以保存在磁盤上,或通過網絡傳輸,以備以後重新恢復成原來的對象,系列化機制使得對象可以脫離程序的運行而獨立存在,

所有可能在網絡上傳播的對象的類都應該是可系列化的

15.8.2 使用對象流實現系列化

通過實現Serializable接口來實現對象系列化,程序可以通過如下兩個步驟來實現對象系列化

1.創建一個ObjectOutputStream 這個輸出流是一個處理流

2.調用 ObjectOutputStream對象的writeObject()方法輸出可系列化的對象

 

反系列化步驟

1.創建一個ObjectInputStream輸入流,這個輸入流是一個處理流

2.調用ObjectInputStream對象的readObject() 方法讀取流中的對象

 

反序列化讀取的僅僅是java對象的數據,不是java類,因此採用反序列化恢復java對象時,必須提供對象所屬於的class對象,

 

反系列化機制無需通過構造器來初始化java對象

 

如果使用系列化機制想文件中寫入多個java對象,使用反系列機制恢復對象時,必須按實際寫的順序讀取

 

當一個可系列化對象有多個父類時,這些父類要麼有無參數的構造器要麼也是可系列化的

 

15.8.3 對象引用的系列化

如果系列化類引用的對象沒有系列化,那麼該類也無法系列化

程序系列化算法 如下

1.所有保存在磁盤中的對象都有一個系列化編號

2.當程序試圖系列化一個對象時,程序將先檢查對象是否已經被系列化過,如果該對象從未被系列化過,系統纔會將對象轉化爲字節系列並輸出

3.如果對象已經系列化過,程序將只是直接輸出一個系列化編號

 

15.9 NIO

15.9.1  java IO

IO採用內存映射文件的方式來處理輸入輸出,新IO將文件或文件的一段區域映射到內存中,這樣就可以像訪問內存一樣訪問文件了,模擬了操作系統上的虛擬內存的概念,

 

Channel(通道)和Buffer(緩存) 是NIO的兩個核心對象,Channel是對傳統的輸入輸出系統的模擬,在NIO系統中國所有數據都需要通過通道傳輸,與傳統的輸入輸出系統模擬,Channel與傳統的InputStream OutputStream(面向流輸的處理)最大的區別是提供了一個map()方法,通過該方法可以直接將一塊數據映射到內存中(面向塊的處理)

 

Buffer可以理解爲一個容器,本質是一個數組,發送到Channel中的所有對象都必須首先放到Buffer中,從Channel讀取數據也必須先放到Buffer

 

NIO還提供了一個用於將Unicode字符串映射成字節系列以及逆映射操作的Charset類,也提供了用於支持非阻塞式輸入輸出的Selector

 

Buffer中有三個重要的概念 容量 界限 和位置

 

Buffer中有兩個重要方法 flip() 將limit設置爲position所在位置,並將position設爲0 爲從Buffer中取出數據做好準備

clear()  position置爲0,將limit置爲capacity

 

put get來訪問Buffer中的數據時,分爲相對和絕對兩種

相對 從buffer中的當前position出開始讀寫,然後將position的值按處理元素的個數增加

絕對 直接根據索引執行Buffer中讀取或寫入數據 不影響position的值

 

通過allocate() 方法創建的Buffer對象是普通的Buffer

ByteBuffer還提供了一個allocateDirect()方法來創建直接Buffer

直接創建Buffer的成本很高,所以只適用於長生存期的buffer

 

15.9.3 使用Channel

Channel類似於傳統的流對象,但與傳統的流對象有兩個主要區別

1. Channel 可以直接將指定文件的部分或者全部直接映射成Buffer

2. 程序不能直接訪問Channel中的數據,只能通過Buffer進行交互

 

所有的Channel都不應該直接通過構造器直接創建,而是通過傳統的節點InputStream...getChannel()方法來獲取,

 

Channel中最常用的三類方法是map() read() write() 其中map()方法用於將Channel對應

的部分或者全部數據映射成ByteBuffer  read() write() 用來讀取數據

 

15.9.4 字符集和Charset

newDecoder() 代表該Charset 解碼器

newEncoder() 代表該Charset 編碼器

 

15.9.5 文件鎖

文件鎖可以有效的阻止多個進程併發修改同一個文件

NIOjava提供了FileLock來支持文件鎖定功能,在FileChannel中提供的lock()/tryLock()方法可以獲得文件鎖FileLock對象  

lock() 當試圖鎖定某一個文件時,如果無法得到文件鎖,程序將一直阻塞,

try lock() 是嘗試鎖定文件,它將直接返回而不是阻塞,如果獲得文件鎖,則返回文件鎖,否則返回null

 

文件鎖雖然可以用於控制併發訪問,但是對於高併發訪問的情景,還是推薦使用數據庫來保存程序信息

 

文件鎖還需要指出的幾點

1. 在某一些平臺上,文件鎖僅是建議性的(即使一個文件不能獲得文件鎖,它也可以讀寫)

2. 在某一些平臺上,不能同步的鎖定一個文件並把它映射到內存中

3. 文件鎖是由java虛擬機所持有的,如果兩個java程序使用同一個java虛擬機允許,則他們不能對同時同一個文件進行加鎖

4. 在某一些平臺上關閉FileChannel時,會釋放jvm 在該文件中的所有鎖

 

15.10 java 7 NIO.2

java 7對原有的NIO進行了重大的改進,主要有

1. 提供了全面的文件IO和文件系統訪問支持

2. 基於異步的Channel IO

15.10.1 Path  Paths  Files核心API

15.10.2 使用FileVisitor遍歷文件和目錄

在以前的java版本中,如果要遍歷指定目錄下的所有文件和子類目錄,只能使用遞歸進行遍歷,但這種方式不僅複雜,而且性能也不高

Files 工具類提供了更方便的方法遍歷文件和子目錄

 

15.10.3 使用WatchService 監控文件的變化

java之前的版本如果程序需要監視文件的變化,則可以考慮啓動一條後臺線程,這條後臺線程每隔一段時間去遍歷一次指定目錄的文件,如果和上次遍歷結果不一樣就認爲發生了變化,但這種方式不僅十分繁瑣,而且性能也不高

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章