【Notes】《Thinking in Java》【Chapter 11】Part II

六、Typical uses of I/O streams

 java.io.;
 java.io.;

 java.io.;
 java.io.;
 java.io.;
 java.io.;
 java.io.;
 java.io.;
 java.io.;
 java.io.;
 java.io.;
 java.io.;
 java.io.;
 java.io.;
 java.io.;
 java.io.;
 java.io.;

 IOStreamDemo {
    
     main([] args)   {
         s = ;
         s2 = ;
        
        
         in =  (
             ("IOStreamDemo.java"));
        ((s = in.readLine()) != )
            s2 += s + "\n";
        in.close();
        
        
         stdin =  (
             ());
        ..print();
        ..println(stdin.readLine());
        
        
         in2 =  (s2);
         c;
        ((c = in2.read()) != -1)
            ..print(() c);
            
        
         {
             in3 =  (
                 (s2.getBytes()));
            
            ()
                ..print(() in3.readByte());
        } ( e) {
            ..println();
        }
        
        
         {
             in4 =  (
                 (s2));
                
             out1 =  (
                 ( ()));
            
             lineCount = 1;
            
            ((s = in4.readLine()) != )
                out1.println(lineCount++ +  + s);
               
            out1.close();
        } ( e) {
            ..println();
        }
        
        
         {
             out2 =  (
                 ( ()));
                
            out2.writeDouble(3.14159);
            out2.writeChars();
            out2.writeBytes();
            out2.close();
            
             in5 =  (
                 ( ()));
             in5br =  (
                 (in5));
            
            
            ..println(in5.readDouble());
            
            ..println(in5br.readLine());
            
            ..println(in5br.readLine());
        } ( e) {
            ..println();
        }
        
        
         rf =  (, );
        ( i = 0; i < 10; i++)
            rf.writeDouble(i * 1.414);
        rf.close();
        
        rf =  (, );
        rf.seek(5 * 8);
        rf.writeDouble(47.0001);
        rf.close();
        
        rf =  (, );
        ( i = 0; i < 10; i++)
            ..println( + i +  + rf.readDouble());
        rf.close();
    }
}

(1)Input streams

  1. Buffered input file
    • 需要開啟一個文件用於字符的讀取時,可以使用 FileReader 並以一個 String 或 File 對象指定文件名。基於速度考量,通常會把這個 FileReader 的 reference 傳給 BufferedReader 的 constructor 使這個文件具備緩衝功能。

    • BufferedReader 的 readLine() 函數會將換行符過濾掉,所以使用它的時候必須自行添加換行符。

    • java.lang.System 的三個 I/O 成員(final static):
      • err:PrintStream

      • out:InputStream

      • in:PrintStream

  2. Input from memory
    • 通過 String(已含內容)產生 StringReader。

    • java.io.Reader(abstract class)的 read() 函數返回的是 int,所以使用 read() 時必須根據實際進行轉型。

  3. Formatted memory input
    • 讀取格式化數據通常使用的是 DataInputStream(byte-oriented),因此也就只能使用 InputStream classes 而不能使用 Reader classes。

    • 任何一個 Byte 對 DataInputStream 的 readByte() 函數來說都是合法的結果,所以不能憑它的返回值來判斷輸入是否結束。

    • DataInputStream 的 available()(繼承自 java.io.FilterInputStream)函數返回可供讀取的字符數。但它的運作方式取決於所輸入的對象,所以應當結合實際使用。

  4. File output
    • 產生一個 FileWriter 對象連接至文件,將其 reference 傳給 BufferedWriter 的 constructor(緩衝可以大幅提高 I/O 效能)。如果需要格式化輸出,可以再將 BufferedWriter 的 object reference 傳給 PrintWriter 的 constructor。(這樣產生的文件是文本文件)

    • 使用 Buffered 類的 classes 緩衝輸出文件後,必須調用 close() 函數關閉文件,否則緩衝區的內容可能不會被清空從而得不到完整的結果。

