亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > Java > 正文

Java 編程要點之 I/O 流詳解

2019-11-11 06:04:12
字體:
來源:轉載
供稿:網友

學習java的同學注意了?。。?nbsp;學習過程中遇到什么問題或者想獲取學習資源的話,歡迎加入Java學習交流群,群號碼:183993990  我們一起學Java!

本文詳細介紹了 Java I/O 流的基礎用法和原理。

字節流(Byte Streams)

字節流處理原始的二進制數據 I/O。輸入輸出的是8位字節,相關的類為 InputStream 和 OutputStream.

字節流的類有許多。為了演示字節流的工作,我們將重點放在文件 I/O字節流 FileInputStream 和 FileOutputStream上。其他種類的字節流用法類似,主要區別在于它們構造的方式,大家可以舉一反三。

用法

下面一例子 CopyBytes, 從 xanadu.txt 文件復制到 outagain.txt,每次只復制一個字節:

public class CopyBytes { /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { FileInputStream in = null; FileOutputStream out = null; try { in = new FileInputStream("resources/xanadu.txt"); out = new FileOutputStream("resources/outagain.txt"); int c; while ((c = in.read()) != -1) { out.write(c); } } finally { if (in != null) { in.close(); } if (out != null) { out.close(); } } }}

CopyBytes 花費其大部分時間在簡單的循環里面,從輸入流每次讀取一個字節到輸出流,如圖所示:

記得始終關閉流

不再需要一個流記得要關閉它,這點很重要。所以,CopyBytes 使用 finally 塊來保證即使發生錯誤兩個流還是能被關閉。這種做法有助于避免嚴重的資源泄漏。

一個可能的錯誤是,CopyBytes 無法打開一個或兩個文件。當發生這種情況,對應解決方案是判斷該文件的流是否是其初始 null 值。這就是為什么 CopyBytes 可以確保每個流變量在調用前都包含了一個對象的引用。

何時不使用字節流

CopyBytes 似乎是一個正常的程序,但它實際上代表了一種低級別的 I/O,你應該避免。因為 xanadu.txt 包含字符數據時,最好的方法是使用字符流,下文會有討論。字節流應只用于最原始的 I/O。所有其他流類型是建立在字節流之上的。

字符流(Character Streams)

字符流處理字符數據的 I/O,自動處理與本地字符集轉化。

Java 平臺存儲字符值使用 Unicode 約定。字符流 I/O 會自動將這個內部格式與本地字符集進行轉換。在西方的語言環境中,本地字符集通常是 ASCII 的8位超集。

對于大多數應用,字符流的 I/O 不會比 字節流 I/O操作復雜。輸入和輸出流的類與本地字符集進行自動轉換。使用字符的程序來代替字節流可以自動適應本地字符集,并可以準備國際化,而這完全不需要程序員額外的工作。

如果國際化不是一個優先事項,你可以簡單地使用字符流類,而不必太注意字符集問題。以后,如果國際化成為當務之急,你的程序可以方便適應這種需求的擴展。見國際化獲取更多信息。

用法

字符流類描述在 Reader 和 Writer。而對應文件 I/O ,在 FileReader 和 FileWriter,下面是一個 CopyCharacters 例子:

public class CopyCharacters { /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { FileReader inputStream = null; FileWriter outputStream = null; try { inputStream = new FileReader("resources/xanadu.txt"); outputStream = new FileWriter("resources/characteroutput.txt"); int c; while ((c = inputStream.read()) != -1) { outputStream.write(c); } } finally { if (inputStream != null) { inputStream.close(); } if (outputStream != null) { outputStream.close(); } } }}

CopyCharacters 與 CopyBytes 是非常相似的。最重要的區別在于 CopyCharacters 使用的 FileReader 和 FileWriter 用于輸入輸出,而 CopyBytes 使用 FileInputStream 和FileOutputStream 中的。請注意,這兩個CopyBytes和CopyCharacters使用int變量來讀取和寫入;在 CopyCharacters,int 變量保存在其最后的16位字符值;在 CopyBytes,int 變量保存在其最后的8位字節的值。

