The Java I/O System

一、The File class

  • java.io.File class 表示一個特定文件的名稱或是某個目錄下的一組文件的名稱(即該目錄的路徑名)。

  • 如果 java.io.File class 表示的是一組文件名,那麼可以使用 list() 來取得它們的名稱,該函數返回一個 String array。

(1)A directory lister

  • 取得目錄名稱列表的兩種方式(使用 overloaded list()):
    1. 使用默認的不帶參數的 list(),返回該 File 對象所包含的完整列表。

    2. 使用帶參數的 list(FilenameFilter filter), java.io.FilenameFilter interface 僅含 accept(File dir, String name) 一個函數(list() 會針對目錄中的每個文件逐一調用 accept() 以判斷它們是否應當被包括在內,判斷結果由 accept() 返回的 boolean 值表示)。
/*
 * @filename DirList.java
 * @author Bruce Eckel
 *
 * ToDo Displays directory listing.
 */

import java.io.File;
import java.io.FilenameFilter;
import java.util.Arrays;
import java.util.Comparator;

public class DirList {
    public static void main(String[] args) {
        File path = new File(".");
        String[] list;
        
        if(args.length == 0)
            list = path.list();
        else
            list = path.list(new DirFilter(args[0]));
            
        Arrays.sort(list, new AlphabeticComparator());

        for(int i = 0; i < list.length; i++)
            System.out.println(list[i]);
    }
}

class DirFilter implements FilenameFilter {
    String afn;
    
    DirFilter(String afn) {
        this.afn = afn;
    }

    public boolean accept(File dir, String name) {
        // 使用 getName() 是為了確保 name 僅僅是文件名而不包括任何路徑信息。
        String f = new File(name).getName();
        return f.indexOf(afn) != -1;
    }
}

class AlphabeticComparator implements Comparator{
    /**
     * Directory filter,依照字母排序。
     *
     * @param o1
     * @param o2
     * @return 
     */
    public int compare(Object o1, Object o2) {
        String s1 = (String) o1;
        String s2 = (String) o2;
    
        return s1.toLowerCase().compareTo(s2.toLowerCase());
    }
}

  • Annoymous inner classes
/*
 * @filename DirList2.java
 * @author Bruce Eckel
 *
 * ToDo Displays directory listing by inner classes.
 */

import java.io.File;
import java.io.FilenameFilter;
import java.util.Arrays;
import java.util.Comparator;

public class DirList2 {
    // 參數 afn 必須是 final,這樣該 anonymous inner class 才能使用其所在範圍以外的對象。
    public static FilenameFilter filter(final String afn) { 
        return new FilenameFilter() {
            String fn = afn;

            public boolean accept(File dir, String n) {
                String f = new File(n).getName();

                return f.indexOf(fn) != -1;
            }
        };
    }

    public static void main(String[] args) {
        File path = new File(".");
        String[] list;

        if(args.length == 0)
            list = path.list();
        else
            list = path.list(filter(args[0]));

        Arrays.sort(list, new AlphabeticComparator());

        for(int i = 0; i < list.length; i++)
            System.out.println(list[i]);
    }
}

class AlphabeticComparator implements Comparator{
    /**
     * Directory filter,依照字母排序。
     *
     * @param o1
     * @param o2
     * @return 
     */
    public int compare(Object o1, Object o2) {
        String s1 = (String) o1;
        String s2 = (String) o2;
    
        return s1.toLowerCase().compareTo(s2.toLowerCase());
    }
}

(2)Checking for and creating directories

  • java.io.File 提供了一系列的函數,它們可以用來實現對文件、目錄的管理(更新至 J2SE 5.0)。
