Java NIO教程 文件系统

在NIO.2的文件系统中,Path是一切操作的基础。Path准确来说,代表着文件系统中的位置。可以代表一个目录(也就是通常所说的文件夹),也可以代表一个文件。

在新文件系统中,还有一个不得不说的就是Files。它是一个工具类,但是这个工具类跟打了鸡血一样,强大到不可思议。以前需要写繁重代码或者需要调用第三方类库才能完成的功能,现在只需一行。

下面的代码展示了Path的最基本操作-获取一个Path;并且通过我们得到的Path和强大的工具类Files来创建一个文件或目录

Path dir0 = Paths.get("c:\\test");// 创建一个Path路径;Paths是工具类,用于产生Path
Files.createDirectory(dir0);// 创建单级目录;若已经存在 则抛出异常
Path dir1 = Paths.get("c:\\test\\Hello.java");
Files.createFile(dir1);// 创建文件;若目录没有 则抛出异常
Path dir2 = Paths.get("c:\\test\\Hello\\World");
Files.createDirectories(dir2);// 创建多级目录;若已经存在 不抛出异常

展示完最基础的创建功能后,再展示一点Path的高级功能——遍历目录

遍历目录有两种方式,一种是通过DirectoryStream类来遍历单层目录,另一种就是通过实现FileVisitor接口来递归的遍历目录

重点强调一下这两者的区别。DirectoryStream只能遍历给定的目录下的文件或目录,如果在给定的目录下还有子目录,那么DirectoryStream不会再向下遍历子目录中的内容。而用实现FileVisitor接口的方式,则在给定的目录下若还有子目录,它会接着遍历子目录中的内容(类似于深度优先搜索)

上代码 首先给出的是DirectoryStream的方式

Path dir3 = Paths.get("c:\\test");
// 1.DirectoryStream可以理解成对于Path的Iterable,返回在dir3中符合第二个参数形式的Path集合
// 2.下面用到了一种try-with-resources的写法,该写法是在java1.7中引入的语法糖(极力推荐大家用该写法,因为它可以帮助你正确的关闭资源)
try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir3,
"*.java")) {
for (Path entry : stream) {
System.out.println(entry.getFileName());
}
}

再强调一下Files.newDirectoryStream的第二个参数。这种形式是glob表达式,如*.java代表的就是所有以".java"结尾的字符串

下面再说一下,实现FileVisitor接口的方式。这里得用到Files的一个静态方法

Files.walkFileTree(Path start,FileVisitor<? super Path> visitor)

第一眼就感觉好复杂,而且一看walkFileTree的二个参数就不是善茬,要实现它就得实现下面的4个方法

  • FileVisitResult postVisitDirectory(T dir, IOException exc)
  • FileVisitResult preVisitDirectory(T dir, BasicFileAttributes attrs)
  • FileVisitResult visitFile(T file, BasicFileAttributes attrs)
  • FileVisitResult visitFileFailed(T file, IOException exc)

但好在java1.7已经提供了一个默认实现类SimpleFileVisitor,这样你就可以只用重写你需要的方法了,下面的例子是打印一个目录下所有文件的名称(文件夹的名称不打印)

Path dir3 = Paths.get("c:\\test");
Files.walkFileTree(dir3, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file,
BasicFileAttributes attrs) throws IOException {
System.out.println(file.getFileName());
return FileVisitResult.CONTINUE;
}
});

两段困难的代码看完了,咱们再来的简单的。

文件的创建、删除、复制、移动、重命名等操作,都是我们平时常用的操作。以前想写的话,真得下点功夫或者直接找个第三方类库。但是现在时代不同了,有了Files一切如丝般顺滑。看看Files的文档吧,那里有许多你梦遗所求的方法。这里展示几个常用的。

先来一段删除文件或目录的

Path dir4 = Paths.get("c:\\test\\del");
Files.delete(dir4);// 删除文件或文件夹;删除文件夹时,若文件夹中还有文件或文件夹则抛出异常
Files.deleteIfExists(dir4);// 如果存在该文件或文件夹则删除

