Java NIO文件
Java NIO Files类(java.nio.file.Files)提供了几种在文件系统中操作文件的方法。本Java NIO"文件"教程将介绍这些方法中最常用的方法。 Files类包含许多方法,因此,如果需要此处未描述的方法,也请检查JavaDoc。 Files类仍然可能有一个方法。
" java.nio.file.Files"类可与" java.nio.file.Path"实例一起使用,因此在使用" Files"类之前,我们需要了解" Path"类。
Files.exists()
Files.exists()方法检查文件系统中是否存在给定的Path。
可以创建文件系统中不存在的Path
实例。例如,如果我们打算创建一个新目录,则首先要创建相应的Path
实例,然后再创建目录。
由于Path
实例可能指向或者不指向文件系统中存在的路径,因此我们可以使用Files.exists()
方法确定它们是否存在(以防我们需要检查)。
这是一个JavaFiles.exists()
示例:
Path path = Paths.get("data/logging.properties"); boolean pathExists = Files.exists(path, new LinkOption[]{ LinkOption.NOFOLLOW_LINKS});
这个例子首先创建一个Path
实例,该实例指向我们要检查的路径是否存在。其次,该示例以" Path"实例作为第一个参数调用" Files.exists()"方法。
注意Files.exists()方法的第二个参数。这个参数是一系列选项,这些选项会影响Files.exists()确定路径是否存在的方式。在上面的示例中,数组包含" LinkOption.NOFOLLOW_LINKS",这意味着" Files.exists()"方法不应跟随文件系统中的符号链接来确定路径是否存在。
Files.createDirectory()
Files.createDirectory()方法从Path实例创建一个新目录。这是一个JavaFiles.createDirectory()
示例:
Path path = Paths.get("data/subdir"); try { Path newDir = Files.createDirectory(path); } catch(FileAlreadyExistsException e){ // the directory already exists. } catch (IOException e) { //something else went wrong e.printStackTrace(); }
第一行创建一个" Path"实例,该实例代表要创建的目录。在try-catch块中,使用路径作为参数调用Files.createDirectory()方法。如果创建目录成功,则将返回一个Path实例,该实例指向新创建的路径。
如果目录已经存在,将抛出java.nio.file.FileAlreadyExistsException
。如果其他地方出了问题,可能会抛出IOException。例如,如果所需的新目录的父目录不存在,则可能抛出" IOException"。父目录是我们要其中创建新目录的目录。因此,这意味着新目录的父目录。
Files.copy()
Files.copy()方法将文件从一个路径复制到另一个路径。这是一个Java NIOFiles.copy()
示例:
Path sourcePath = Paths.get("data/logging.properties"); Path destinationPath = Paths.get("data/logging-copy.properties"); try { Files.copy(sourcePath, destinationPath); } catch(FileAlreadyExistsException e) { //destination file already exists } catch (IOException e) { //something else went wrong 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) { //destination file already exists } catch (IOException e) { //something else went wrong e.printStackTrace(); }
注意Files.copy()方法的第三个参数。如果目标文件已经存在,则此参数指示copy()
方法覆盖现有文件。
Files.move()
Java NIOFiles
类还包含一个用于将文件从一个路径移动到另一路径的函数。移动文件与重命名相同,只是移动文件既可以将文件移动到另一个目录,又可以在同一操作中更改其名称。是的,java.io.File
类也可以通过其renameTo()
方法来做到这一点,但是现在我们在java.nio.file.Files
类中也具有文件移动函数。
这是一个JavaFiles.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) { //moving file failed. e.printStackTrace(); }
首先,创建源路径和目标路径。源路径指向要移动的文件,目标路径指向应将文件移动到的位置。然后调用Files.move()方法。这导致文件被移动。
注意第三个参数传递给Files.move()。这个参数告诉Files.move()方法覆盖目标路径上的任何现有文件。该参数实际上是可选的。
如果文件移动失败,则Files.move()方法可能会抛出IOException。例如,如果目标路径上已经存在文件,而我们省略了" StandardCopyOption.REPLACE_EXISTING"选项,或者要移动的文件不存在等。
Files.delete()
Files.delete()方法可以删除文件或者目录。这是一个JavaFiles.delete()
示例:
Path path = Paths.get("data/subdir/logging-moved.properties"); try { Files.delete(path); } catch (IOException e) { //deleting file failed 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_SIBLINGS
SKIP_SUBTREE
通过返回这些值之一,被调用的方法可以决定如何继续文件遍历。
" CONTINUE"表示文件继续浏览应照常进行。
TERMINATE表示文件遍历应立即终止。
" SKIP_SIBLINGS"表示应该继续进行文件遍历,但不要访问该文件或者目录的任何同级文件。
" SKIP_SUBTREE"表示文件继续浏览,但不访问该目录中的条目。该值仅从preVisitDirectory()
返回时才具有函数。如果从任何其他方法返回,它将被解释为" CONTINUE"。
搜索文件
这是一个walkFileTree()
,它扩展了'SimpleFileVisitor来查找名为
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(); }
Files类中的其他方法
" java.nio.file.Files"类包含许多其他有用的函数,例如用于创建符号链接,确定文件大小,设置文件许可权的函数。