本教程是针对JDK 8编写的。本页中描述的示例和实践未充分利用后续版本中引入的改进,并可能使用已不再可用的技术。
请参阅Java语言更改,了解Java SE 9及后续版本中更新的语言特性的摘要。
请参阅JDK发行说明,了解所有JDK版本的新功能、增强功能和已删除或弃用选项的信息。
Files
类是java.nio.file
包的另一个主要入口点。该类提供了一组丰富的静态方法,用于读取、写入和操作文件和目录。这些Files
方法适用于Path
对象的实例。在继续阅读后面的部分之前,您应该熟悉以下常见概念:
在此API中使用的许多资源,如流或通道,实现或扩展了java.io.Closeable
接口。Closeable
资源的要求是在不再需要时调用close
方法来释放资源。忽略关闭资源可能会对应用程序的性能产生负面影响。下一节中介绍的try-
with-resources语句可以处理此步骤。
在文件I/O中,意外情况是生活中的事实:期望的文件存在(或不存在),程序无法访问文件系统,默认文件系统实现不支持特定功能等等。可能会遇到许多错误。
所有访问文件系统的方法都可能抛出IOException
。最佳实践是通过将这些方法嵌入到try-
with-resources语句中来捕获这些异常,该语句在Java SE 7版本中引入。try-
with-resources语句的优点是,编译器会在不再需要时自动生成关闭资源的代码。以下代码显示了这种写法:
Charset charset = Charset.forName("US-ASCII"); String s = ...; try (BufferedWriter writer = Files.newBufferedWriter(file, charset)) { writer.write(s, 0, s.length()); } catch (IOException x) { System.err.format("IOException: %s%n", x); }
更多信息,请参阅try-with-resources语句。
或者,您可以将文件I/O方法嵌入到try
块中,然后在catch
块中捕获任何异常。如果您的代码已经打开了任何流或通道,应该在finally
块中关闭它们。使用try-catch-finally方法,前面的示例将如下所示:
Charset charset = Charset.forName("US-ASCII"); String s = ...; BufferedWriter writer = null; try { writer = Files.newBufferedWriter(file, charset); writer.write(s, 0, s.length()); } catch (IOException x) { System.err.format("IOException: %s%n", x); } finally { if (writer != null) writer.close(); }
更多信息,请参见捕获和处理异常。
除了IOException
之外,许多特定的异常都扩展自FileSystemException
。这个类有一些有用的方法,可以返回涉及的文件(getFile
),详细的消息字符串(getMessage
),文件系统操作失败的原因(getReason
),以及涉及的“其他”文件(如果有)(getOtherFile
)。
以下代码片段展示了如何使用getFile
方法:
try (...) { ... } catch (NoSuchFileException x) { System.err.format("%s 不存在\n", x.getFile()); }
为了清晰起见,本课程中的文件I/O示例可能没有显示异常处理,但您的代码应始终包含异常处理。
当指定标志时,Files
中的几个方法接受任意数量的参数。例如,在下面的方法签名中,CopyOption
参数后的省略符号表示该方法接受可变数量的参数,通常称为可变参数:
Path Files.move(Path, Path, CopyOption...)
当方法接受可变参数时,您可以将逗号分隔的值列表或值数组(CopyOption[]
)传递给它。
在move
示例中,可以如下调用该方法:
import static java.nio.file.StandardCopyOption.*; Path source = ...; Path target = ...; Files.move(source, target, REPLACE_EXISTING, ATOMIC_MOVE);
有关可变参数语法的更多信息,请参见任意数量的参数。
一些Files
方法,例如move
,可以在某些文件系统中执行某些操作。
原子文件操作是一种无法中断或“部分”执行的操作。要么完全执行该操作,要么操作失败。当多个进程在文件系统的同一区域上操作,并且您需要保证每个进程访问完整的文件时,这是很重要的。
许多文件I/O方法支持方法链的概念。
首先调用一个返回对象的方法。然后立即在该对象上调用一个方法,该方法又返回另一个对象,依此类推。许多I/O示例使用以下技术:
String value = Charset.defaultCharset().decode(buf).toString(); UserPrincipal group = file.getFileSystem().getUserPrincipalLookupService(). lookupPrincipalByName("me");
这种技术产生了紧凑的代码,并且使您可以避免声明不需要的临时变量。
Files
类中的两个方法接受一个glob参数,但是什么是glob?
您可以使用glob语法来指定模式匹配行为。
glob模式是指定为字符串的模式,并与其他字符串(例如目录或文件名)匹配。Glob语法遵循几个简单的规则:
*
匹配任意数量的字符(包括零个)。**
与*
类似,但跨越目录边界。该语法通常用于匹配完整路径。?
匹配正好一个字符。{sun,moon,stars}
匹配"sun"、"moon"或"stars"。{temp*,tmp*}
匹配以"temp"或"tmp"开头的所有字符串。-
时)一系列字符。例如:
[aeiou]
匹配任何小写元音字母。[0-9]
匹配任何数字。[A-Z]
匹配任何大写字母。[a-z,A-Z]
匹配任何大写或小写字母。*
、?
和\
匹配它们自己。*
、?
或其他特殊字符,可以使用反斜杠字符\
进行转义。例如:\\
匹配单个反斜杠,\?
匹配问号。以下是一些glob语法的示例:
*.html
- 匹配所有以 .html 结尾的字符串???
- 匹配所有包含三个字母或数字的字符串*[0-9]*
- 匹配所有包含数字值的字符串*.{htm,html,pdf}
- 匹配任何以 .htm、.html 或 .pdf 结尾的字符串a?*.java
- 匹配任何以 a
开头,后跟至少一个字母或数字,并以 .java 结尾的字符串{foo*,*[0-9]*}
- 匹配任何以 foo 开头的字符串或任何包含数字值的字符串"*"
),使用反斜杠(\*
),或使用命令行支持的任何转义机制。
glob语法功能强大且易于使用。但是,如果它不满足您的需求,您还可以使用正则表达式。更多信息,请参阅正则表达式教程。
有关glob语法的更多信息,请参阅FileSystem
类中的getPathMatcher
方法的API规范。
Files
类是“链接感知”的。每个Files
方法要么检测遇到符号链接时该执行什么操作,要么提供一个选项,使您能够配置遇到符号链接时的行为。