当前位置: 首页 > 知识库问答 >
问题:

Java8流并尝试使用资源

卫骏
2023-03-14

我认为流API在这里是为了使代码更易于阅读。我觉得有点烦。流接口扩展了java。lang.AutoCloseable接口。

因此,如果你想正确地关闭流,你必须使用try-with资源。

清单1.不是很好,流没有关闭。

public void noTryWithResource() {
    Set<Integer> photos = new HashSet<Integer>(Arrays.asList(1, 2, 3));

    @SuppressWarnings("resource") List<ImageView> collect = photos.stream()
        .map(photo -> new ImageView(new Image(String.valueOf(photo))))
        .collect(Collectors.<ImageView>toList());
}

清单2.使用2嵌套try

public void tryWithResource() {
    Set<Integer> photos = new HashSet<Integer>(Arrays.asList(1, 2, 3));

    try (Stream<Integer> stream = photos.stream()) {
        try (Stream<ImageView> map = stream
                .map(photo -> new ImageView(new Image(String.valueOf(photo)))))
        {
            List<ImageView> collect = map.collect(Collectors.<ImageView>toList());
        }
    }
}

清单3。当map返回流时,必须关闭stream()和map()函数。

public void tryWithResource2() {
    Set<Integer> photos = new HashSet<Integer>(Arrays.asList(1, 2, 3));

    try (Stream<Integer> stream = photos.stream(); Stream<ImageView> map = stream.map(photo -> new ImageView(new Image(String.valueOf(photo)))))
    {
        List<ImageView> collect = map.collect(Collectors.<ImageView>toList());
    }
}

我举的例子毫无意义。为了示例,我将jpg图像的路径替换为整数。但不要让这些细节分散你的注意力。

使用这些可自动关闭的流的最佳方式是什么。我不得不说,我对我展示的3个选项中的任何一个都不满意。你怎么认为?还有其他更优雅的解决方案吗?

共有3个答案

夹谷成龙
2023-03-14

“可关闭”的意思是“可以关闭”,而不是“必须关闭”。

过去是这样的,例如,请参阅ByteArrayOutputStream:

通过tearrayoutputstream关闭

现在,这对于流是正确的,文档中明确指出:

Streams有一个BaseStream.close()方法并实现AutoCloseable,但几乎所有的流实例在使用后实际上并不需要关闭,一般只有源为IO通道的流(如Files.lines(Path, Charset)返回的流)才会需要关闭。

因此,如果审计工具生成错误警告,那是审计工具的问题,而不是API的问题。

注意,即使要添加资源管理,也不需要嵌套try语句。虽然以下内容足够:

final Path p = Paths.get(System.getProperty("java.home"), "COPYRIGHT");
try(Stream<String> stream=Files.lines(p, StandardCharsets.ISO_8859_1)) {
    System.out.println(stream.filter(s->s.contains("Oracle")).count());
}

您还可以将辅助流添加到资源管理中,而无需额外的try:

final Path p = Paths.get(System.getProperty("java.home"), "COPYRIGHT");
try(Stream<String> stream=Files.lines(p, StandardCharsets.ISO_8859_1);
    Stream<String> filtered=stream.filter(s->s.contains("Oracle"))) {
    System.out.println(filtered.count());
}

通鸿风
2023-03-14

只有当流需要自己进行任何清理(通常是I/O)时,才需要关闭流。您的示例使用哈希集,因此它不需要关闭。

来自Stream javadoc:

通常,只有源为IO通道的流(如Files.lines(Path, Charset)返回的流)才需要关闭。大多数流由集合、数组或生成函数支持,不需要特殊的资源管理。

因此,在您的示例中,这应该可以正常工作

List<ImageView> collect = photos.stream()
                       .map(photo -> ...)
                       .collect(toList());

编辑

