简介
Java I/O File API是JDK的核心API之一,在构建系统时,十分常用。我在工作实践中,见到大多数人最常使用的是java.io.File
的API,对Java7之后出现的NIO的API不太熟悉。
总的来说,File能实现的功能NIO都能实现,并且NIO API更好用、更方便,同时内置了不少工具类,也解决了File API的一些缺陷。
根据实际工作中的使用和一些文档,本文总结了一些常见的Java File NIO的用法。
File的缺陷
File API比较简单好用,能完成大部分功能,但使用上有不少的问题,比如:
- 大多数方法出错不抛出异常
比如:删除new File("1111.txt").delete()
,如果删除失败,delete
方法只会返回一个false
,开发者没办法知道具体原因,比如是因为没权限,还是因为文件已被删除? - 不支持软链接,这会导致有某些情况下没办法遍历目录
- 文件的元数据支持较少,比如缺少文件的权限、安全属性等
等等。
File和Path
java.nio.file.Path
是Java7中用于替代File的新API,File和Path的概念相似,都是表示一个文件或文件夹的抽象。实际上,也可以把Path当成新API中的File。
FIle与Path的主要区别:
- File是实体类,Path是接口,使得Path更符合面向对象的设计原则。
- File独立于文件系统,Path与文件系统相关,以及Path能给出更多关于文件的信息。
新写的代码最好就开始使用Path,有需要与File交互的地方,可以使用FIle与Path相互转换方法:
Path path = new File(DEMO_PATH).toPath();
File file = path.toFile();
Path的基本操作
创建Path
URI uri = new URI("file:///" + DEMO_PATH);
// method 1
Path path1 = Paths.get(DEMO_PATH);
// method 2
Path path2 = Paths.get(uri);
// method 3
Path path3 = Path.of(DEMO_PATH);
// method 4
Path path4 = Path.of(uri);
3和4是在Java11中引入的。
由于接口的默认方法是在Java8中引入的,所以NIO刚开始是使用工具类
Paths
来创建Path
对象
从Path获取各种信息
Path path = Path.of(DEMO_PATH);
boolean exists = Files.exists(path);
System.out.println("exists = "+exists);
boolean exists2 = Files.exists(path, LinkOption.NOFOLLOW_LINKS);
System.out.println("exists2 = " + exists2);
boolean hidden = Files.isHidden(path);
System.out.println("hidden = " + hidden);
boolean directory = Files.isDirectory(path);
System.out.println("directory = " + directory);
boolean regularFile = Files.isRegularFile(path);
System.out.println("regularFile = " + regularFile);
boolean readable = Files.isReadable(path);
System.out.println("readable = " + readable);
BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class);
System.out.println("creationTime = " + attrs.creationTime());
System.out.println("size = " + attrs.size());
if (System.getProperty("os.name").toLowerCase().contains("win")) {
// windows
DosFileAttributes dosAttrs = Files.readAttributes(path, DosFileAttributes.class);
System.out.println("isSystem = " + dosAttrs.isSystem());
System.out.println("isReadOnly = " + dosAttrs.isReadOnly());
}else{
// linux
PosixFileAttributes posixAttrs = Files.readAttributes(path, PosixFileAttributes.class);
System.out.println("permissions = " + posixAttrs.permissions());
}
写入文件
Path path = Path.of(DEMO_PATH);
OpenOption openOption = StandardOpenOption.APPEND;
// method 1
try(BufferedWriter writer = Files.newBufferedWriter(path, StandardCharsets.UTF_8, openOption);){
writer.write("HelloWorld");
writer.newLine();
writer.write("HelloWorld2");
}
// method 2
Files.writeString(path, "HelloWorld3", StandardCharsets.UTF_8,openOption);
// method 3
Files.write(path, List.of("HelloWorld4"), StandardCharsets.UTF_8,openOption);
读取文件
Path path = Path.of(DEMO_PATH);
// method 1
try(BufferedReader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8);){
String line = reader.readLine();
System.out.println(line);
}
// method 2
try( Stream<String> lines = Files.lines(path) ){
lines.forEach(System.out::println);
}
// method 3
String string = Files.readString(path);
System.out.println(string);
创建、移动、复制文件
public void createFile() throws IOException {
Path path = Files.createFile(Path.of(DEMO_PATH));
System.out.println(Files.exists(path));
}
public void moveFile() throws IOException {
Path path = Files.move(Path.of(DEMO_PATH), Path.of(DEMO_MOVE_PATH), StandardCopyOption.REPLACE_EXISTING);
boolean sameFile = Files.isSameFile(path, Path.of(DEMO_MOVE_PATH));
System.out.println(sameFile);
}
public void copyFile() throws IOException {
Path path = Files.copy(Path.of(DEMO_PATH), Path.of(DEMO_COPY_TAR_PATH), StandardCopyOption.REPLACE_EXISTING);
boolean sameFile = Files.isSameFile(path, Path.of(DEMO_COPY_TAR_PATH));
System.out.println(sameFile);
}
在指定目录查找文件
Path dir = Path.of("G:/");
Path file = Path.of("demoFile2.txt");
Files.find(dir, 1, (p, attr) ->
Files.isRegularFile(p) && p.getFileName().endsWith(file)
, FileVisitOption.FOLLOW_LINKS).findAny()
.ifPresent(System.out::println);
遍历目录
Path walkPath = Path.of(DEMO_WALK_PATH);
// method 1
AtomicLong fileCount = new AtomicLong();
FileVisitor<Path> fileVisitor = new FileVisitor<>() {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
fileCount.getAndIncrement();
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
return FileVisitResult.CONTINUE;
}
};
Files.walkFileTree(walkPath, fileVisitor);
System.out.println(fileCount.get());
// method 2
long count2 = Files.walk(walkPath, FileVisitOption.FOLLOW_LINKS)
.filter(Files::isRegularFile).count();
System.out.println(count2);
参考资料
[1]Oracle对于NIO File API的介绍:https://www.oracle.com/technical-resources/articles/javase/nio.html
[2]file与path方法对照表:https://docs.oracle.com/javase/tutorial/essential/io/legacy.html
评论
发表评论