字符流使用字節流

字符流往往是對字節流的“包裝”。字符流使用字節流來執行物理I/O,同時字符流處理字符和字節之間的轉換。例如,FileReader 使用 FileInputStream,而 FileWriter使用的是 FileOutputStream。

有兩種通用的字節到字符的“橋梁”流:InputStreamReader 和 OutputStreamWriter。當沒有預包裝的字符流類時,使用它們來創建字符流。在 socket 章節中將展示該用法。

面向行的 I/O

字符 I/O 通常發生在較大的單位不是單個字符。一個常用的單位是行:用行結束符結尾。行結束符可以是回車/換行序列(“/r/n”),一個回車(“/r”),或一個換行符(“/n”)。支持所有可能的行結束符,程序可以讀取任何廣泛使用的操作系統創建的文本文件。

修改 CopyCharacters 來演示如使用面向行的 I/O。要做到這一點,我們必須使用兩個類,BufferedReader 和PRintWriter 的。我們會在緩沖 I/O 和Formatting 章節更加深入地研究這些類。

該 CopyLines 示例調用 BufferedReader.readLine 和 PrintWriter.println 同時做一行的輸入和輸出。

public class CopyLines { /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { BufferedReader inputStream = null; PrintWriter outputStream = null; try { inputStream = new BufferedReader(new FileReader("resources/xanadu.txt")); outputStream = new PrintWriter(new FileWriter("resources/characteroutput.txt")); String l; while ((l = inputStream.readLine()) != null) { outputStream.println(l); } } finally { if (inputStream != null) { inputStream.close(); } if (outputStream != null) { outputStream.close(); } } }}

調用 readLine 按行返回文本行。CopyLines 使用 println 輸出帶有當前操作系統的行終止符的每一行。這可能與輸入文件中不是使用相同的行終止符。

除字符和行之外,有許多方法來構造文本的輸入和輸出。欲了解更多信息,請參閱 Scanning 和 Formatting。

緩沖流(Buffered Streams)

緩沖流通過減少調用本地 API 的次數來優化的輸入和輸出。

目前為止,大多數時候我們到看到使用非緩沖 I/O 的例子。這意味著每次讀或寫請求是由基礎 OS 直接處理。這可以使一個程序效率低得多,因為每個這樣的請求通常引發磁盤訪問,網絡活動,或一些其它的操作,而這些是相對昂貴的。

為了減少這種開銷,所以 Java 平臺實現緩沖 I/O 流。緩沖輸入流從被稱為緩沖區(buffer)的存儲器區域讀出數據;僅當緩沖區是空時,本地輸入 API 才被調用。同樣,緩沖輸出流,將數據寫入到緩存區,只有當緩沖區已滿才調用本機輸出 API。

程序可以轉換的非緩沖流為緩沖流,這里用非緩沖流對象傳遞給緩沖流類的構造器。

inputStream = new BufferedReader(new FileReader("xanadu.txt"));outputStream = new BufferedWriter(new FileWriter("characteroutput.txt"));

用于包裝非緩存流的緩沖流類有4個:BufferedInputStream 和 BufferedOutputStream 用于創建字節緩沖字節流,BufferedReader 和 BufferedWriter 用于創建字符緩沖字節流。

刷新緩沖流

刷新緩沖區是指在某個緩沖的關鍵點就可以將緩沖輸出,而不必等待它填滿。

一些緩沖輸出類通過一個可選的構造函數參數支持 autoflush(自動刷新)。當自動刷新開啟,某些關鍵事件會導致緩沖區被刷新。例如,自動刷新 PrintWriter 對象在每次調用 println 或者 format 時刷新緩沖區。查看 Formatting 了解更多關于這些的方法。

如果要手動刷新流,請調用其 flush 方法。flush 方法可以用于任何輸出流,但對非緩沖流是沒有效果的。

掃描(Scanning)和格式化(Formatting)

掃描和格式化允許程序讀取和寫入格式化的文本。