(2)Output streams

  1. Storing and recovering data
    • output streams 主要分為兩種:1)為了讓人直接查看結果(如 java.io.PrintWriter);2)為了讓 DataInputStream 可以再次讀取(Random Access File 不屬于上述的兩種,但其數據格式與 DataInputStream 和 DataOutputStream 相容)。

    • DataOutputStream 使用 writeChars() 和 writeBytes() 這兩個函數輸出字符串。其中 writeChars() 是以 16bit 的 Unicode 字符進行輸出的,所以如果用 readLine() 讀取這些文本,那麽每個字符間都會多了一個空格,這是因為 Unicode 會額外插入一個 byte。因此,對 ASCII 而言,使用 writeBytes() 加上換行符輸出,然後以 readLine() 讀取的方法會更為簡單。

  2. Random access files
    • java.io.RandomAccessFile 完全獨立於 I/O 繼承體系之外。由於它實現了 DataInput 和 DataOutput 接口,所以它無法和 InputStream / OutputStream 的子類搭配使用(例如,無法為其加上緩衝功能)。

(3)A bug?

  • 數據的寫入必須出現在文本之前,否則在讀取的時候就會擲出 EOFException。
 java.io.;

 java.io.;
 java.io.;
 java.io.;
 java.io.; 
 java.io.;
 java.io.;
 java.io.;
 java.io.;

 IOProblem {
    
     main([] args)   {
         out =  (
             ( ()));
        out.writeDouble(3.14159);
        out.writeBytes();
        out.writeBytes();
        out.writeDouble(3.14159/2);
        out.close();

         in =  (
             ( ()));
         inbr =  (
             (in));
        
        
        ..println(in.readDouble());
        
        ..println(inbr.readLine());
        ..println(inbr.readLine());
        
        
        ..println(in.readDouble());
    }
}

3.14159
That was the value of pi
This is pi/2:
Exception in thread "main" java.io.EOFException
        at java.io.DataInputStream.readFully(Unknown Source)
        at java.io.DataInputStream.readLong(Unknown Source)
        at java.io.DataInputStream.readDouble(Unknown Source)
        at IOProblem.main(IOProblem.java:42)

(4)Piped streams

  • PipedInputStream / PipedOutputStream / PipedReader / PipedWriter 主要用於 multithreads。

七、Standard I/O

  • Standard I/O:可為程序所用的單一信息流。所有的程序輸入皆可取自 standard input,所有的輸出皆可送至 standard output;所有的錯誤信息皆可送至 standard error。

(1)Reading from standard input

  • System.out 和 System.err 都被包裝為 PrintStream object(in 和 err 是 java.lang.System 的 final static PrintStream 成員),可以直接使用;而 System.in 則是原始的 InputStream,所以在讀取前必須加以包裝(通常是 InputStreamReader + BufferedReader)。
 java.io.;

 java.io.;
 java.io.;

 Echo {
     main([] args)   {
         s;
    
         in =  (
             (.));
        ((s = in readLine()).length() != 0)
            ..println(s);
        
    }
}

(2)Changing System.out to PrintWriter

  • System.out 是 PrintStream,而 PrintStream 則是一個 OutputStream。

  • PrintWriter 有一個構造函數可以以 OutputStream 為參數,將 System.out 轉換為 PrintWriter。
 
 java.io.;

 ChangeSystemOut {
     main([] args) {
        
         out =  (., );
        out.println();
    }
}

(3)Redirecting standard I/O

  • java.io.System 提供了幾個 static 函數,可對 standard input、standard output、standard error 等 I/O streams 進行重定向:
    • setIn(InputStream)

    • setOut(PrintStream)

    • setErr(PrintStream)

  • I/O 重定向處理的是以 byte 而非 character 為單位的 streams,因此使用的是 InputStream 和 OutputStream 而不是 Reader 和 Writer。
 
 java.io.;

 java.io.;
 java.io.;
 java.io.;
 java.io.;
 java.io.;
 java.io.;
 java.io.;

 Redirecting {
     main([] args)   {
         in =  (
             ());
         out =  (
             ( ()));
        .setIn(in);
        .setOut(out);
        .setErr(out);
        
         br =  (
             ());
         s;
        ((s = br.readLine()).length() != 0)
            ..println(s);
        .close(); 
    }
}

八、Compression

  • Java I/O library 提供了一系列的 classes 讓我們可以以壓縮格式對 streams 進行讀寫,這些 classes 將既有的 I/O classes 包裝起來並提供壓縮功能。

  • Java I/O 的 compression library 全部繼承自 InputStream / OutputStream,這是因為它們處理的是 bytes 而不是 characters。