return method ToDo
boolean canRead() Tests whether the application can read the file denoted by this abstract pathname.
boolean canWrite() Tests whether the application can modify the file denoted by this abstract pathname.
int compareTo(File pathname) Compares two abstract pathnames lexicographically.
boolean createNewFile() Atomically creates a new, empty file named by this abstract pathname if and only if a file with this name does not yet exist.
static File createTempFile(String prefix, String suffix) Creates an empty file in the default temporary-file directory, using the given prefix and suffix to generate its name.
static File createTempFile(String prefix, String suffix, File directory) Creates a new empty file in the specified directory, using the given prefix and suffix strings to generate its name.
boolean delete Deletes the file or directory denoted by this abstract pathname.
void deleteOnExit() Requests that the file or directory denoted by this abstract pathname be deleted when the virtual machine terminates.
boolean equals(Object obj) Tests this abstract pathname for equality with the given object.
boolean exists() Tests whether the file or directory denoted by this abstract pathname exists.
File getAbsoluteFile() Returns the absolute form of this abstract pathname.
String getAbsolutePath() Returns the absolute pathname string of this abstract pathname.
File getCanonicalFile() Returns the canonical form of this abstract pathname.
String getCanonicalPath() Returns the canonical pathname string of this abstract pathname.
String getName() Returns the name of the file or directory denoted by this abstract pathname.
String getParent() Returns the pathname string of this abstract pathname's parent, or null if this pathname does not name a parent directory.
File getParentFile() Returns the abstract pathname of this abstract pathname's parent, or null if this pathname does not name a parent directory.
String getPath() Converts this abstract pathname into a pathname string.
int hashCode Computes a hash code for this abstract pathname.
boolean isAbsolute() Tests whether this abstract pathname is absolute.
boolean isDirectory() Tests whether the file denoted by this abstract pathname is a directory.
boolean isFile() Tests whether the file denoted by this abstract pathname is a normal file.
boolean isHidden() Tests whether the file named by this abstract pathname is a hidden file.
long lastModified() Returns the time that the file denoted by this abstract pathname was last modified.
long length() Returns the length of the file denoted by this abstract pathname.
String[] list() Returns an array of strings naming the files and directories in the directory denoted by this abstract pathname.
String[] list(FilenameFilter filter) Returns an array of strings naming the files and directories in the directory denoted by this abstract pathname that satisfy the specified filter.
File[] listFiles() Returns an array of abstract pathnames denoting the files in the directory denoted by this abstract pathname.
File[] listFiles(FileFilter filter) Returns an array of abstract pathnames denoting the files and directories in the directory denoted by this abstract pathname that satisfy the specified filter.
File[] listFiles(FilenameFilter filter) Returns an array of abstract pathnames denoting the files and directories in the directory denoted by this abstract pathname that satisfy the specified filter.
static File[] listRoots() List the available filesystem roots.
boolean mkdir() Creates the directory named by this abstract pathname.
boolean mkdirs() Creates the directory named by this abstract pathname, including any necessary but nonexistent parent directories.
boolean renameTo(File dest) Renames the file denoted by this abstract pathname.
boolean setLastModified(long time) Sets the last-modified time of the file or directory named by this abstract pathname.
boolean setReadOnly() Marks the file or directory named by this abstract pathname so that only read operations are allowed.
String toString() Returns the pathname string of this abstract pathname.
URI toURI() Constructs a file: URI that represents this abstract pathname.
URL toURL() Converts this abstract pathname into a file: URL.

二、Input and output

  • I/O 庫通常使用 stream 這個概念,它代表了所有有能力產生/接收數據的對象,也將實際的 I/O 設備處理的細節隱藏了起來。

  • 所有衍生自 InputStream 或 Reader 的 classes 都具備了 read() 函數,所有衍生自 OutputStream 或 Writer 的 classes 都具備了 write() 函數。它們可用來讀取/寫入單一的 byte 數據或是讀取/寫入 bytes array。但通常不直接使用它們,而是它們的子類(根據實際應用分類的子類)。

(1)Types of InputStream

  • InputStream 的職責在於將輸入從各種數據源中取出,而這些數據源可以是:
    1. bytes array

    2. String object

    3. file

    4. pipe

    5. 其它 streams 的序列,你可以將這些 streams 的內容匯集成為單一的 stream

    6. 其它的數據源,例如 Internet 鏈接

  • 每種數據源都有自己的 InputStream subclass。此外還有 FilterInputStream(被用作 decorator 的 base class)。