I/O 編程通常涉及對人類喜歡的整齊的格式化數據進行轉換。為了幫助您與這些瑣事,Java 平臺提供了兩個API。scanning API 使用分隔符模式將其輸入分解為標記。formatting API 將數據重新組合成格式良好的,人類可讀的形式。

掃描

將其輸入分解為標記

默認情況下,Scanner 使用空格字符分隔標記。(空格字符包括空格,制表符和行終止符。為完整列表,請參閱Character.isWhitespace)。示例 ScanXan 讀取 xanadu.txt 的單個詞語并打印他們:

public class ScanXan { /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { Scanner s = null; try { s = new Scanner(new BufferedReader(new FileReader("resources/xanadu.txt"))); while (s.hasNext()) { System.out.println(s.next()); } } finally { if (s != null) { s.close(); } } }}

雖然 Scanner 不是流,但你仍然需要關閉它,以表明你與它的底層流執行完成。

調用 useDelimiter() ,指定一個正則表達式可以使用不同的標記分隔符。例如,假設您想要標記分隔符是一個逗號,后面可以跟空格。你會調用

s.useDelimiter(",//s*");

轉換成獨立標記

該 ScanXan 示例是將所有的輸入標記為簡單的字符串值。Scanner 還支持所有的 Java 語言的基本類型(除 char),以及 BigInteger 和 BigDecimal 的。此外,數字值可以使用千位分隔符。因此,在一個美國的區域設置,Scanner 能正確地讀出字符串“32,767”作為一個整數值。

這里要注意的是語言環境,因為千位分隔符和小數點符號是特定于語言環境。所以,下面的例子將無法正常在所有的語言環境中,如果我們沒有指定 scanner 應該用在美國地區工作??赡苣闫綍r并不用關心,因為你輸入的數據通常來自使用相同的語言環境??梢允褂孟旅娴恼Z句來設置語言環境:

s.useLocale(Locale.US);

該 ScanSum 示例是將讀取的 double 值列表進行相加:

public class ScanSum { /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { Scanner s = null; double sum = 0; try { s = new Scanner(new BufferedReader(new FileReader("resources/usnumbers.txt"))); s.useLocale(Locale.US); while (s.hasNext()) { if (s.hasNextDouble()) { sum += s.nextDouble(); } else { s.next(); } } } finally { s.close(); } System.out.println(sum); }}

輸出為:1032778.74159

格式化

實現格式化流對象要么是 字符流類的 PrintWriter 的實例,或為字節流類的 PrintStream 的實例。

注:對于 PrintStream 對象,你很可能只需要 System.out 和 System.err。 (請參閱命令行I/O)當你需要創建一個格式化的輸出流,請實例化 PrintWriter,而不是 PrintStream。

像所有的字節和字符流對象一樣,PrintStream 和 PrintWriter 的實例實現了一套標準的 write 方法用于簡單的字節和字符輸出。此外,PrintStream 和 PrintWriter 的執行同一套方法,將內部數據轉換成格式化輸出。提供了兩個級別的格式:

print 和 println 在一個標準的方式里面格式化獨立的值 。format 用于格式化幾乎任何數量的格式字符串值,且具有多種精確選擇。

print 和 println 方法

調用 print 或 println 輸出使用適當 toString 方法變換后的值的單一值。我們可以看到這 Root 例子:

public class Root { /** * @param args */ public static void main(String[] args) { int i = 2; double r = Math.sqrt(i); System.out.print("The square root of "); System.out.print(i); System.out.print(" is "); System.out.print(r); System.out.println("."); i = 5; r = Math.sqrt(i); System.out.println("The square root of " + i + " is " + r + "."); }}

輸出為:

The square root of 2 is 1.4142135623730951.The square root of 5 is 2.23606797749979.

在 i 和 r 變量格式化了兩次:第一次在重載的 print 使用代碼,第二次是由Java編譯器轉換碼自動生成,它也利用了 toString。您可以用這種方式格式化任意值,但對于結果沒有太多的控制權。

format 方法