即使您需要清理资源,您也应该能够只使用一次资源试用。假设您正在读取一个文件,其中文件中的每一行都是图像的路径:

 try(Stream<String> lines = Files.lines(file)){
       List<ImageView> collect = lines
                                  .map(line -> new ImageView( ImageIO.read(new File(line)))
                                  .collect(toList());
  }
慕铭
2023-03-14

您正在使用@SuppressWarnings("资源"),它可能会抑制有关未关闭资源的警告。这不是javac发出的警告之一。网络搜索似乎表明,如果AutoCloseable未关闭,Eclipse会发出警告。

根据引入自动关闭的Java 7规范,这是一个合理的警告:

不再需要时必须关闭的资源。

然而,AutoCloseable的Java 8规范被放宽,以删除“必须关闭”子句。它现在部分地说,

可能包含资源的对象。。。直到它关闭。

基类实现AutoCloseable是可能的,事实上也是常见的,即使不是它的所有子类或实例都包含可释放的资源。对于必须完全通用地运行的代码,或者当知道AutoCloseable实例需要资源释放时,建议使用资源尝试结构。但是,当使用同时支持基于I/O和非基于I/O的表单的工具(如Stream)时,使用非基于I/O的表单时,资源尝试块通常是不必要的。

Lambda专家组广泛讨论了这个问题;此消息总结了决策。除其他事项外,它提到了对可自动关闭规范(上文引用)和基流规范(其他答案引用)的更改。它还提到可能需要为更改的语义调整Eclipse代码检查器,可能不会无条件地为可自动关闭的对象发出警告。显然,这条消息没有传到Eclipse的人那里,或者他们还没有改变它。

总之,如果Eclipse警告导致您认为需要关闭所有可自动关闭的对象,那么这是不正确的。只有某些特定的可自动关闭的对象需要关闭。Eclipse需要修复(如果尚未修复)不为所有可自动关闭的对象发出警告。

 类似资料:
  • 我目前正在进行一个项目,该项目使用HP的Fortify SCA工具来捕捉代码库中的安全问题。我在确定正确处理JDBC资源的最佳方法时遇到了一些问题。 我现在拥有的代码如下所示; 问题是Fortify会标记这段代码,说明如果嵌套的try语句中发生异常,那么对conn和ps的引用将丢失,它们将无法正确关闭。fortify标记这一点是正确的还是误报?据我所知,try with资源应该总是关闭它们的资源,

  • 问题内容: 我一直在看代码,并且看到了尝试资源的机会。我以前使用过标准的try-catch语句,看起来它们在做同样的事情。所以我的问题是“ 尝试使用资源”与“尝试捕获 ”之间的区别是什么,哪个更好。 这是尝试使用资源: 问题答案: 尝试使用资源的重点是确保可靠地关闭资源。 当你不使用try-with-resources时,存在一个潜在的陷阱,称为异常屏蔽。当try块中的代码引发异常,而finall

  • 我知道,如果资源已实现自动关闭,您通过尝试传递的资源将自动关闭。到现在为止,一直都还不错。但是,当我有几个我想要自动关闭的资源时,我该怎么办呢。带插座的示例; 所以我知道套接字将被正确关闭,因为它在try中作为参数传递,但是输入和输出应该如何正确关闭呢?

  • 我需要打开N个多播套接字(其中N来自参数列表的大小)。然后,我将向循环中的N个套接字中的每个套接字发送相同的数据,最后关闭每个套接字。我的问题是,如何使用try with resources块来实现这一点?以下是我将如何使用单个资源来实现这一点: 我能想到的使用多个端口执行此操作的唯一方法如下: 有没有一种更简洁的方法来实现这一点,或者我提出的解决方案是否尽可能好?

  • 不管错误情况如何,使用资源尝试是否总是关闭资源?我的意思是考虑以下代码: 会一直关闭吗?我读过Oracle文档,其中说: 无论try语句是正常完成还是突然完成,它都将关闭 因此无论程序正常运行还是抛出异常,它都将起作用。但是,类似或崩溃的情况怎么办?我知道这些条件对块不起作用。那么,使用资源尝试失败的条件是否存在? 这只是我请求的好奇心,谁能说明这一点吗?

  • 问题内容: 使用对象,我们使用3个基本步骤: 宣言 实例化 初始化 我的问题是,为了自动关闭将要使用的资源,必须在试用过程中执行哪些步骤。 示例1-将在以下代码中自动关闭FileReader对象: 示例2-将使用以下代码自动关闭buf2: PS有人认为这个问题是Try With Resources vs Try-Catch的重复。它不是。这个问题是关于try-catch和try-with-reso