class function constructor arguments how to use it
ByteArrayInputStream 允許將內存的一塊緩衝區當做 InputStream 使用 緩衝區(bytes 數據將從中取出) 以它為數據源連接至 FilterInputStream 對象以提供一個有用的接口
StringBufferInputStream 將 String 轉為 InputStream String(底層是 StringBuffer) 以它為數據源連接至 FilterInputStream 對象以提供一個有用的接口
FileInputStream 讀取文件中的信息 String,用來代表一個文件名、File 對象或是 FileDescriptor 對象 以它為數據源連接至 FilterInputStream 對象以提供一個有用的接口
PipedInputStream 產生一份寫入相應的 PipedOutputStream 的數據;此二者的協作實現管道的概念 PipedOutputStream 以它為多線程環境中的數據源連接至 FilterInputStream 對象以提供一個有用的接口
SequenceInputStream 將兩個或以上的 InputStream 對象轉換為一個單一的 InputStream 對象 兩個 InputStream 對象或是一個 Enumeration(後者列舉某個容器內的所有 InputStream 對象) 以它為數據源連接至 FilterInputStream 對象以提供一個有用的接口
FilterInputStream 作為 decorator 的接口為其它的 InputStream classes 提供有用的功能 見 FilterInputStream 列表 見 FilterInputStream 列表

(2)Types of OutputStream

class function constructor arguments how to use it
ByteArrayOutputStream 在內存中建立一塊緩衝區,所有送至此 stream 的數據都會被置於該緩衝區中 緩衝區的初始大小(可選) 為數據指定目的地。將它連接到 FilterOutputStream 對象以提供一個有用的接口
FileOutputStream 將信息寫入文件 String,用來代表一個文件名、File 對象或是 FileDescriptor 對象 為數據指定目的地。將它連接到 FilterOutputStream 對象以提供一個有用的接口
PipedOutputStream 任何被寫入其中的信息都會成為相應的 PipedInputStream 的輸入;此二者的協作實現管道的概念 PipedInputStream 以它為多線程環境中的數據源連接至 FilterOutputStream 對象以提供一個有用的接口
FilterOutputStream 作為 decorator 的接口為其它的 OutputStream classes 提供有用的功能 見 FilterOutputStream 列表 見 FilterOutputStream 列表

三、Adding attributes and useful interfaces

  • FilterInputStream 和 FilterOutputStream 是兩個用來提供 decorators 以控制 InputStream 和 OutputStream 的 classes(兩者都繼續自 InputStream 和 OutputStream)。

(1)Reading from an InputStream with FilterInputStream

 
class function constructor arguments how to use it
DataInputStream 與 DataOutputStream 協同工作,以可移植的方式從 stream 讀取 primitives InputStream 包含一個完整的接口使得可以從 stream 讀取各種 primitives
BufferedInputStream 使用它可以避免每次取數據都進行物理讀取,即使用緩衝區 InputStream(可指定緩衝區大小,可選) 它本身並不提供額外的接口,只是用來申請一塊可用的緩衝區,因此需要為它附上一個 interface 對象
LineNumberInputStream 記錄 input stream 內的行數(有 getLineNumber 和 setLineNumber(int lineNumber) 函數) InputStream 僅為 input stream 加上編號,因此可能會需要為它附上一個 interface 對象
PushbackInputStream 具備一個 one-byte pushback buffer,可將讀到的最後一個 byte 回送 InputStream 通常用於編譯器的 scanner 上。它之所以被放進來,可能是因為編譯器需要。一般不會使用到它

(2)Writing to an OutputStream with FilterOutputStream