該 format 方法用于格式化基于 format string(格式字符串) 多參。格式字符串包含嵌入了 format specifiers (格式說明)的靜態文本;除非使用了格式說明,否則格式字符串輸出不變。

格式字符串支持許多功能。在本教程中,我們只介紹一些基礎知識。有關完整說明,請參閱 API 規范關于格式字符串語法。

Root2 示例在一個 format 調用里面設置兩個值:

public class Root2 { /** * @param args */ public static void main(String[] args) { int i = 2; double r = Math.sqrt(i); System.out.format("The square root of %d is %f.%n", i, r); }}

輸出為:The square root of 2 is 1.414214.

像本例中所使用的格式為:

d 格式化整數值為小數f 格式化浮點值作為小數n 輸出特定于平臺的行終止符。

這里有一些其他的轉換格式:

x 格式化整數為十六進制值s 格式化任何值作為字符串tB 格式化整數作為一個語言環境特定的月份名稱。

還有許多其他的轉換。

注意:除了 %% 和 %n,其他格式符都要匹配參數,否則拋出異常。在 Java 編程語言中,/ n轉義總是產生換行符(/u000A)。不要使用/?除非你特別想要一個換行符。為了針對本地平臺得到正確的行分隔符,請使用%n。

除了用于轉換,格式說明符可以包含若干附加的元素,進一步定制格式化輸出。下面是一個 Format 例子,使用一切可能的一種元素。

public class Format { /** * @param args */ public static void main(String[] args) { System.out.format("%f, %1$+020.10f %n", Math.PI); }}

輸出為:3.141593, +00000003.1415926536

附加元素都是可選的。下圖顯示了長格式符是如何分解成元素

元件必須出現在顯示的順序。從合適的工作,可選的元素是:

Precision(精確)。對于浮點值,這是格式化值的數學精度。對于 s 和其他一般的轉換,這是格式化值的最大寬度;該值右截斷,如果有必要的。Width(寬度)。格式化值的最小寬度;如有必要,該值被填充。默認值是左用空格填充。Flags(標志)指定附加格式設置選項。在 Format 示例中,+ 標志指定的數量應始終標志格式,以及0標志指定0是填充字符。其他的標志包括 - (墊右側)和(與區域特定的千位分隔符格式號)。請注意,某些標志不能與某些其他標志或與某些轉換使用。Argument Index(參數索引)允許您指定的參數明確匹配。您還可以指定<到相同的參數作為前面的說明一致。這樣的例子可以說:System.out.format(“%F,%<+ 020.10f%N”,Math.PI);

命令行 I/O

命令行 I/O 描述了標準流(Standard Streams)和控制臺(Console)對象。

Java 支持兩種交互方式:標準流(Standard Streams)和通過控制臺(Console)。

標準流

標準流是許多操作系統的一項功能。默認情況下,他們從鍵盤讀取輸入和寫出到顯示器。它們還支持對文件和程序之間的 I/O,但該功能是通過命令行解釋器,而不是由程序控制。

Java平臺支持三種標準流:標準輸入(Standard Input, 通過 System.in 訪問)、標準輸出(Standard Output, 通過System.out 的訪問)和標準錯誤( Standard Error, 通過System.err的訪問)。這些對象被自動定義,并不需要被打開。標準輸出和標準錯誤都用于輸出;錯誤輸出允許用戶轉移經常性的輸出到一個文件中,仍然能夠讀取錯誤消息。

您可能希望標準流是字符流,但是,由于歷史的原因,他們是字節流。 System.out 和System.err 定義為PrintStream 的對象。雖然這在技術上是一個字節流,PrintStream 利用內部字符流對象來模擬多種字符流的功能。

相比之下,System.in 是一個沒有字符流功能的字節流。若要想將標準的輸入作為字符流,可以包裝 System.in 在 InputStreamReader

InputStreamReader cin = new InputStreamReader(System.in);

Console (控制臺)

