Path
我接触到 nio,最先的应用就是 Path 这个类。
最直观的感觉就是 api 设计的很优雅(相对于传统的 File)。
Path 类
Java的path接口是作为Java NIO 2的一部分是Java6,7中NIO的升级增加部分。
Path在Java 7新增的。
相关接口位于java.nio.file包下,所以Javaz内Path接口的完整名称是 java.nio.file.Path
.
path可以指向文件也可以指向目录。可以使相对路径也可以是绝对路径。
绝对路径包含了从根目录到该文件(目录)的完整路径。相对路径包含该文件(目录)相对于其他路径的路径。相对路径听起来可能有点让人头晕。但是别急,稍后我们会详细介绍。
创建 Path
Path = path = Paths.get("c:\\data\\myfile.txt");
也可以通过 File 直接转换为 Path
Path path = file.toPath();
文件路径
- 绝对路径
Path path = Paths.get("/home/jakobjenkov/myfile.txt");
- 相对路径
创建一个相对路径可以通过调用 Path.get(basePath, relativePath)
,
下面是一个示例:
Path file = Paths.get("d:\\data", "projects\\a-project\\myfile.txt");
- 当前路径
Path currentDir = Paths.get(".");
- 父级路径
Path parentDir = Paths.get("..");
Files
这个类就是 File 相关的工具方法合集,和 Path 结合使用。
为我们操作带来了很多方便,所以了解即可。
Files.exists()
Files.exits() 方法用来检查给定的Path在文件系统中是否存在。
Path path = Paths.get("data/logging.properties");
boolean pathExists = Files.exists(path, new LinkOption[]{ LinkOption.NOFOLLOW_LINKS});
注意Files.exists()的的第二个参数。
他是一个数组,这个参数直接影响到Files.exists()如何确定一个路径是否存在。
在本例中,这个数组内包含了LinkOptions.NOFOLLOW_LINKS,表示检测时不包含符号链接文件。
Files.createDirectory()
Files.createDirectory()会创建Path表示的路径,下面是一个示例:
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();
}
Files.copy()
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) {
//destination file already exists
} catch (IOException e) {
//something else went wrong
e.printStackTrace();
}
- 覆盖已经存在的文件
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.move()
Java NIO的Files类也包含了移动的文件的接口。
移动文件和重命名是一样的,但是还会改变文件的目录位置。
java.io.File类中的renameTo()方法与之功能是一样的。
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.delete()
Files.delete()方法可以删除一个文件或目录:
Path path = Paths.get("data/subdir/logging-moved.properties");
try {
Files.delete(path);
} catch (IOException e) {
//deleting file failed
e.printStackTrace();
}
Files.walkFileTree()
Files.walkFileTree()方法具有递归遍历目录的功能。
walkFileTree接受一个Path和FileVisitor作为参数。
Path对象是需要遍历的目录,FileVistor则会在每次遍历中被调用。
ps: 这方法用来做文件遍历,是真心强大。
- 实现的示例代码
FileVisitor需要调用方自行实现,然后作为参数传入walkFileTree().FileVisitor的每个方法会在遍历过程中被调用多次。
如果不需要处理每个方法,那么可以继承他的默认实现类SimpleFileVisitor,它将所有的接口做了空实现。
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;
}
});
FileVisitResult
上述四个方法都返回一个 FileVisitResult 枚举对象。
具体的可选枚举项包括:
-
CONTINUE
-
TERMINATE
-
SKIP_SIBLINGS
-
SKIP_SUBTREE
返回这个枚举值可以让调用方决定文件遍历是否需要继续。
CONTINE 表示文件遍历和正常情况下一样继续。
TERMINATE 表示文件访问需要终止。
SKIP_SIBLINGS 表示文件访问继续,但是不需要访问其他同级文件或目录。
SKIP_SUBTREE 表示继续访问,但是不需要访问该目录下的子目录。这个枚举值仅在preVisitDirectory()中返回才有效。如果在另外几个方法中返回,那么会被理解为CONTINE。
Searching For Files
简单的例子,当然一般我们使用通配符,实用性更强。
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();
}
参考资料
Path
http://wiki.jikexueyuan.com/project/java-nio-zh/java-nio-path.html
Files
http://wiki.jikexueyuan.com/project/java-nio-zh/java-nio-files.html