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()):
- 使用默認的不帶參數的 list(),返回該 File 對象所包含的完整列表。
- 使用帶參數的 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 的職責在於將輸入從各種數據源中取出,而這些數據源可以是:
- bytes array
- String object
- file
- pipe
- 其它 streams 的序列,你可以將這些 streams 的內容匯集成為單一的 stream
- 其它的數據源,例如 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:
- InputStream / OutputStream 基於 8bit 的 byte;Reader / Writer 則基於 16bit 的 character(Java 的 char 類型為 16bit);因此 InputStream / OutputStream 無法處理 16bit 的 Unicode 字符;
- Reader / Writer 較 InputStream / OutputStream 提供了更快的速度;
- 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 也有於訪問位置相關的函數,但由於限制過多所以並不實用)。