更先進的替代標準流的是 Console 。這個單一,預定義的 Console 類型的對象,有大部分的標準流提供的功能,另外還有其他功能。Console 對于安全的密碼輸入特別有用。Console 對象還提供了真正的輸入輸出字符流,是通過 reader 和 writer 方法實現的。

若程序想使用 Console ,它必須嘗試通過調用 System.console() 檢索 Console 對象。如果 Console 對象存在,通過此方法將其返回。如果返回 NULL,則 Console 操作是不允許的,要么是因為操作系統不支持他們或者是因為程序本身是在非交互環境中啟動的。

Console 對象支持通過讀取密碼的方法安全輸入密碼。該方法有助于在兩個方面的安全。第一,它抑制回應,因此密碼在用戶的屏幕是不可見的。第二,readPassWord 返回一個字符數組,而不是字符串,所以,密碼可以被覆蓋,只要它是不再需要就可以從存儲器中刪除。

Password 例子是一個展示了更改用戶的密碼原型程序。它演示了幾種 Console 方法

public class Password { /** * @param args */ public static void main(String[] args) { Console c = System.console(); if (c == null) { System.err.println("No console."); System.exit(1); } String login = c.readLine("Enter your login: "); char [] oldPassword = c.readPassword("Enter your old password: "); if (verify(login, oldPassword)) { boolean noMatch; do { char [] newPassword1 = c.readPassword("Enter your new password: "); char [] newPassword2 = c.readPassword("Enter new password again: "); noMatch = ! Arrays.equals(newPassword1, newPassword2); if (noMatch) { c.format("Passwords don't match. Try again.%n"); } else { change(login, newPassword1); c.format("Password for %s changed.%n", login); } Arrays.fill(newPassword1, ' '); Arrays.fill(newPassword2, ' '); } while (noMatch); } Arrays.fill(oldPassword, ' '); } // Dummy change method. static boolean verify(String login, char[] password) { // This method always returns // true in this example. // Modify this method to verify // password according to your rules. return true; } // Dummy change method. static void change(String login, char[] password) { // Modify this method to change // password according to your rules. }}

上面的流程是:

嘗試檢索 Console 對象。如果對象是不可用,中止。調用 Console.readLine 提示并讀取用戶的登錄名。調用 Console.readPassword 提示并讀取用戶的現有密碼。調用 verify 確認該用戶被授權可以改變密碼。(在本例中,假設 verify 是總是返回true )重復下列步驟,直到用戶輸入的密碼相同兩次:調用 Console.readPassword 兩次提示和讀一個新的密碼。如果用戶輸入的密碼兩次,調用 change 去改變它。 (同樣,change 是一個虛擬的方法)用空格覆蓋這兩個密碼。用空格覆蓋舊的密碼。

數據流(Data Streams)

Data Streams 處理原始數據類型和字符串值的二進制 I/O。

支持基本數據類型的值((boolean, char, byte, short, int, long, float, 和 double)以及字符串值的二進制 I/O。所有數據流實現 DataInput 或 DataOutput 接口。本節重點介紹這些接口的廣泛使用的實現,DataInputStream 和DataOutputStream 類。

DataStreams 例子展示了數據流通過寫出的一組數據記錄到文件,然后再次從文件中讀取這些記錄。每個記錄包括涉及在發票上的項目,如下表中三個值:

記錄中順序數據類型數據描述輸出方法輸入方法示例值
1doubleItem priceDataOutputStream.writeDoubleDataInputStream.readDouble19.99
2intUnit countDataOutputStream.writeIntDataInputStream.readInt12
3StringItem descriptionDataOutputStream.writeUTFDataInputStream.readUTF"Java T-Shirt"

首先,定義了幾個常量,數據文件的名稱,以及數據。

static final String dataFile = "invoicedata";static final double[] prices = { 19.99, 9.99, 15.99, 3.99, 4.99 };static final int[] units = { 12, 8, 13, 29, 50 };static final String[] descs = { "Java T-shirt", "Java Mug", "Duke Juggling Dolls", "Java Pin", "Java Key Chain"};

DataStreams 打開一個輸出流,提供一個緩沖的文件輸出字節流:

out = new DataOutputStream(new BufferedOutputStream( new FileOutputStream(dataFile)))

DataStreams 寫出記錄并關閉輸出流:

for (int i = 0; i < prices.length; i ++) { out.writeDouble(prices[i]); out.writeInt(units[i]); out.writeUTF(descs[i]);}

該 writeUTF 方法寫出以 UTF-8 改進形式的字符串值。

現在,DataStreams 讀回數據。首先,它必須提供一個輸入流,和變量來保存的輸入數據。像 DataOutputStream 、DataInputStream 類,必須構造成一個字節流的包裝器。

in = new DataInputStream(new BufferedInputStream(new FileInputStream(dataFile)));double price;int unit;String desc;double total = 0.0;

現在,DataStreams 可以讀取流里面的每個記錄,并在遇到它時將數據報告出來:

try { while (true) { price = in.readDouble(); unit = in.readInt(); desc = in.readUTF(); System.out.format("You ordered %d" + " units of %s at $%.2f%n", unit, desc, price); total += unit * price; }} catch (EOFException e) {}

請注意,DataStreams 通過捕獲 EOFException 檢測文件結束的條件而不是測試無效的返回值。所有實現了 DataInput 的方法都使用 EOFException 類來代替返回值。

還要注意的是 DataStreams 中的各個 write 需要匹配對應相應的 read。它需要由程序員來保證。

DataStreams 使用了一個非常糟糕的編程技術:它使用浮點數來表示的貨幣價值。在一般情況下,浮點數是不好的精確數值。這對小數尤其糟糕,因為共同值(如 0.1),沒有一個二進制的表示。

正確的類型用于貨幣值是 java.math.BigDecimal 的。不幸的是,BigDecimal 是一個對象的類型,因此它不能與數據流工作。然而,BigDecimal 將與對象流工作,而這部分內容將在下一節講解。

對象流(Object Streams)

對象流處理對象的二進制 I/O。

正如數據流支持的是基本數據類型的 I/O,對象流支持的對象 I/O。大多數,但不是全部,標準類支持他們的對象的序列化,都需要實現 Serializable 接口。

對象流類包括 ObjectInputStream 和 ObjectOutputStream 的。這些類實現的 ObjectInput 與 ObjectOutput 的,這些都是 DataInput 和DataOutput 的子接口。這意味著,所有包含在數據流中的基本數據類型 I/O 方法也在對象流中實現了。這樣一個對象流可以包含基本數據類型值和對象值的混合。該ObjectStreams 例子說明了這一點。ObjectStreams 創建與 DataStreams 相同的應用程序。首先,價格現在是 BigDecimal 對象,以更好地代表分數值。其次,Calendar 對象被寫入到數據文件中,指示發票日期。

public class ObjectStreams { static final String dataFile = "invoicedata"; static final BigDecimal[] prices = { new BigDecimal("19.99"), new BigDecimal("9.99"), new BigDecimal("15.99"), new BigDecimal("3.99"), new BigDecimal("4.99") }; static final int[] units = { 12, 8, 13, 29, 50 }; static final String[] descs = { "Java T-shirt", "Java Mug", "Duke Juggling Dolls", "Java Pin", "Java Key Chain" }; public static void main(String[] args) throws IOException, ClassNotFoundException { ObjectOutputStream out = null; try { out = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(dataFile))); out.writeObject(Calendar.getInstance()); for (int i = 0; i < prices.length; i ++) { out.writeObject(prices[i]); out.writeInt(units[i]); out.writeUTF(descs[i]); } } finally { out.close(); } ObjectInputStream in = null; try { in = new ObjectInputStream(new BufferedInputStream(new FileInputStream(dataFile))); Calendar date = null; BigDecimal price; int unit; String desc; BigDecimal total = new BigDecimal(0); date = (Calendar) in.readObject(); System.out.format ("On %tA, %<tB %<te, %<tY:%n", date); try { while (true) { price = (BigDecimal) in.readObject(); unit = in.readInt(); desc = in.readUTF(); System.out.format("You ordered %d units of %s at $%.2f%n", unit, desc, price); total = total.add(price.multiply(new BigDecimal(unit))); } } catch (EOFException e) {} System.out.format("For a TOTAL of: $%.2f%n", total); } finally { in.close(); } }}

