[toc]
-
Path:Path用于表示文件路径和文件,相当于File类
-
Paths:通过get()方法返回一个Path对象。
-
Files:提供了大量处理文件的方法,例如文件复制、读取、写入,获取文件属性、快捷遍历文件目录等.....
-
Files 和 Paths是Java8新增的工具类,在处理文件方面功能非常强大。
Path和Paths
//以当前路径作为Path对象
Path p = Paths.get(".");
//使用传入的字符串返回一个Path对象
Path p2 = Paths.get("D","ReviewIO","URL");
//对应的路径
System.out.println("p对象的对应路径:" + p.toString());
System.out.println("p2对象的对应路径:" + p2.toString());
//路径数量是以路径名的数量作为标准
System.out.println("p路径数量:" + p.getNameCount());
System.out.println("p2路径数量:" + p2.getNameCount());
//获取绝对路径
System.out.println("p绝对路径:" + p.toAbsolutePath());
System.out.println("p2绝对路径:" + p2.toAbsolutePath());
//获取父路径
System.out.println("p父路径:" + p.getParent());
System.out.println("p2父路径:" + p2.getParent());
//获取p2对象的文件名或者文件目录名
System.out.println(p2.getFileName());
//通过Path对象返回一个分隔符对象
Spliterator<Path> split = p2.spliterator();
Paths类获取文件或文件目录路径可以使用采用多个字符串形式,也可以使用Path.get(D:\ReviewIO\URL)这种形式。返回的Path对象完全可以代替File类用于文件IO操作。
Files操作文件
创建与删除文件/目录
//创建文件
Path path = Paths.get("d:/test.txt");
Files.createFile(path);
//创建目录
Path directory = Paths.get("d:", "pictures");
Files.createDirectory(directory);
//删除有两种方法:
//两者的区别在于当文件不存在时,前者会抛出 NoSuchFileException 异常,后者会返回 false。
//注意:当删除目录时,无论使用哪种删除方法,都只能删除空目录,否则会抛出 DirectoryNotEmptyException 异常。
public static void delete(Path path) throws IOException
public static boolean deleteIfExists(Path path) throws IOException
文件复制
public static Path copy(Path source, Path target, CopyOption… options)
//示例如下:
Path source = Paths.get("d:/test.txt");
Path target = Paths.get("e:/");
Files.copy(source, target.resolve(source.getFileName()), StandardCopyOption.REPLACE_EXISTING);
resolve() 方法用来将两个路径合在一起组成新的路径,以上例来说,它的作用是将 d 盘下的 test.txt文件复制到 e 盘下,文件名保持一致。
StandardCopyOption 有如下类型:
- REPLACE_EXISTING:替换已存在的文件
- COPY_ATTRIBUTES:将源文件的文件属性信息复制到目标文件中
- ATOMIC_MOVE:原子性复制,即不存在因失败导致文件复制不完整的情况出现注意:使用 copy 方法复制目录时只会复制空目录,目录中的文件不会复制。复制目录需要通过文件遍历来实现。
文件移动/重命名
// 文件移动与重命名用到的是同一个命令
public static Path move(Path source, Path target, CopyOption… options)
示例如下:
Path source = Paths.get("d:/test.txt");
Path target = Paths.get("e:/");//注意要有"/",否则会使用当前工作目录
//移动到其它目录
Files.move(source, target.resolve(source.getFileName()), StandardCopyOption.REPLACE_EXISTING);
//重命名文件
Files.createFile(source);
Files.move(source, source.resolveSibling("test2.txt"),StandardCopyOption.REPLACE_EXISTING);
resolveSibling() 方法的作用与 resolve() 方法类似,但它是以 source 的父目录与方法中的参数进行合并,例如 source.resolveSibling(“test2.txt”) 代表的路径就是 “d:/test2.txt”。
注意:当移动目录时,只能移动空目录,否则会抛出 DirectoryNotEmptyException 异常;给目录重命名时没有此限制。
文件读写
在之前,如果要使用 BufferedWriter 与 BufferedReader 实现这一操作,需要写一连串冗长的代码;现在,Files类提供了实用的方法封装了这些操作。
//写流的方式
Path path = Paths.get("d:/test.txt");
Files.createFile(path);
BufferedWriter bw = Files.newBufferedWriter(path,StandardCharsets.UTF_8,StandardOpenOption.WRITE);
bw.write("Hello");
bw.newLine();
bw.write("World!");
bw.flush();
bw.close();
//jdk8方式
Files.write(Paths.get("a.txt"), "aaaa".getBytes());
// 在文件读取时,有以下两种方法:
//读文件方式1(jdk1.7)
BufferedReader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8);
String str;
while ((str = reader.readLine()) != null) {
System.out.println(str);
}
reader.close();
//读文件方式2(jdk1.8)
List<String> list = Files.readAllLines(path);
System.out.println(list);
文件遍历
文件遍历有两种方式:非递归遍历与递归遍历。
//非递归遍历的两种方法
Path dir = Paths.get("E:/Pictures");
//非递归遍历目录方式1(jdk1.7)
DirectoryStream<Path> directoryStream = Files.newDirectoryStream(dir);
for (Path item : directoryStream)
System.out.println(item.toAbsolutePath());
directoryStream.close();
//非递归遍历目录方式2(jdk1.8)
Stream<Path> stream = Files.list(dir);
stream.forEach(System.out::println);
stream.close();
递归遍历(深度优先遍历)的两种方法
//深度优先遍历方式1(jdk1.7)
Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
//访问文件时调用
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attr) throws IOException {
System.out.println(file.toAbsolutePath());
return FileVisitResult.CONTINUE;
}
//访问目录前调用
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attr) throws IOException {
System.out.println(dir.toAbsolutePath());
return FileVisitResult.CONTINUE;
}
});
//深度优先遍历方式2(jdk1.8)
Stream<Path> deepStream = Files.walk(dir);
deepStream.forEach(System.out::println);
stream.close();
获取与修改文件属性
public static Map<String,Object> readAttributes(Path path, String attributes, LinkOption… options)
示例如下:
Path path = Paths.get("d:/test.txt");
System.out.println(Files.readAttributes(path, "*"));
输出结果:
{lastAccessTime=2017-11-15T12:57:07.681984Z, lastModifiedTime=2017-11-15T12:57:07.681984Z,
size=0, creationTime=2017-11-15T12:57:07.681984Z, isSymbolicLink=false,
isRegularFile=true, fileKey=null, isOther=false, isDirectory=false}
也可以通过 setAttributes() 方法对文件属性进行修改:
public static Path setAttribute(Path path, String attribute, Object value, LinkOption… options)
示例如下:
Path path = Paths.get("d:/test.txt");
//更改文件属性方式1(Windows)
DosFileAttributeView view = Files.getFileAttributeView(path, DosFileAttributeView.class);
view.setArchive(false);
//更改文件属性方式2(Windows)
Files.setAttribute(path, "dos:archive", true);
方式1的好处是不需要记文件属性名,方式2的好处是简洁易懂,但前提是需要知道准确的文件属性名。
注意:由于各个操作系统文件属性并不相同,Windows 下使用的是 DosFileAttributeView;如果是 Linux 操作系统,使用的是 PosixFileAttributeView。
寻找文件
public static Stream find(Path start, int maxDepth, BiPredicate<Path, BasicFileAttributes> matcher, FileVisitOption… options)
//以寻找后缀为 “docx” 的文件为例:
Path dir = Paths.get("E:/Pictures");
Stream<Path> result = Files.find(dir, 2, (p, basicFileAttributes) -> String.valueOf(p).endsWith(".docx"));
result.map(Path::getFileName).forEach(System.out::println);
也可以在 walk() 方法返回的 Stream 上调用过滤器 filter 来实现此功能。但 find() 方法可能更有效,因为避免了重复检索 BasicFileAttributes。
文件监视 WatchService
在 NIO 2 中也提供了文件监视机制,可以对文件的创建、修改和删除事件进行监视。监视服务的具体实现依赖于操作系统,在可用的情况下会利用操作系统本地的文件监视功能。 但是,当操作系统不支持文件监视机制时,监视服务将采用轮询机制。
使用文件监视的步骤如下:
- 创建 WatchService 实例 watch
- 将需要监控的每个目录注册到观察器,返回 WatchKey 实例 key。在注册目录时,指定要通知的事件的类型
- 通过一个无限循环等待传入的事件。当事件发生时,key 发出信号,加入到 watch 的 queue
- 遍历 key 的事件并根据事件类型进行处理
- 重置 key,重新等待事件
- 关闭监视服务
public class WatchServiceDemo {
public static void main(String[] args) throws IOException, InterruptedException {
String path = "d:/watch";
watch(path);
}
public static void watch(String directory) throws IOException, InterruptedException {
Path path = Paths.get(directory);
WatchService watch = FileSystems.getDefault().newWatchService();
path.register(watch, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
while (true) {
WatchKey key = watch.take();
for (WatchEvent event : key.pollEvents()) {
if (event.kind() == ENTRY_CREATE) {
System.out.println("文件创建:" + event.context());
}
if (event.kind() == ENTRY_MODIFY) {
System.out.println("文件修改:" + event.context());
}
if (event.kind() == ENTRY_DELETE) {
System.out.println("文件删除:" + event.context());
}
}
boolean reset = key.reset();
if (!reset) {
System.out.println("监视服务终止!");
break;
}
}
watch.close();
}
}
注意:在创建、重命名文件以及修改文件内容时会监测到两个重复的事件。此外,上述代码只能监测到该目录下的文件事件,而无法监测到子目录中的文件事件(但能监测到子目录是否发生了变化)。