目錄
- Java NIO教程
- Java NIO 教程(一) 概述
- Java NIO 教程(二) Channel
- Java NIO 教程(三) Buffer
- Java NIO 教程(四) Scatter/Gather
- Java NIO 教程(五) 通道之間的數據傳輸
- Java NIO 教程(六) Selector
- Java NIO 教程(七) FileChannel
- Java NIO 教程(八) SocketChannel
- Java NIO 教程(九) ServerSocketChannel
- Java NIO 教程(十) 非阻塞式服務器
- Java NIO 教程(十一) Java NIO DatagramChannel
- Java NIO 教程(十二) Pipe
- Java NIO 教程(十三) Java NIO vs. IO
- Java NIO 教程(十四) Java NIO Path
- Java NIO 教程(十五) Java NIO Files
- Java NIO 教程(十六) Java NIO AsynchronousFileChannel
Java NIO Files
類(java.nio.file.Files
)提供了幾種操作文件系統中的文件的方法。這個Java NIO Files
教程將介紹最常用的這些方法。Files
類包含許多方法,所以如果您需要一個在這里沒有描述的方法,那么請檢查JavaDoc。Files
類可能還會有一個方法來實現它。
java.nio.file.Files
類與java.nio.file.Path
實例一起工作,因此在處理Files
類之前,您需要了解Path
類。
Files.exists()
Files.exists()
方法檢查給定的Path
在文件系統中是否存在。
可以創建在文件系統中不存在的Path
實例。例如,如果您計劃創建一個新目錄,您首先要創建相應的Path
實例,然后創建目錄。
由于Path
實例可能指向,也可能沒有指向文件系統中存在的路徑,你可以使用Files.exists()
方法來確定它們是否存在(如果需要檢查的話)。
這里是一個Java Files.exists()
的例子:
Path path = Paths.get("data/logging.properties");
boolean pathExists =
Files.exists(path,
new LinkOption[]{ LinkOption.NOFOLLOW_LINKS});
這個例子首先創建一個Path
實例指向一個路徑,我們想要檢查這個路徑是否存在。然后,這個例子調用Files.exists()
方法,然后將Path
實例作為第一個參數。
注意Files.exists()
方法的第二個參數。這個參數是一個選項數組,它影響Files.exists()
如何確定路徑是否存在。在上面的例子中的數組包含LinkOption.NOFOLLOW_LINKS
,這意味著Files.exists()
方法不應該在文件系統中跟蹤符號鏈接,以確定文件是否存在。
Files.createDirectory()
Files.createDirectory()
方法,用于根據Path
實例創建一個新目錄,下面是一個Files.createDirectory()
例子:
Path path = Paths.get("data/subdir");
try {
Path newDir = Files.createDirectory(path);
} catch(FileAlreadyExistsException e){
// 目錄已經存在
} catch (IOException e) {
// 其他發生的異常
e.printStackTrace();
}
第一行創建表示要創建的目錄的Path
實例。在try-catch
塊中,用路徑作為參數調用Files.createDirectory()
方法。如果創建目錄成功,將返回一個Path
實例,該實例指向新創建的路徑。
如果該目錄已經存在,則是拋出一個java.nio.file.FileAlreadyExistsException
。如果出現其他錯誤,可能會拋出IOException
。例如,如果想要的新目錄的父目錄不存在,則可能會拋出IOException
。父目錄是您想要創建新目錄的目錄。因此,它表示新目錄的父目錄。
Files.copy()
Files.copy()
方法從一個路徑拷貝一個文件到另外一個目錄,這里是一個Java Files.copy()
例子:
Path sourcePath = Paths.get("data/logging.properties");
Path destinationPath = Paths.get("data/logging-copy.properties");
try {
Files.copy(sourcePath, destinationPath);
} catch(FileAlreadyExistsException e) {
// 目錄已經存在
} catch (IOException e) {
// 其他發生的異常
e.printStackTrace();
}
首先,該示例創建一個源和目標Path
實例。然后,這個例子調用Files.copy()
,將兩個Path
實例作為參數傳遞。這可以讓源路徑引用的文件被復制到目標路徑引用的文件中。
如果目標文件已經存在,則拋出一個java.nio.file.FileAlreadyExistsException
異常。如果有其他錯誤,則會拋出一個IOException
。例如,如果將該文件復制到不存在的目錄,則會拋出IOException
。
重寫已存在的文件
可以強制Files.copy()
覆蓋現有的文件。這里有一個示例,演示如何使用Files.copy()
覆蓋現有文件。
Path sourcePath = Paths.get("data/logging.properties");
Path destinationPath = Paths.get("data/logging-copy.properties");
try {
Files.copy(sourcePath, destinationPath,
StandardCopyOption.REPLACE_EXISTING);
} catch(FileAlreadyExistsException e) {
// 目標文件已存在
} catch (IOException e) {
// 其他發生的異常
e.printStackTrace();
}
請注意Files.copy()
方法的第三個參數。如果目標文件已經存在,這個參數指示copy()
方法覆蓋現有的文件。
Files.move()
Java NIO Files
還包含一個函數,用于將文件從一個路徑移動到另一個路徑。移動文件與重命名相同,但是移動文件既可以移動到不同的目錄,也可以在相同的操作中更改它的名稱。是的,java.io.File
類也可以使用它的renameTo()
方法來完成這個操作,但是現在已經在java.nio.file.Files
中有了文件移動功能。
這里有一個Java Files.move()
例子:
Path sourcePath = Paths.get("data/logging-copy.properties");
Path destinationPath = Paths.get("data/subdir/logging-moved.properties");
try {
Files.move(sourcePath, destinationPath,
StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
//移動文件失敗
e.printStackTrace();
}
首先創建源路徑和目標路徑。源路徑指向要移動的文件,而目標路徑指向文件應該移動到的位置。然后調用Files.move()
方法。這會導致文件被移動。
請注意傳遞給Files.move()
的第三個參數。這個參數告訴Files.move()
方法來覆蓋目標路徑上的任何現有文件。這個參數實際上是可選的。
如果移動文件失敗,Files.move()
方法可能拋出一個IOException
。例如,如果一個文件已經存在于目標路徑中,并且您已經排除了StandardCopyOption.REPLACE_EXISTING
選項,或者被移動的文件不存在等等。
Files.delete()
Files.delete()
方法可以刪除一個文件或者目錄。下面是一個Java Files.delete()
例子:
Path path = Paths.get("data/subdir/logging-moved.properties");
try {
Files.delete(path);
} catch (IOException e) {
// 刪除文件失敗
e.printStackTrace();
}
首先,創建指向要刪除的文件的Path
。然后調用Files.delete()
方法。如果Files.delete()
由于某種原因不能刪除文件(例如,文件或目錄不存在),會拋出一個IOException
。
Files.walkFileTree()
Files.walkFileTree()
方法包含遞歸遍歷目錄樹的功能。walkFileTree()
方法將Path
實例和FileVisitor
作為參數。Path
實例指向您想要遍歷的目錄。FileVisitor
在遍歷期間被調用。
在我解釋遍歷是如何工作之前,這里我們先了解FileVisitor
接口:
public interface FileVisitor {
public FileVisitResult preVisitDirectory(
Path dir, BasicFileAttributes attrs) throws IOException;
public FileVisitResult visitFile(
Path file, BasicFileAttributes attrs) throws IOException;
public FileVisitResult visitFileFailed(
Path file, IOException exc) throws IOException;
public FileVisitResult postVisitDirectory(
Path dir, IOException exc) throws IOException {
}
您必須自己實現FileVisitor
接口,并將實現的實例傳遞給walkFileTree()
方法。在目錄遍歷過程中,您的FileVisitor
實現的每個方法都將被調用。如果不需要實現所有這些方法,那么可以擴展SimpleFileVisitor
類,它包含FileVisitor
接口中所有方法的默認實現。
這里是一個walkFileTree()
的例子:
Files.walkFileTree(path, new FileVisitor<Path>() {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
System.out.println("pre visit dir:" + dir);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
System.out.println("visit file: " + file);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
System.out.println("visit file failed: " + file);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
System.out.println("post visit directory: " + dir);
return FileVisitResult.CONTINUE;
}
});
FileVisitor
實現中的每個方法在遍歷過程中的不同時間都被調用:
在訪問任何目錄之前調用preVisitDirectory()
方法。在訪問一個目錄之后調用postVisitDirectory()
方法。
調用visitFile()
在文件遍歷過程中訪問的每一個文件。它不會訪問目錄-只會訪問文件。在訪問文件失敗時調用visitFileFailed()
方法。例如,如果您沒有正確的權限,或者其他什么地方出錯了。
這四個方法中的每個都返回一個FileVisitResult
枚舉實例。FileVisitResult
枚舉包含以下四個選項:
- CONTINUE 繼續
- TERMINATE 終止
- SKIP_SIBLING 跳過同級
- SKIP_SUBTREE 跳過子級
通過返回其中一個值,調用方法可以決定如何繼續執行文件。
CONTINUE
繼續意味著文件的執行應該像正常一樣繼續。
TERMINATE
終止意味著文件遍歷現在應該終止。
SKIP_SIBLINGS
跳過同級意味著文件遍歷應該繼續,但不需要訪問該文件或目錄的任何同級。
SKIP_SUBTREE
跳過子級意味著文件遍歷應該繼續,但是不需要訪問這個目錄中的子目錄。這個值只有從preVisitDirectory()
返回時才是一個函數。如果從任何其他方法返回,它將被解釋為一個CONTINUE
繼續。
文件搜索
這里是一個用于擴展SimpleFileVisitor
的walkFileTree()
,以查找一個名為README.txt
的文件:
Path rootPath = Paths.get("data");
String fileToFind = File.separator + "README.txt";
try {
Files.walkFileTree(rootPath, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
String fileString = file.toAbsolutePath().toString();
//System.out.println("pathString = " + fileString);
if(fileString.endsWith(fileToFind)){
System.out.println("file found at path: " + file.toAbsolutePath());
return FileVisitResult.TERMINATE;
}
return FileVisitResult.CONTINUE;
}
});
} catch(IOException e){
e.printStackTrace();
}
遞歸刪除目錄
Files.walkFileTree()
也可以用來刪除包含所有文件和子目錄的目錄。Files.delete()
方法只會刪除一個目錄,如果它是空的。通過遍歷所有目錄并刪除每個目錄中的所有文件(在visitFile()
)中,然后刪除目錄本身(在postVisitDirectory()
中),您可以刪除包含所有子目錄和文件的目錄。下面是一個遞歸目錄刪除示例:
Path rootPath = Paths.get("data/to-delete");
try {
Files.walkFileTree(rootPath, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
System.out.println("delete file: " + file.toString());
Files.delete(file);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
Files.delete(dir);
System.out.println("delete dir: " + dir.toString());
return FileVisitResult.CONTINUE;
}
});
} catch(IOException e){
e.printStackTrace();
}
文件類中的其他方法
java.nio.file.Files
類包含許多其他有用的函數,比如用于創建符號鏈接的函數、確定文件大小、設置文件權限等等。有關這些方法的更多信息,請查看java.nio.file.Files
類的JavaDoc。