java |关闭资源的最安全方法[重复]

de90aj5v  于 2023-04-19  发布在  Java
关注(0)|答案(2)|浏览(107)

此问题已在此处有答案

Try With Resources vs Try-Catch [duplicate](5个答案)
What's the purpose of try-with-resources statements?(7个回答)
Close a Scanner linked to System.in(5个答案)
2天前关闭。

提问

在Java中关闭“I/O资源”的最好/最安全的方法是什么?

上下文

来自python背景,它使用with创建一个上下文管理器语句来阅读和写入文件,它确保,如果关闭/清理上下文的资源(技术上这可以是任何东西,但内置在io操作中,例如一旦超出范围,即使是套接字也会关闭)
因此,使用此代码:

file = open('workfile', encoding="utf-8")
read_data = f.read()
file.close()

推荐使用with语句

with open('workfile', encoding="utf-8") as file:
    read_data = file.read()

当我们退出with作用域时(字面意思是,前一个缩进级别),即使发生了错误,文件也将始终关闭
你确定我们能做到吗

try:
    file = open('workfile', encoding="utf-8")
    read_data = f.read()
except Exception as _: 
    pass
finally:
    file.close()

但是,大多数情况下,上下文管理器更好。一个原因是,简单地说,有些人可能会忘记写下file.close(),但也简单地更干净和安全。

Java示例代码

现在在java中,什么是推荐的,类似的为什么要处理这个?
Scanner为例:

public class App 
{
    public static void main( String... args )
    {
        Scanner reader = new Scanner(System.in);
        try {
            System.out.print(">>> username: ");
            if (reader.hasNextLine()) {
                String username = reader.nextLine();
                System.out.format("Hello %s", username);
            }
        } catch (Exception error) {
            System.out.format("Error while reading scanner %s", error);
        } finally {
            reader.close();
        }

    }
}

如你所见,我就是这么处理的,

这是java方式吗?
xjreopfe

xjreopfe1#

我认为你可以使用try(...){...}catch{}语句(try with resource statement)
例如:

try (
           Scanner reader = new Scanner(System.in);
           // other resource initalization
 ) {
            System.out.print(">>> username: ");
            if (reader.hasNextLine()) {
                String username = reader.nextLine();
                System.out.format("Hello %s", username);
            }
        } catch (Exception error) {
            System.out.format("Error while reading scanner %s", error);
        }

从JDK 1.7开始,try后面的()用于初始化程序结束后必须关闭的一个或多个资源。

ubby3x7f

ubby3x7f2#

好吧,这是一个很老的功能,有很好的文档记录,我只是没有看到它。这个功能被称为The try-with-resources Statement
我保留@shmily作为公认的答案,因为它是正确的,它回答了问题(也是第一个),我只会在这里添加一点,因为它可能会有所帮助,特别是我会为我的用例添加代码,我需要它。

另外,阅读关于用户“Hovercraft Full Of Eels”关于我的实现的最后一条注解

所以在它的简单形式下,我们可以用它作为

public static void main( String... args )
    {
        try (Scanner reader = new Scanner(System.in)) {
            System.out.print(">>> username: ");
            if (reader.hasNextLine()) {
                String username = reader.nextLine();
                System.out.format("Hello %s", username);
            }
        } catch (NoSuchElementException | IllegalStateException error) {
            System.out.println("Error while reading scanner");
            error.printStackTrace();
        }
    }

现在,根据www.example.com的文档baeldung.com/java-try-with-resources,它表明你实际上可以在外部声明它,只要是 final

final Scanner scanner = new Scanner(new File("testRead.txt"));
PrintWriter writer = new PrintWriter(new File("testWrite.txt"))
try (scanner;writer) { 
    // omitted
}

现在,对于我的特定用例,以及第一次评估,我不建议总是这样(因为,实际上即使在他们的示例中,PrintWriter也可能抛出错误FileNotFoundException),因此如果该错误是预先捕获的或预期的,那么它是有意义的。

public static void main( String... args )
    {
        String fileName = "output.txt";
        final Scanner readerTerminal = new Scanner(System.in);
        try (
                readerTerminal;
                PrintWriter writer = new PrintWriter("output.txt");
        ) {
            System.out.print(">>> username: ");
            if (readerTerminal.hasNextLine()) {
                String username = readerTerminal.nextLine();
                System.out.format("Hello %s", username);

                writer.write(username);
            }
        } catch (FileNotFoundException error) {
            System.out.format("File %s not found, reason: %s", fileName, error);
            error.printStackTrace();
        } catch (NoSuchElementException | IllegalStateException error) {
            System.out.format("Error while reading scanner, reason: %s", error);
            error.printStackTrace();
        } catch (Exception error) {
            System.out.format("Something went wrong while reading scanner, reason: %s", error);
            error.printStackTrace();
        }
    }

