NIO.2手冊(1)
Java 7引入了NIO.2,NIO.2是繼承自NIO框架,并增加了新的功能(例如:處理軟鏈接和硬鏈接的功能)。這篇帖子包括三個部分,我將使用NIO.2的一些示例,由此向大家演示NIO.2的基本使用方法。
文件拷貝
Q:怎樣拷貝一個文件?
A:你可以使用java.nio.file.Files類的public static Path copy(Path source, Path target, CopyOption… options)方法來實現這個功能,該方法可以實現從源文件到目標文件的拷貝。
默認情況下,如果目標文件已經存在或者是一個符號鏈接,拷貝就會失敗。但是,如果源文件和目標文件是同一個文件,這個拷貝的動作就不會執行。
此外還有一些注意的事項:
- 文件的屬性的拷貝不是必須的。
- 如果支持符號鏈接,當源文件是一個符號鏈接時,拷貝的是最終目標文件的鏈接。
- 當源文件是一個目錄,copy()方法將目標位置生成一個空目錄(目錄中的元素不會拷貝)。
每個java.nio.file.CopyOption類型的參數傳遞到copy()方法的可變參數列表后將改變該方法的行為。該參數是一個java.nio.file.StandardCopyOption類型或java.nio.file.LinkOption枚舉常量:
- COPY_ATTRIBUTES:嘗試將文件的屬性拷貝到目標文件。這些屬性依賴于平臺和文件系統,因此是不確定的。但是,至少來說,如果源文件和目標文件的存儲都支持最后修改時間屬性的話,該屬性是會拷貝到目標文件的。不過,需要注意的時,拷貝文件的時間戳的精度可能會有所丟失。
- NOFOLLOW_LINKS:不一樣的符號鏈接.如果該文件是一個符號鏈接,拷貝的是符號鏈接自身而不是其引用的目標文件。它的特殊實現在于是否拷貝文件的屬性到新的鏈接上,換句話說,當拷貝一個符號鏈接的時候,COPY_ATTRIBUTES可能被忽略。
- REPLACE_EXISTING:當目標文件已經存在時,目標文件將被替換,除非目標文件是一個非空的目錄。當目標文件是一個符號鏈接并且已經存在的話,僅僅符號鏈接自身被替換而不改變符號鏈接所引用的文件。
copy() 方法不支持StandardCopyOption的ATOMIC_MOVE選項,在文件拷貝中該選項是一個無意義的。我將在之后關于文件移動的討論中介紹ATOMIC_MOVE選項。
非原子性拷貝:
拷貝文件是一個非原子性操作。如果拋出java.io.IOException異常,意味著目標文件可能沒有拷貝完成或者文件的屬性沒有從源文件拷貝過來。如果指定為REPLACE_EXISTING模式并且目標文件已經存在,則目標文件已經被替換了。此外,對文件系統已有文件的存在的檢查和新文件的創建的檢查可能也不是原子的。
除了java.lang.SecurityException,copy()還會拋出以下某一種異常:
- java.nio.file.DirectoryNotEmptyException: REPLACE_EXISTING模式下,因目標文件是一個非空的目錄文件而不能被替換。
- java.nio.file.FileAlreadyExistsException:目標文件已經存在,但沒有指定REPLACE_EXISTING參數而不能被替換。
- IOException: I/O異常
- java.lang.UnsupportedOperationException: 傳入的可變參數CopyOptions是不被支持的。
可選的特定異常:
DirectoryNotEmptyException和FileAlreadyExistsException是可選的異常,它們之所以是可選的是因為這些異常的拋出需要底層操作系統能識別,如果不能識別的話,就拋出IOException來替換。
我已經創建了一個小的應用程序來展示copy()方法的最基本方法。列表1展示了該應用程序的源代碼:
列表1:Copy.java
import java.io.IOException;import java.nio.file.DirectoryNotEmptyException; import java.nio.file.Files; import java.nio.file.FileAlreadyExistsException; import java.nio.file.Path; import java.nio.file.Paths;
public class Copy { public static void main(String[] args) { if (args.length != 2) { System.err.println("usage: java Copy source target"); return; }
Path source = Paths.get(args[0]); Path target = Paths.get(args[1]); try { Files.copy(source, target); } catch (FileAlreadyExistsException faee) { System.err.printf("%s: file already exists%n", target); } catch (DirectoryNotEmptyException dnee) { System.err.printf("%s: not empty%n", target); } catch (IOException ioe) { System.err.printf("I/O error: %s%n", ioe.getMessage()); }
} }</pre>
列表1的main()方法首先驗證命令行確認有兩個參數,代表源文件和目標文件,如果沒有,則輸出相關信息,并結束該程序。
接下來,java.nio.file.Paths類的靜態方法Path get(URI uri)方法被調用兩次,根據文件名從文件系統獲取源文件和目標文件的java.nio.file.Path的實例對象。
Path對象現在被傳到了copy()方法。如果方法執行成功了,將不會輸出任何信息,否則,將輸出適當的錯誤信息。
編譯列表1中的代碼(javac Copy.java)然后運行該程序。例如,執行java Copy Copy.java Copy.bak.你可以嘗試拷貝一個非空目錄到另一個目錄.將出現什么現象?
作為練習,可以修改Copy.java增加命令行參數使得該程序能識別CopyOptions,然后傳入對應的枚舉常量到copy()方法,再觀察這些參數對copy()功能的影響。
向后兼容
為了兼容過去的基于流的I/O框架,Files類提供以下兩種copy()方法的變種:
public static long copy(InputStream in, Path target, CopyOption… options)
public static long copy(Path source, OutputStream out)在此系列的后面,我將演示擴展列表1中的copy方法,使得此方法能夠拷貝一個目錄及子目錄到另外一個目錄中。
刪除文件和目錄
Q: 怎樣刪除一個文件或目錄?
A:你可以使用Files類的public static void delete(Path path)方法來刪除一個文件或目錄,該方法根據文件或目錄的路徑來刪除:
- 如果該路徑引用的文件是一個被使用的打開的文件,某些操作系統將會阻止該文件被刪除。
- 如果該路徑引用的是一個目錄,該目錄必須是空的(除非是特殊操作系統的特殊的文件)。
- 如果該路徑引用的是一個符號鏈接,該方法只刪除符號鏈接,而不刪除符號鏈接指向的文件。
非原子性刪除
該方法的實現是可能需要檢查該物理文件是否是在當前目錄下。由于這個原因,delete()方法對部分操作系統來說可能不是原子的。為了可移植性和安全性考慮,你不應該認為delete()方法的實現是原子的。
delete()遇到I/O異常時拋出IOException,如果要刪除的文件不存在,將拋出java.nio.file.NoSuchFileException,如果目錄不為空,則會拋出DirectoryNotEmptyException。
可選的特定異常:
NoSuchFileException是一個可選的特定的異常。
我創建了一個小的應用程序,用于演示怎樣使用delete()方式。列表2中列出了該應用程序的源代碼。
列表2: Delete.java
import java.io.IOException;import java.nio.file.DirectoryNotEmptyException; import java.nio.file.Files; import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.nio.file.Paths;
public class Delete { public static void main(String[] args) { if (args.length != 1) { System.err.println("usage: java Delete file-or-directory"); return; }
Path path = Paths.get(args[0]); try { Files.delete(path); } catch (NoSuchFileException nsfe) { System.err.printf("%s: no such file or directory%n", path); } catch (DirectoryNotEmptyException dnee) { System.err.printf("%s: not empty%n", path); } catch (IOException ioe) { System.err.printf("I/O error: %s%n", ioe.getMessage()); }
} }</pre>
列表2的main()方法首先驗證命令行參數,確保有且僅有一個參數被傳入,該參數是一個文件或者目錄的路徑。如果沒有,將會輸出有用的信息并結束該程序。
接下來,調用Paths類的get()方法獲取Path對象,該對象代表著文件系統中的文件或目錄。
Path對象現在被傳入到delete()方法中。如果該方法執行成功,將不會輸出任何信息。但是,如果失敗,則會輸出適當的錯誤信息。
編譯列表2(javac Delete.java)中的代碼并運行該應用程序。試著刪一個可讀寫的文件,一個只讀文件,一個非空目錄和一個符號鏈接,然后觀察刪除的結果。
deleteIfExists方法:
Files類聲明一個靜態方法boolean deleteIfExists(Path path)作為delete()的變種。這兩個方法唯一的區別是deleteIfExists只刪除存在的文件,因此,deleteIfExists方法永遠不會拋出NoSuchFileException異常。
移動文件:
Q: 怎樣移動一個文件?
A:你可以使用Files類的public static Path move(Path source, Path target, CopyOption… options)方法來移動文件,該方法的作用就是從源文件移動到目標文件。
默認情況下,該方法嘗試移動源文件到目標文件,當目標文件已經存在的時候會發生異常,除非目標文件是源文件自身,這種情況下,該方法不會起作用。
每個CopyOption類型的參數傳遞到move()方法的可變參數列表后將改變該方法的行為。該參數是一個java.nio.file.StandardCopyOption類型枚舉常量:
- ATOMIC_MOVE: move方法表現為原子的文件系統操作,其他的參數都會被忽略。當目標文件已經存在的時候,特定的實現表現為該存在的文件是否能夠被替換,否則將會拋出IO異常。如果該move方法不能實現原子的文件系統操作,將會拋出java.nio.file.AtomicMoveNotSupportedException異常。
- REPLACE_EXISTING:當目標文件已經存在的時候,目標文件將會被替換,除非目標文件是一個非空的目錄。當目標文件已經存在并且是一個符號鏈接,只替換該符號鏈接自身,而不替換符號鏈接所指向的文件。
除了AtomicMoveNotSupportedException之外,move方法拋出的異常與copy方法一致。
我創建了一個小的應用程序用于演示move方法最基本的使用。列表3列出了該應用程序的源代碼,該方法與列表1的代碼基本一致。
列表3:Move.java
import java.io.IOException;import java.nio.file.DirectoryNotEmptyException; import java.nio.file.Files; import java.nio.file.FileAlreadyExistsException; import java.nio.file.Path; import java.nio.file.Paths;
public class Move { public static void main(String[] args) { if (args.length != 2) { System.err.println("usage: java Move source target"); return; }
Path source = Paths.get(args[0]); Path target = Paths.get(args[1]); try { Files.move(source, target); } catch (FileAlreadyExistsException faee) { System.err.printf("%s: file already exists%n", target); } catch (DirectoryNotEmptyException dnee) { System.err.printf("%s: not empty%n", target); } catch (IOException ioe) { System.err.printf("I/O error: %s%n", ioe.getMessage()); }
} }</pre>
編譯列表3并運行該應用程序。例如,假設有一個名為report.txt的文件,執行java Move report.txt report bak。當你移動文件到另一個已經存在的文件的時候將會發生什么?
作為練習,可以修改Move.java增加命令行參數使得該程序能識別CopyOptions,然后傳入對應的枚舉常量到move()方法,然后觀察這些參數對move()功能的影響。
接下來的內容
在第二部分,我將演示路徑相關方法(例如獲取路徑、檢索路徑信息),文件或目錄測試方法(例如測試文件或目錄的存在性)以及面向屬性的一些方法。
原文鏈接: JavaWorld 翻譯: ImportNew.com - paddx
譯文鏈接: http://www.importnew.com/15884.html