删除很简单吧,下面让我们来看看复制

复制要考虑的情况就有点多了,复制我们主要用到的方法是

Files.copy(Path source,Path target,CopyOption... options)

前两个参数分别为原位置和目的位置,第三个参数为复制选项。复制选项的意思,说白了就是复制时发生了各种情况时,该如何处理。CopyOption其实也应该是让你写的,但是java中内置了三种方式供你选择,它们分别是:

StandardCopyOption.REPLACE_EXISTING:如果目的路径有同名文件则替换

StandardCopyOption.ATOMIC_MOVE:若失败则回滚

StandardCopyOption.COPY_ATTRIBUTES:把源文件的文件属性一同复制给新文件(readAttributes等方法可以读取文件属性)

给你个例子

Path source = Paths.get("c:\\test\\Hello.java");
Path target = Paths.get("c:\\test\\Hello\\World\\Hello.java");
Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);

再就说说文件的移动,基本上与复制同理,主要用到的方法是

Files.move(Path source,Path target,CopyOption... options)

同样,也需要复制选项,其实给文件重命名用的也是这招,下面的例子就展示了这点

Path source = Paths.get("c:\\test\\Hello\\World\\Hello.java");
Path target = Paths.get("c:\\test\\Hello\\World\\He.java");
Files.move(source, target, StandardCopyOption.REPLACE_EXISTING);

下面要介绍的就是我最喜欢的内容了,给你们看两个方法

List<String> Files.readAllLines(Path path)

Files.write(Path path,Iterable<? extends CharSequence> lines,OpenOption... options)

是不是一目了然。对这就是文件的按行读和按行写,用起来真的特别方便。在java1.7之前,我在guava中看见过这些方法,有时候用guava的io其实根本不是为了它的source和sink,而就是为了这几个方法。这两个方法的出现,反映了java的一种新态度,那就是纳谏、虚心。优秀的命名、方便的工具类,这都是以前java官方中所欠缺的,无论是新的Time包还是lamda表达式或是Coin项目,都是向社区学习、吸收优秀的社区资源,都是将开发者的梦想变为现实、方便开发者。

不好意思扯远了。那就直接上代码了

Path source = Paths.get("c:\\test\\Hello\\World\\Ab.java");
Path target = Paths.get("c:\\test\\Hello\\World\\He.java"); //以防乱码最好设置一下文件的编码格式,在StandardCharsets中有很多种内置编码格式
List<String> list = Files.readAllLines(source, StandardCharsets.UTF_8);
for (String s : list) {
System.out.println(s);
}
//在StandardOpenOption中有很多种内置文件打开方式可供选择
Files.write(target, list, StandardCharsets.UTF_8,StandardOpenOption.WRITE);

整个NIO.2中的新文件系统的主要内容,也就基本这么多(文件属性这块没太说,想学的自己找找资料吧)最后咱们来的略显高端的就是文件系统的监测;主要用到的类是java.nio.file.WatchService,该类在注册后,一直监控文件或目录的变化,若有相应的变化发生,就返回一个事件。这在很多地方都是有用的,而且这个类性能不错

try (WatchService watcher = FileSystems.getDefault().newWatchService()) {
Path dir = Paths.get("c:\\test");
WatchKey key = dir.register(watcher,
StandardWatchEventKinds.ENTRY_DELETE);
for (;;) {
key = watcher.take();
for (WatchEvent<?> event : key.pollEvents()) {
System.out.println(event.kind());
}
boolean valid = key.reset();
if (!valid) {
break;
}
}
}

关于NIO.2中的新文件系统就聊这么多,有什么问题欢迎讨论,若有什么错误,望不吝赐教(赶紧告诉我,我好改,改晚了耽误其他人)

上一篇:Java语言的个人理解


下一篇:JAVA版Kafka代码及配置解释