如果reader来自函数参数,则这尤其有意义。
话虽如此,这个特性**非常非常类似于python context manager*,你可以定义/自定义你自己的实现AutoCloseable的类。

  • https://www.baeldung.com/java-try-with-resources#custom
public class MyResource implements AutoCloseable {
    @Override
    public void close() throws Exception {
        System.out.println("Closed MyResource");
    }
}

只要它调用close,你就可以用它做任何你想做的事情(任何类型的清理,资源关闭,API关闭等等)。
我看到的唯一区别(至少从用户的Angular 来看,显然每种语言上的实现可能非常不同)是,要在java启用此功能,您确实需要将其放入try/catch块中,而在python中,它完全独立于错误捕获场景。

为什么这是相关的?

在我看来,将它作为一个try-catch场景似乎限制了你实际上可以使用它做什么,特别是,你想要关闭该资源不是因为引发了错误,而是因为它存在于作用域!,这实际上就是它的工作方式,但它可能会让你认为,它只在引发时关闭。
在下面的例子中,我展示了如何在不需要抛出异常的情况下使用它,但是,老实说,它看起来并不像Python那样明显(这不是世界末日)。
正常自动关闭

public class Main
{
    public static class MyResource implements AutoCloseable {
        @Override
        public void close() throws Exception {
            System.out.println("Resource closed, let's call some resource ... ");
        }
    }

    public static void main( String[] args )
    {
        final MyResource resource1 = new MyResource();

        try (resource1; ) {
            System.out.println("Inside Try - catch block");
        } catch (Exception error) {
            error.printStackTrace();
        }

    }
}

但是如果我不想抛出错误呢?,我只想调用一些其他的东西,我想确保在对象关闭时被调用。

public class Main
{
    public static class MyResource implements AutoCloseable {
        @Override
        public void close() {
            System.out.println("Resource closed, let's call some resource ... ");
        }
    }

    public static void main( String[] args )
    {
        final MyResource resource1 = new MyResource();

        try (resource1; ) {
            System.out.println("Inside Try - catch block");
        }

    }
}

这样做,它看起来非常类似于python版本,我不需要捕获(和抛出)任何东西,因为这不是我特定的autoclosable的重点,
很酷的一点是,任何在try-with-resource中发生的未捕获的错误仍然会调用
close方法

public class Main
{
    public static class MyResource implements AutoCloseable {
        @Override
        public void close() {
            System.out.println("Resource closed, let's call some resource ... ");
        }
    }

    public static void main( String[] args ) throws FileNotFoundException {
        final MyResource resource1 = new MyResource();

        try (resource1; ) {
            System.out.println("Inside Try - catch block");
            final Scanner readerTerminal = new Scanner(new File("dont_exits.txt"));
        }

    }
}

哪些输出

Inside Try - catch block
Resource closed, let's call some resource ... 
Exception in thread "main" java.io.FileNotFoundException: dont_exits.txt (No such file or directory)
    at java.base/java.io.FileInputStream.open0(Native Method)
    at java.base/java.io.FileInputStream.open(FileInputStream.java:216)
    at java.base/java.io.FileInputStream.<init>(FileInputStream.java:157)
    at java.base/java.util.Scanner.<init>(Scanner.java:639)
    at com.koubae.app1.Main.main(Main.java:30)

Process finished with exit code 1

《Hovercraft Full Of Eels》评论

Java有try-with-resources,这可能是最好的方法,除非在某些情况下,其中一种情况是你正在使用System.in(就像你正在做的那样),并希望避免过早关闭它。一旦关闭,该流将保持关闭状态。
所以,尽管上面的例子,我不认为你应该关闭一个扫描System.in的扫描仪(但可能有原因)

public static void main( String[] args )
    {
        final Scanner readerUserTerminal = new Scanner(System.in);
        try (
            readerUserTerminal;
        ) {
            System.out.print(">>> username: ");
            if (readerUserTerminal.hasNextLine()) {
                String username = readerUserTerminal.nextLine();
                System.out.format("Hello %s", username);
            }
        } catch (NoSuchElementException | IllegalStateException error) {
            System.out.format("Error while reading scanner, reason: %s", error);
            error.printStackTrace();
        } catch (Exception error) {
            System.out.format("Something went wrong while reading scanner, reason: %s", error);
            error.printStackTrace();
        }
        // yea no ... will break...
        Scanner second = new Scanner(System.in);
        System.out.print(">>> username: ");
        String username2 = second.nextLine();
        System.out.format("Hello %s", username2);


    }

输出

Hello fede>>> username: Exception in thread "main" java.util.NoSuchElementException: No line found
    at java.base/java.util.Scanner.nextLine(Scanner.java:1651)
    at com.koubae.app1.Main.main(Main.java:39)

所以要小心!,我其实很惊讶,因为,在许多许多教程中,他们使用扫描仪从终端读取用户输入,然后显示关闭它,which,不是一个文件,但System.in(对我来说就像stdin像在C或Python sys.stdin.相当可怕的教程水平在那里.小心!:D

相关问题