compression classfunction
java.util.zip.CheckedInputStreamgetCheckSum() 能針對任意類型的 InputStream 產生 checksum(校驗碼)而不僅是解壓
java.util.zip.CheckedOutputStreamgetCheckSum() 能針對任意類型的 OutputStream 產生 checksum(校驗碼)而不僅是壓縮
java.util.zip.DeflaterOutputStreamcompression classes 的 base class
java.util.zip.ZipOutputStream可將數據壓縮為 Zip 格式
java.util.zip.GZIPOutputStream可將數據壓縮為 GZIP 格式
java.util.zip.InflaterInputStreamdecompression classes 的 base class
java.util.zip.ZipInputStream可將以 Zip 格式存儲的數據解壓
java.util.zip.GZIPInputStream可將以 GZIP 格式存儲的數據解壓

(1)Simple compression with GZIP

  • compression 相關 classes 的運用方式很直觀:只需將 output stream 包裝成 GZIPOutputStream / ZipOutputStream,並將 input stream 包裝成 GZIPInputStream / ZipInputStream,然後直接進行一般的 I/O 處理即可。

  • GZIP 接口較為簡單因此比較適合對單一的 stream 數據而非多份相異數據進行壓縮。
 java.io.;

 java.io.;
 java.io.;
 java.io.;
 java.io.;
 java.io.;
 java.io.;

 java.util.zip.;
 java.util.zip.;

 GZIPcompress {
     main([] args)   {
        
         in =  (
             (args[0]));
         out =  (
             ( ()));
        ..println();
        
         c;
        ((c = in.read()) != -1)
            out.write(c);
        in.close();
        out.close();
        
        
        ..println();
         in2 =  (
             ( (
                 ())));
                
         s;
        ((s = in2.readLine()) != )
            ..println(s);
    }
}

(2)Multifile storage with Zip

  • java.util.zip 提供了一系列針對 Zip 格式的 classes;其中 CheckSum(interface)有兩類:Adler32(較快) 和 CRC32(較慢但更為精確)。

(3)Java ARchives (JARs)

  • JAR 是由多個經過 Zip 壓縮的文件所組成的單一文件,它同時附有一份內部文件的清單(manifest)。其中 mainfest 可以自己建立,否則 JAR 程序會自動產生。

  • Sun JDK 內附了一個 JAR 工具,其壓縮格式如下:
        jar [options] destination [mainfest] inputfile(s)
optionfunction
c創建新的文件
t列出文件的內容
x解壓所有文件
u更新既有的文件
v產生 jar 執行過程的完整信息
f指定歸檔的文件名。如果不進行指定,JAR 就會假設其輸入來自 standard input,並在建立文件時假定其輸出對象為 standard output
m指定 manifest 文件名
O只存儲文件,並不進行 Zip 壓縮
M通知 JAR 不要自動產生 manifest 文件
i為指定的 JAR 文件生成索引信息
C指定歸檔的目錄(包含其子目錄)

九、Object serialization

  • Java 的 object serialization 機制可以將任何實現了 Serializable interface 的對象轉換為 bytes 序列,該序列可被還原為原來的對象,還可以通過網絡進行傳輸(object serialization 機制會自動處理 bytes 序列在不同 OS 上的差異)。

  • Object serialization 可將 serializable 對象寫入磁盤,並於程序重新啟動時恢復其狀態;從而實現了 lightweight persistence(之所以稱其為 lightweight,是因為 serialize 和 deserialize 的動作需要手動完成)。

  • Object serialization 使 Java 得以支持 RMI(Remote Method Invocation)和 JavaBeans。

  • 對一個對象進行 serialization,只需它實現了 Serializable interface 即可;而 Serializable interface 本身並不具備任何的函數(這樣的 interface 也稱為 marker interface)。

  • Object Serialization:
    • serialize:產生某種 OutputStream 對象並以 ObjectOutputStream 對象加以包裝,然後調用 writeObject() 即可,輸出的數據將被送回該 OutputStream 中;

    • deserialize:產生某種 InputStream 對象並以 ObjectInputStream 對象加以包裝,然後調用 readObject() 即可(readObject() 返回的是經過 upcasting 的對象,所以使用該對象前必須進行 downcasting)。

  • Object serialization 不僅存儲對象在內存中的原始數據,還會追蹤該對象內含的 reference 所指的對象並將它們存儲,以此類推,整個對象網絡都會被存儲下來。

  • Deserializing 並不會調用任何的 constructor(包括 defalut constructor ),整個對象的狀態都是通過讀取 InputStream 恢復的。

