IO其實意味著:數據不停地搬入搬出緩沖區而已(使用了緩沖區)。
什么是Java序列化和反序列化,如何實現?
- 把Java對象轉換為字節序列的過程稱為對象的序列化,也就是將對象寫入到IO流中。序列化是為了解決在對對象流進行讀寫操作時所引發的問題。序列化機制允許將實現序列化的Java對象轉換位字節序列,這些字節序列可以保存在磁盤上,或通過網絡傳輸,以達到以后恢復成原來的對象。序列化機制使得對象可以脫離程序的運行而獨立存在。
ps:要對一個對象序列化,這個對象就需要實現Serializable接口,如果這個對象中有一個變量是另一個對象的引用,則引用的對象也要實現Serializable接口,這個過程是遞歸的。Serializable接口中沒有定義任何方法,只是作為一個標記來指示實現該接口的類可以進行序列化。
實現序列化:
- 步驟一:創建一個ObjectOutputStream輸出流;
- 步驟二:調用ObjectOutputStream對象的 writeObject 方法輸出可序列化對象。
序列化只能保存對象的非靜態成員變量,而不能保存任何成員方法和靜態成員變量,并且保存的只是變量的值,變量的修飾符對序列化沒有影響。
有一些對象類不具有可持久化性,因為其數據的特性決定了它會經常變化,其狀態只是瞬時的,這樣的對象是無法保存去狀態的,如Thread對象或流對象。對于這樣的成員變量,必須用 transient 關鍵字標明,否則編譯器將報錯。任何用 transient 關鍵字標明的成員變量,都不會被序列化。
另外,序列化可能涉及將對象存放到磁盤上或在網絡上發送數據,這時會產生安全問題。對于一些需要保密的數據(如用戶密碼等),不應保存在永久介質中,為了保證安全,應在這些變量前加上 transient 關鍵字。
反序列化就是從 IO 流中恢復對象。反序列化實現:
- 步驟一:創建 ObjectInputStream 輸入流
- 步驟二:調用ObjectInputStream對象的readObject()得到序列化的對象。
控制臺只輸出了Person的信息,沒有輸出構造方法中的內容,說明反序列化的對象是由 JVM 自己生成的,不通過構造方法生成。
Java 中 IO 流分為幾種?
按照流的流向分,可以分為輸入流和輸出流;
按照操作單元劃分,可以劃分為字節流和字符流;
按照流的角色劃分為節點流和處理流(包裝流)。
Java IO 流共涉及 40 多個類,這些類看上去很雜亂,但實際上很有規則,而且彼此之間存在非常緊密的聯系, Java I0 流的 40 多個類都是從如下 4 個抽象類基類中派生出來的。
InputStream/Reader: 所有的輸入流的基類,前者是字節輸入流,后者是字符輸入流。
OutputStream/Writer: 所有輸出流的基類,前者是字節輸出流,后者是字符輸出流。
按照操作方式分類結構圖:
按照角色劃分:
既然有了字節流,為什么還要有字符流?區別?
問題本質想問:不管是文件讀寫還是網絡發送接收,信息的最小存儲單元都是字節,那為什么 I/O 流操作要分為字節流操作和字符流操作呢?
因為字符流是由 Java 虛擬機將字節轉換得到的,問題就出在這個過程還算是非常耗時,并且,如果我們不知道編碼類型就很容易出現亂碼問題。所以, I/O 流就干脆提供了一個直接操作字符的接口,方便我們平時對字符進行流操作。如果音頻文件、圖片等媒體文件用字節流比較好,如果涉及到字符的話使用字符流比較好。
總結:性能和使用更加方便兩方面闡述。
如何選擇?
大多數情況下使用字節流會更好,因為大多數時候 IO 操作都是直接操作磁盤文件,所以這些流在傳輸時都是以字節的方式進行的
為了提高性能,針對讀寫對象的不同,字節流可以采用帶緩沖區的BufferedInputStream和BufferedOutputStream,字符流可以采用帶緩沖區的BufferedReader和BufferedWriter。
java.io 包下有哪些流?
字節流和字符流。字節流繼承于InputStream、OutputStream,字符流繼承于Reader、Writer。在java.io包中還有許多其他的流,主要是為了提高性能和使用方便。關于Java的I/O需要注意的有兩點:一是兩種對稱性(輸入和輸出的對稱性,字節和字符的對稱性);二是兩種設計模式(適配器模式和裝潢模式)。
字符流和字節流都有對應的緩沖流,字節流也可以包裝為字符流,緩沖流帶有一個 8KB 的緩沖數組,可以提高流的讀寫效率。除了緩沖流外還有過濾流 FilterReader、字符數組流 CharArrayReader、字節數組流 ByteArrayInputStream、文件流 FileInputStream 等。
ps:BufferedReader屬于處理流中的緩沖流,可以將讀取的內容存在內存里面,有readLine()方法,它,用來讀取一行
什么是節點流,什么是處理流,它們各有什么用處,處理流的創建有什么特征?
- 節點流: 直接與數據源相連,用于輸入或者輸出,常見涉及操作:文件操作、管道操作與數組操作。
- 處理流:在節點流的基礎上對之進行加工,進行一些功能的擴展,常見操作:緩沖操作、對象序列化操作、轉化操作、打印控制與基本數據類型操作
- 處理流的構造器必須要 傳入節點流的子類
什么是緩沖區?有什么作用?
緩沖區就是一段特殊的內存區域,很多情況下當程序需要頻繁地操作一個資源(如文件或數據庫)則性能會很低,所以為了提升性能就可以將一部分數據暫時讀寫到緩存區,以后直接從此區域中讀寫數據即可,這樣就顯著提升了性。
針對讀寫對象的不同,字節流可以采用帶緩沖區的BufferedInputStream和BufferedOutputStream,字符流可以采用帶緩沖區的BufferedReader和BufferedWriter。
流一般需要不需要關閉,如果關閉的話在用什么方法,一般要在那個代碼塊里面關閉比較好,處理流是怎么關閉的,如果有多個流互相調用傳入是怎么關閉的?
- 流一旦打開就必須關閉,使用close方法
- 放入finally語句塊中(finally 語句一定會執行)
- 調用的處理流就關閉處理流
- 多個流互相調用只關閉最外層的流
文件操作使用的流
Filter Stream是一種IO流主要作用是用來對存在的流增加一些額外的功能,像給目標文件增加源文件中不存在的行數,或者增加拷貝的性能。
ps:FileInputStream和FileOutputStream是什么?
這是在拷貝文件操作的時候,經常用到的兩個類。
- 在處理小文件的時候,它們性能表現還不錯(針對字符文件,FileReader/FileWriter)。
- 在大文件的時候,最好使用BufferedInputStream (或 BufferedReader) 和 BufferedOutputStream (或 BufferedWriter)
說說管道流(Piped Stream)
- 有四種管道流, PipedInputStream, PipedOutputStream, PipedReader 和 PipedWriter.
- 在多個線程或進程中傳遞數據的時候管道流非常有用。
鍵盤獲取輸入的兩種方式?
注意:這里的輸入(和輸出)針對的是內存,類似反序列化將對象流還原為對象的,創建輸入流,調用readObject(),寫入內存。
// Scanner:用來包裝System.in流,很方便地將輸入的String字符串轉換成需要的數據類型。
Scanner input = new Scanner(System.in);
String s = input.nextLine();
input.close()
// BufferedReader
BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
String s = input.readLine();
System.out.println()是什么?
PrintStream類的輸出功能非常強大,通常如果需要輸出文本內容,都應該將輸出流包裝成PrintStream后進行輸出。它還提供其他兩項功能。與其他輸出流不同,PrintStream 永遠不會拋出 IOException;而是,異常情況僅設置可通過 checkError 方法測試的內部標志。另外,為了自動刷新,可以創建一個 PrintStream.
println是PrintStream的一個方法。out是一個靜態PrintStream類型的成員變量,System是一個java.lang包中的類,用于和底層的操作系統進行交互。
巨人的肩膀: