当前教程
释放资源和捕获异常
系列中的下一个

系列中的下一个: 读取和写入小文件

释放资源和捕获异常

 

释放系统资源

此 API 中使用的许多资源(例如流或通道)实现或扩展了 java.io.Closeable 接口。一个 Closeable 资源的要求是必须调用 close() 方法以在不再需要时释放资源。忽略关闭资源可能会对应用程序的性能产生负面影响。下一节中描述的try-with-resources语句将为您处理此步骤。

关闭资源

为了简单起见,前面的示例省略了两件事:异常处理和关闭读取器。

所有 I/O 操作在 Java I/O API 中抛出相同的默认异常:IOException。根据您访问的资源类型,可能会抛出更多异常。例如,如果您的reader从文件读取字符,您可能需要处理FileNotFoundException

在您的应用程序中关闭 I/O 资源是必须的。如果让资源未关闭,最终会导致您的应用程序崩溃。

从 Java SE 7 开始,可以使用try-with-resources语句关闭 I/O 资源。让我们使用此模式重写前面的代码。

Path path = Paths.get("file.txt");
try (BufferedReader reader = Files.newBufferedReader(path)) {

    // do something with the reader

} catch (IOException e) {
    // do something with the exception
}

在此示例中,reader对象可以在try块中使用。当程序离开此块时,无论它是正常离开还是异常离开,都会为您调用reader对象的close()方法。

关闭多个资源

您可能会看到使用其构造函数创建的文件读取器和缓冲读取器。这些是在 Java SE 7 中引入 Files 工厂类之前使用的模式。在这种情况下,您将看到创建了几个中间 I/O 资源,这些资源必须以正确的顺序关闭。

在使用文件读取器创建的缓冲读取器的情况下,正确的模式如下所示。

File file = new File("file.txt");

try (FileReader fileReader = new FileReader(file);
     BufferedReader bufferedReader = new BufferedReader(fileReader);) {

    // do something with the bufferedReader or the fileReader

} catch (IOException e) {
    // do something with the exception
}

 

捕获异常

在文件 I/O 中,意外情况是不可避免的:文件在预期时存在(或不存在),程序无法访问文件系统,默认文件系统实现不支持特定功能,等等。可能会遇到许多错误。

所有访问文件系统的函数都可能抛出IOException。最佳实践是通过将这些函数嵌入到 Java SE 7 版本中引入的try-with-resources语句中来捕获这些异常。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 {
    try{
        if (writer != null)
            writer.close();
    } catch (IOException x) {
        System.err.format("IOException: %s%n", x);
    }
}

有关更多信息,请参阅部分 捕获和处理异常

除了IOException之外,许多特定异常扩展了FileSystemException。此类具有一些有用的方法,可以返回所涉及的文件(getFile())、详细的消息字符串(getMessage())、文件系统操作失败的原因(getReason())以及所涉及的“其他”文件(如果有)(getOtherFile())。

以下代码片段显示了如何使用getFile()方法

try (...) {
    ...
} catch (NoSuchFileException x) {
    System.err.format("%s does not exist\n", x.getFile());
}

为了清晰起见,本节中的文件 I/O 示例可能不会显示异常处理,但您的代码应始终包含它。

 

使用可变参数

几个 Files 方法在指定标志时接受任意数量的参数。例如,在以下方法签名中,CopyOption 参数后的省略号表示该方法接受可变数量的参数,或者通常称为可变参数

Path Files.move(Path, Path, CopyOption...)

当方法接受可变参数参数时,您可以传递逗号分隔的值列表或值数组(CopyOption[])。

在以下示例中,该方法可以按如下方式调用

Path source = ...;
Path target = ...;
Files.move(source,
           target,
           REPLACE_EXISTING,
           ATOMIC_MOVE);

有关可变参数语法的更多信息,请参阅部分 任意数量的参数

 

方法链

许多文件 I/O 方法支持方法链的概念。

您首先调用返回对象的方法。然后,您立即调用该对象上的方法,该方法又返回另一个对象,依此类推。许多 I/O 示例使用以下技术

String value = Charset.defaultCharset().decode(buf).toString();
UserPrincipal group =
    file.getFileSystem()
        .getUserPrincipalLookupService()
        .lookupPrincipalByName("me");

此技术可以生成简洁的代码,并使您能够避免声明不需要的临时变量。


上次更新: 2023 年 1 月 25 日


当前教程
释放资源和捕获异常
系列中的下一个

系列中的下一个: 读取和写入小文件