Path 和 Files类封装了在用户机器上处理文件系统所需要的所有功能。例如,可以使用Files 类来移除或重命名文件,或者查询文件最后被修改的时间。换句话说,流类关心的是文件的内容,而我们在此处要讨论的类关心的是在磁盘上如何存储文件。
Path 和 Files 是在Java SE7 中新添加进来的类,它们用起来比自JDK1.0 就一直使用的File 类 要方便的多。
Path
Path 表示的是一个目录名序列,其后还可以跟着一个文件名。路径中的第一个部件可以是根部件,例如/或C:\,而允许访问的根部件取决于文件系统。以根部件开始的路径是绝对路径;否则就是相对路径。
Path absolut = Paths.get("G:\\04JavaIO","hello.txt");
Path relative = Paths.get("04JavaIO","hello.txt");
静态的Paths.get 方法接受一个或多个字符串,并将它们用默认文件系统的路径分隔符连接起来。然后它解析连接起来的结果,如果其表示的不是给定文件系统中的合法路径,那么就抛出InvalidPathException 异常。这个连接起来的结果就是一个Path 对象。
路径不必对应着某个实际存在的文件,它仅仅只是一个抽象的名字序列。当你想要创建文件时,首先要创建一个路径,然后才调用方法去创建对应的文件。
组合或解析路径
调用p.resolve(q) 将按照下面规则返回一个路径:
- 如果q 是绝对路径,则结果就是q。
否则,根据文件系统的规则,将“p后面跟着q”作为结果。
resolveSibling方法,通过解析指定路径的父路径产生其兄弟路径。例如,如果workPath 是 \opt\myapp\work,那么下面的调用
Path temPath = workPath.resolveSibling("temp");
将创建\otp\myapp\temp。
relativize方法,即调用p.relativize(r) 将产生路径q,对q进行解析会产生r。例如,以“/home/cay” 为目标对”/home/fred/myprog” 进行相对化操作,会产生“../fred/myprog”,其中,我们假设 .. 表示文件系统中的父目录。
toAbsolutePath 方法将产生给定路径的绝对路径,该绝对路径从根部件开始。
读写文件
Files 类可以使得普通文件操作变得快捷。例如,可以用下面的方式很容易地读取文件的所有内容:
byte[] bytes = Files.readAllBytes(path);
如果想将文件当做字符串读入,那么可以在调用readAllBytes 之后,调用下面的代码:
String content = new String(bytes,charset);
如果希望将文件当做行序列读入:
List<String> lines = Files.readAllLines(path,charset);
如果希望写出一个字符串到文件中:
Files.write(path,content,getBytes(charset));
向指定文件追加内容,可以调用:
Files.write(path,content.getBytes(charset),StandardOpenOption.APPEND);
还可以用下面的语句将一个行的集合写出到文件中:
Files.write(path,lines);
这些简便方法用于处理中等长度的文本文件,如果要处理的文件长度比较大,或者是二进制文件,那么还是应该用流或者读入器/写出器:
InputStream in = Files.newInputStream(path);
OutputStream out = Files.newOutputStream(path);
Reader in = Files.newBufferedReader(path,charset);
Writer out = Files.newBufferedWriter(path,charset);
复制、移动和删除文件
将一个文件从一个位置复制到另一个位置可以直接调用:
Files.copy(fromPath,toPath);
移动(即复制并删除原文件)可以调用:
Files.move(fromPath,toPath);
如果目标路径已经存在,那么复制或移动将失败,如果想要覆盖已有的目标路径,可以使用REPLACE_EXISTING选项。如果想要复制所有的文件属性,可以使用COPY_ATTRIBUTES选项。
Files.copy(fromPath,toPath,StandardCopyOption.REPLACE_EXISTING,StandardCopyOption.COPY_ATTRIBUTES);
你可以将移动操作定义为原子性的,这样就可以保证要么移动操作成功完成,要么原文件继续保持在原来的位置。
Files.move(fromPath,toPath,StandardCopyOption.ATOMIC_MOVE);
最后删除文件可以调用:
Files.delete(path);
如果删除的文件不存在,这个方法就会抛出异常。因此可以转而使用下面的方法:
`boolean deleted = Files.deleteIfExists(path);
该删除方法还可以用来移除空目录。
创建文件和目录
创建新目录可以调用:
Files.createDirectory(path);
其中路径中除最后一个部件外,其他部分都必须是已存在的。要创建路径中的中间目录,应该使用:
Files.createDirectories(path);
如果文件已经存在了,那么这个调用就会抛出异常。
迭代目录中的文件
Files类设计了一个方法,它可以产生一个Iterable对象。
try(DirectoryStream<Path> entries = Files.newDirectoryStream(path)) //Java 7新特性,支持try后面跟随()管理释放资源
{ for(Path entry:entries)
System.out.println(entry.getFileName());
}catch(Exception e){
}
可以用glob模式来过滤文件:
try(DirectoryStream<Path> entries = Files.newDirectoryStream(path,"*.java"))
如果想要访问某个目录的所有子孙成员,可以转而调用walkFileTree方法,并向其传递一个FileVisitor 类型的对象,这个对象会得到一系列通知:
- 在遇到一个文件或目录时:FileVisitResult visitFile(T path,BasicFileAttributes attrs)
- 在一个目录被处理前:FileVisitResult preVisitDirectory(T dir,IOException ex)
- 在一个目录被处理前:FileVisitResult postVisitDirectory(T dir,IOException ex)
在试图访问文件或目录发生错误,例如没有权限打开目录:FileVisitResult visitFileFailed(path,IOException ex)
对于上述每种情况,都可以指定是否希望执行下面的操作:
- 继续访问下一个文件:FileVisitResult.CONTINUE
- 继续访问,但是不再访问这个目录下的任何项了:FileVisitResult.SKIP_SUBTREE
- 继续访问,但是不再访问这个文件的兄弟文件了:FileVisitResult.SKIP_SIBLINGS
- 终止访问:FileVisitResult.TERMINATE
当有任何方法抛出异常时,就会终止访问,而这个异常会从walkFileTree 方法中抛出。
Files.walkFileTree(absolute,new SimpleFileVisitor<Path>(){
public FileVisitResult preVisitDirectory(Path path,BasicFileAttributes attrs) throws IOException{
System.out.println(path);
return FileVisitResult.CONTINUE;
}
public FileVisitResult visitFileFailed(Path path,IOException exc) throws IOException{
return FileVisitResult.CONTINUE;
}
public FileVisitResult visitFile(Path path,BasicFileAttributes attrs) throws IOException{
System.out.println(path);
return FileVisitResult.CONTINUE;
}
});
ZIP文件系统
Paths 类会在默认文件系统中查找路径,即在用户本地磁盘中的文件。也可以有别的文件系统,其中最有用的之一是ZIP文件系统。如果zipname 是某个ZIP文件的名字,那么下面的调用
FileSystem fs =FileSystems.newFilseSystem(Paths.get(zipname),null);
将建立一个文件系统,它包含ZIP文档中的所有文件,如果知道文件名,那么从ZIP文档中复制出这个文件就会变得容易:
Files.copy(fs.getPath(sourceName),targetPath);
以上内容和代码来源于Java 核心技术卷Ⅱ (原书第九版)