如果的 readObject() 不返回預期的對象類型,試圖將它轉換為正確的類型可能會拋出一個ClassNotFoundException。在這個簡單的例子,這是不可能發生的,所以我們不要試圖捕獲異常。相反,我們通知編譯器,我們已經意識到這個問題,添加 ClassNotFoundException 到主方法的 throws 子句中的。

復雜對象的 I/O

writeObject 和 readObject 方法簡單易用,但它們包含了一些非常復雜的對象管理邏輯。這不像 Calendar 類,它只是封裝了原始值。但許多對象包含其他對象的引用。如果 readObject 從流重構一個對象,它必須能夠重建所有的原始對象所引用的對象。這些額外的對象可能有他們自己的引用,依此類推。在這種情況下,writeObject 遍歷對象引用的整個網絡,并將該網絡中的所有對象寫入流。因此,writeObject 單個調用可以導致大量的對象被寫入流。

如下圖所示,其中 writeObject 調用名為 a 的單個對象。這個對象包含對象的引用 b和 c,而 b 包含引用 d 和 e。調用 writeObject(a) 寫入的不只是一個 a,還包括所有需要重新構成的這個網絡中的其他4個對象。當通過 readObject 讀回 a 時,其他四個對象也被讀回,同時,所有的原始對象的引用被保留。

