Path和Files类封装了在用户机器上处理文件系统所需的所有功能。Path接口和Files类是在Java 7中新添加进来的,它们用起来比自JDK 1.0以来就一直使用的File类要方便得多。
Path
Path(路径)表示的是一个目录名序列,其后还可以跟着一个文件名。路径中的第一个部件可以是根部件,例如/
或C:\
,而允许访问的根部件取决于文件系统。以根部件开始的路径是绝对路径;否则,就是相对路径。例如,我们要分别创建一个绝对路径和一个相对路径:
Path absolute = Paths.get("/home","harry");
Path relative = Paths.get("myprog","conf","user.properties");
静态的Paths.get
方法接受一个或多个字符串,并将它们用默认文件系统的路径分隔符(类UNIX文件系统是/
,Windows是\
)连接起来。然后它解析连接起来的结果,如果其表示的不是给定文件系统中的合法路径,那么就抛出InvalidPathException异常。这个连接结果就是一个Path对象。
get
方法可以获取包含多个部件的单个字符串,例如 ,可以像下面这样从配置文件中读取路径:
String baseDir = props.getProperty("base.dir");
Path basePath = Paths.get(baseDir);
路径不必对应着某个实际存在的文件,它仅仅是一个抽象的名字序列。当你想要创建文件时,首先要创建一个路径,然后才调用方法去创建对应的文件。
组合或解析路径是司空见惯的操作,调用p.resolve(q)
将按照下列规则返回一个路径:
- 如果q是绝对路径,则结果就是q。
- 否则,根据文件系统的规则,将“p后面跟着q”作为结果。
例如,假设你的应用系统需要查找相对于给定基目录的工作目录,其中基目录是从配置文件中读取的:
Path workRelative = Paths.get("work");
Path workPath = basePath.resolve(workRelative);
resolve
方法有一种快捷方式,它接受一个字符串而不是路径:
Path workPath = basePath.resolve("work");
还有一个很方便的方法resolveSibling
,它通过解析指定路径的父路径产生其兄弟路径。例如,如果workPath是/opt/myapp/work
,那么下面的调用:
Path tempPath = workPath.resolveSibling("temp");
将创建/opt/myapp/temp
。
resolve
的对立面是relativize
,即调用p.relativize(r)
将产生路径q,而对q进行解析的结果正是r。例如,以“/home/harry”为目标对“/home/fred/input.txt”进行相对化操作,会产生“…/fred/input.txt”,其中,我们假设..
表示文件系统中的父目录。
normalize
方法将移除所有冗余的.
和..
部件(或者文件系统认为冗余的所有部件)。例如,规范化/home/harry/../fred/./input.txt
将产生/home/fred/input.txt
。
toAbsolutePath
方法将产生给定路径的绝对路径,该绝对路径从根部件开始,例如/home/fred/input.txt
或c:\Users\fred\input.txt
。
Path类有许多有用的方法用来将路径断开。例如:
Path p = Paths.get("/home","fred","myprog.properties");
Path parent = p.getParent(); // the path /home/fred
Path file = p.getFileName(); // the path myprog.properties
Path root = p.getRoot(); // the path /
当需要与遗留系统的API交互,它们使用的是File类而不是Path接口。Path接口有一个toFile方法,而File类有一个toPath方法。
读写文件
Files类可以使得普通文件操作变得快捷。例如,可以用下面的方式很容易地读取文件的所有内容:
byte[] bytes = Files.readAllBytes(path);
但是如果希望将文件当作行序列读入,那么可以调用:
List<String> lines = Files.readAllLines(path,charset);
向指定文件追加内容,可以调用:
Files.write(path,content.getBytes(charset),StandardOpenOption.APPEND);
还可以用下面语句将一个行的集合写出到文件中:
Files.write(path,lines,charset);
这些简便的方法适用于处理中等长度的文本文件,如果要处理的文件长度比较大,或者是二进制文件,那么还是应该使用所熟知的输入/输出流或者读入器/写出器:
InputStream in = Files.newInputStream(path);
OutputStream out = Files.newOutputStream(path);
Reader in = Files.newBufferedReader(path,charset);
Writer out = Files.newBufferedWriter(path,charset);
这些便捷方法可以将你从处理FileInputStream、FileOutputStream、BufferedReader和BufferedWriter的繁复操作中解脱出来。
创建文件和目录
创建新目录可以调用:
Files.createDirectory(path);
其中,路径中除最后一个部件除外,其他部分必须是已存在的。要创建路径中的中间目录,应该使用:
Files.createDirectories(path);
可以使用下面的语句创建一个空文件:
Files.createFile(path);
如果文件已经存在了,那么这个调用就会抛出异常。检查文件是否存在和创建文件是原子性的,如果文件不存在,该文件就会被创建,并且其他程序在此过程中是无法执行文件创建操作的。
有些便捷方法可以用来在给定位置或系统指定位置创建临时文件或临时目录:
Path newPath = Files.createTempFile(dir,prefix,suffix);
Path newPath = Files.createTempFile(prefix,suffix);
Path newPath = Files.createTempDirectory(dir,prefix);
Path newPath = Files.createTempDirectory(prefix);
其中,dir是一个Path对象,prefix和suffix是可以为null的字符串。
在创建文件或目录时,可以指定属性,例如文件的拥有者和权限。但是,指定属性的细节取决于文件系统。
复制、移动和删除文件
将文件从一个位置复制到另一个位置可以直接调用:
Files.copy(fromPath,toPath);
移动文件(即复制并删除原文件)可以调用:
Files.move(fromPath,toPath);
如果目标路径已经存在,那么复制或移动将失败。如果想要覆盖已有的目标路径,可以使用REPLACE_EXISTING
选项。如果想要复制所有的文件属性,可以使用COPY_ATTRIBUTES
选项。也可以像下面这样同时选择这两个选项:
Files.copy(fromPath,toPath,StandardCopyOption.REPLACE_EXISTING,StandardCopyOption.COPY_ATTRIBUTES);
可以将移动操作定义为原子性的,这样就可以保证要么移动操作成功完成,要么源文件继续保持在原来位置:
Files.copy(fromPath,toPath,StandardCopyOption.ATOMIC_MOVE);
你还可以将一个输入流复制到Path中,这表示想要将该输入流存储到硬盘上。类似地,你可以将一个Path复制到输出流中:
Files.copy(inputStream,toPath);
Files.copy(fromPath,outputStream);
最后,删除文件可以调用:
Files.delete(path);
如果要删除的文件不存在,这个方法就会抛出异常。因此,可转而使用下面的方法:
boolean deleted = Files.deleteIfExists(path);
该删除方法还可以用来移除空目录。
获取文件信息
下面的静态方法都将返回一个boolean值,表示检查路径的某个属性的结果:
- exists
- isHidden
- isReadable、isWritable、isExecutable
- isRegularFile、isDirectory、isSymbolicLink
size方法将返回文件的字节数:
long filesize = Files.size(path);
getOwner方法将文件的拥有者作为java.nio.file.attribute.UserPrincipal的一个实例返回。
所有的文件系统都会报告一个基本属性集,它们被封装在BasicFileAttributes接口中,这些属性与上述信息有部分重叠。基本文件属性包括:
- 创建文件、最后一次访问以及最后一次修改文件的时间,这些时间都表示成java.nio.file.attribute.FileTime。
- 文件是常规文件、目录还是符号链接,抑或是这三者都不是。
- 文件尺寸
- 文件主键,这是某种类的对象,具体所属类与文件系统相关,有可能是文件唯一标识符,也可能不是。
要获取这些属性,可以调用:
BasicFileAttributes attributes = Files.readAttributes(path,BasicFileAttributes.class);