class function constructor arguments how to use it
DataOutputStream 與 DataInputStream 協同工作,以可移植的方式將 primitives 寫入 stream OutputStream 包含一個完整的接口使得可以向 stream 寫入各種 primitives
PrintStream 產生格式化的輸出結果。DataOutputStream 處理的是數據的存儲,而 PrintStream 所處理的則是數據的顯示 OutputStream,可指定一個 boolean 值以決定是否在每次換行時將緩衝區的內容清空(可選) 應當用於 OutputStream 的最外層。常用。(注:1)PrintStream 會抑制所有 IOExceptions,因此需要使用它提供的 checkError();2)PrintStream 並示妥善處理國際化的問題,也沒有以平台無關的方式處理換行動作,PrintWriter 已經解決)
BufferedOutputStream 使用它可以避免每次寫數據都進行物理寫入,即使用緩衝區。可以調用 flush() 來清空緩衝區 OutputStream(可指定緩衝區大小,可選) 它本身並不提供額外的接口,只是用來申請一塊可用的緩衝區,因此需要為它附上一個 interface 對象

四、Readers & Writers

  • InputStream/OutputStream 與 Reader/Writer:
    1. InputStream / OutputStream 基於 8bit 的 byte;Reader / Writer 則基於 16bit 的 character(Java 的 char 類型為 16bit);因此 InputStream / OutputStream 無法處理 16bit 的 Unicode 字符;

    2. Reader / Writer 較 InputStream / OutputStream 提供了更快的速度;

    3. InputStreamReader / OutputStreamWriter(Reader / Writer 的子類)可將 InputStream / OutputStream 轉換為 Reader / Writer。

(1)Sources and sinks of data

  • 幾乎所有的 I/O stream classes 都有相應的 Reader 和 Writer 來提供原生的 Unicode 操作,但也有例外(如 java.util.zip 就是 byte-oriented 而非 char-oriented );因此合理的方式是優先使用 Reader / Writer,僅當編譯無法通過時,才使用 byte-oriented 的程序庫(即 I/O stream)。
I/O stream 相應的 Reader / Writer
InputStream Reader(轉換器:InputStreamReader)
OutputStream Writer(轉換器:OutputStreamWriter)
FileInputStream FileReader
FileOutputStream FileWriter
StringBufferStream StringReader
StringWriter
ByteArrayInputStream CharArrayReader
ByteArrayOutputSream CharArrayWriter
PipedInputStream PipedReader
PipedOutputStream PipedWriter

(2)Modifiying stream behavior

  • Reader / Writer 同樣使用了 decorator 模型,與 I/O stream 體系不同的是,它的 filter 並不繼承自 FilterReader / FilterWriter(除了 PushbackReader,繼承自 java.io.FilterReader)。
I/O stream filter Reader / Writer filter
FilterInputStream FilterReader(abstract class)
FilterOutputStream FilterWriter(abstract class)
BufferedInputStream BufferedReader
BufferedInputStream BufferedWriter
DataInputStream 除非需要使用 readLine() 函數(此時使用 BufferedReader),否則應當使用 DataInputStream
PrintStream PrintWriter
LineNumberInputStream LineNumberReader
StreamTokenizer StreamTokenizer(改用以 Reader 為 argument 的 constructor)
PushbackInputStream PushbackReader

(3)Unchanged Classes

  • 未改變的 classes:
    • java.io.DataOutputStream

    • java.io.File

    • java.io.RandomAccessFile

    • java.io.SequenceInputStream

五、Off by itself: RandomAccessFile

  • java.io.RandomAccessFile 並不繼承自 I/O stream 或 Reader / Writer 體系,它是一個完全獨立的 class,它擁有自己專屬的函數(大多數是原生),這樣做的原因是它的行為模式和其它的 I/O 類型有著根本的差異前:它允許在文件內前後移動。

  • RandomAccessFile 的 constructor 提供了第二個參數用以指定讀寫的方式(提供了 r 和 rw,沒有 write-only )。

  • 只有 RandomAccessFile 才提供與文件訪問位置相關的函數,而且它們也只能用於文件(BufferedInputStream 也有於訪問位置相關的函數,但由於限制過多所以並不實用)。

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