如果在同一個流的兩個對象引用了同一個對象會發生什么?流只包含一個對象的一個拷貝,盡管它可以包含任何數量的對它的引用。因此,如果你明確地寫一個對象到流兩次,實際上只是寫入了2此引用。例如,如果下面的代碼寫入一個對象 ob 兩次到流:

Object ob = new Object();out.writeObject(ob);out.writeObject(ob);

每個 writeObject 都對應一個 readObject, 所以從流里面讀回的代碼如下:

Object ob1 = in.readObject();Object ob2 = in.readObject();

ob1 和 ob2 都是相同對象的引用。

然而,如果一個單獨的對象被寫入到兩個不同的數據流,它被有效地復用 - 一個程序從兩個流讀回的將是兩個不同的對象。

學習Java的同學注意了?。?! 學習過程中遇到什么問題或者想獲取學習資源的話,歡迎加入Java學習交流群,群號碼:183993990  我們一起學Java!


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美福利视频网站| 中文字幕欧美精品日韩中文字幕| 一区二区三区四区视频| 成人免费在线视频网址| 亚洲大尺度美女在线| 国产91免费看片| 国产成+人+综合+亚洲欧洲| 日韩精品高清在线| 亚洲欧洲第一视频| 亚洲va电影大全| 国产精品最新在线观看| 欧美一级在线播放| 国产精品久久久久久久久| 国产精品一二三视频| 精品毛片三在线观看| 亚洲午夜精品久久久久久久久久久久| 成人黄色大片在线免费观看| 欧美成人午夜免费视在线看片| 久久久精品在线| 日韩中文在线不卡| 久久综合电影一区| 欧美乱妇高清无乱码| 91国产在线精品| 欧美视频免费在线观看| 久久久国产视频| 国产精品都在这里| 欧美日韩成人在线播放| 亚洲性av在线| 国产中文欧美精品| 日韩av中文字幕在线播放| 国产69精品99久久久久久宅男| 欧美一级视频免费在线观看| 国产人妖伪娘一区91| 国产精品国产三级国产专播精品人| 亚洲激情国产精品| 久久五月情影视| 欧美日本啪啪无遮挡网站| 日韩免费av片在线观看| 欧美成人精品不卡视频在线观看| 欧美精品做受xxx性少妇| 免费成人高清视频| 色伦专区97中文字幕| 欧美大片免费观看在线观看网站推荐| 亚洲精品第一页| 成人免费午夜电影| 欧美日韩国产中字| 欧美激情日韩图片| 亚洲男人的天堂在线播放| 欧美最顶级丰满的aⅴ艳星| 7777免费精品视频| 国产精品美女在线| 尤物精品国产第一福利三区| 欧美影院久久久| 亚洲а∨天堂久久精品喷水| 国产一区二区三区三区在线观看| 日本电影亚洲天堂| 亚洲精品国产品国语在线| 国产精品日韩在线播放| 欧美成人一二三| 狠狠躁天天躁日日躁欧美| 日产精品99久久久久久| 日韩成人中文电影| 久久av红桃一区二区小说| 777午夜精品福利在线观看| 色一区av在线| 欧洲s码亚洲m码精品一区| 色av中文字幕一区| 亚洲天堂成人在线视频| 91美女片黄在线观看游戏| 久久精品国产69国产精品亚洲| 成人在线播放av| 国产精品久久婷婷六月丁香| 国产精品久久久久999| 亚洲国产欧美一区二区三区久久| 日韩电影在线观看中文字幕| 欧美富婆性猛交| 国产精品成人品| 国产精品扒开腿做| 国产精品久久久av| 欧美激情视频播放| 亚洲乱码国产乱码精品精天堂| 97色伦亚洲国产| 亚洲人成在线观看| 精品国产一区二区在线| 中文字幕av一区二区三区谷原希美| 久久久综合免费视频| 国产乱人伦真实精品视频| 成人福利视频网| 国产精品久久久久久久av大片| 九九九久久国产免费| 欧美日韩在线视频首页| 日本一区二区三区在线播放| 久久国产精品久久久久久| 亚洲成成品网站| 久久国产精品首页| 国产精品嫩草视频| 九九久久久久久久久激情| 91po在线观看91精品国产性色| 性欧美暴力猛交69hd| 91精品国产综合久久久久久蜜臀| 亚洲成色www8888| 国产日韩精品一区二区| 欧美福利在线观看| 欧美成人免费在线观看| 久久精品国产96久久久香蕉| 久久久久亚洲精品成人网小说| 91在线观看免费高清完整版在线观看| 亚洲精品videossex少妇| 成人a视频在线观看| 亚洲精品白浆高清久久久久久| 中文精品99久久国产香蕉| 在线视频日本亚洲性| 久久免费高清视频| 国产欧美日韩专区发布| 国产一区二区日韩| 一区二区三区无码高清视频| 日韩av综合网站| 亚洲欧美激情四射在线日| 精品欧美一区二区三区| 亚洲一区二区自拍| xxav国产精品美女主播| 国产精品va在线播放| 性色av一区二区咪爱| 亚洲精品一区二区三区不| 亚洲黄色在线观看| 欧美在线激情网| 国产大片精品免费永久看nba| 欧美日韩第一视频| 久久五月天色综合| 久久久久久久久网站| 中文字幕一精品亚洲无线一区| 亚洲美女在线观看| 久久精品成人动漫| 91麻豆国产语对白在线观看| 少妇激情综合网| 国产精品久久精品| www.欧美精品一二三区| 日韩欧美中文第一页| 国产精品手机播放| 亚洲第一页在线| 精品国产一区二区在线| 欧美精品在线极品| 日韩专区在线播放| 亚洲自拍欧美色图| 亚洲最大成人免费视频| 韩国三级电影久久久久久| 欧美午夜丰满在线18影院| 91超碰caoporn97人人| 成人观看高清在线观看免费| 性视频1819p久久| 亚洲国产精品成人va在线观看| 在线观看欧美成人| 成人免费激情视频| 国产精品www网站| 日韩欧美亚洲范冰冰与中字| 在线视频欧美日韩| 欧美做受高潮1| 国产精品久久久久久久av大片| 久久99精品久久久久久青青91| 精品久久久久久久大神国产| 国产亚洲一区二区在线| 日韩精品www| 国产精品久久久久久久久久三级| 亚洲精品国产精品国自产观看浪潮|