(1)Finding the class

  • Deserializing 需要通過對象原始的 .class 文件,所以進行 deserializing 時必須首先確保 JVM 可以找到( local 的 classpath 或是 Internet )相應的 .class 文件。

(2)Controlling serialization

  • 與 Serializable interface 相比,Externalizable interface(繼承自 Serializable interface )增加了 writeExternal() 和 readExternal() 兩個函數,用以控制 serialization 的過程。

  • 與 Serializable interface 不同,使用 Externalizable interface 進行 deserializing 時,所有的 default constructor 都會被調用( Serializable interface 僅讀取 InputStream,並不會有任何的調用動作),然後才是 readExternal()。由於 Externalizable interface 的這個特點,因此在使用它的時候必須在 writeExternal() 和 readExternal() 中根據實際調用正確的 constructor。

  • The transient keyword
    • Serializtion 會略過由關鍵字 transient 聲明的數據(不進行存儲)。

    • 因為缺省情況下 Externalizable 對象的值並不會被存儲,所以 transient 只用於 Serializable。
 java.io.;

 java.io.;
 java.io.;
 java.io.;
 java.io.;
 java.io.;

 java.util.;

 Logon   {
      date =  Date();
      username;
    
      password;

    Logon( name,  pwd) {
        username = name;
        password = pwd;
    }

      toString() {
         pwd = (password == null) ?  : password;
          +  + username
            +  + date +  + pwd;
    }

     main([] args) 
         ,  {

        Logon a =  Logon(, );
        ..println( + a);
         o =  (
             ());
        o.writeObject(a);
        o.close();
        
         seconds = 5;
         t = .currentTimeMillis() + seconds * 1000;
        (.currentTimeMillis() < t)
            ;
        
         in =  (
             ());
        ..println( +  Date());
        a = (Logon) in.readObject();
        ..println( + a);
    }
}
  • Externalizable 的替代方案
    • 實現 Serializable interface 並加入 private 的 writeObject() 和 readObject(),它們會取代 serialization 缺省的行為。
       writeObject( stream)  ;
       readObject( stream)  , ;
      
    • 調用 ObjectOutputStream.writeObject() 時,傳入的 Serializable 對象會被檢查以確認有無自己的 writeObject(),有則執行;同理於 readObject()。

    • ObjectOutputStream.defaultWriteObject() 和 ObjectInputStream.defaultReadObject() 用於處理 non-static 和 non-transient 成員。

  • Versioning:Java 的 versioning 機制過於簡單,並不適用於所用的場合(詳見 JDK 的 HTML 說明文檔)。

(3)Using persistence

  • Serialization 機制可以完全恢復單一 stream 中的對象網絡,並且不會額外的複製任何對象,也就是說單一 stream 中如果有多個對象擁有指向同一對象的 reference,該 reference 指向的對象僅會保存一份。對於非單一的 stream,由於系統無法知道各個 stream 中的對象其實是相同的,所以系統會製造出完全不同的對象網絡。

  • 由於 static 成員並不屬於對象,所以 serializing 並不存儲 static 成員,因此需要自行提供 static 函數對其進行處理。

十、Tokenizing input

  • Tokenizing:将 character 序列劃分為 token 序列(由選定的分隔符劃分而成的文本片段)。

(1)StreamTokenizer

  • java.io.StreamTokenizer 並非繼承自 InputStream / OutputStream,但因為它只能處理 InputStream(Deprecated)和 Reader 對象,所以被歸類於 I/O。

(2)StringTokenizer

  • java.util.StringTokenizer 一般只用於處理簡單的應用( String ),可被視為簡化的 java.io.StreamTokenizer。

(3)Checking capitalization style

【Summary】

  • Java I/O 體系主要可分為 bytes-oriented 和 chars-oriented,應根據實際進行選擇,一般情況下支持 Unicode 的 chars-oriented classes 應當被優先考慮。

  • Java I/O library 中大量使用了 decorator 模式。

转载于:https://www.cnblogs.com/jim/archive/2005/11/14/278463.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值