代码之家  ›  专栏  ›  技术社区  ›  Austin Hyde

获取/关闭资源时的Java try/catch/finally最佳实践

  •  40
  • Austin Hyde  · 技术社区  · 15 年前

    在做学校项目时,我写了以下代码:

    FileOutputStream fos;
    ObjectOutputStream oos;
    try {
        fos = new FileOutputStream(file);
        oos = new ObjectOutputStream(fos);
    
        oos.writeObject(shapes);
    } catch (FileNotFoundException ex) {
        // complain to user
    } catch (IOException ex) {
        // notify user
    } finally {
        if (oos != null) oos.close();
        if (fos != null) fos.close();
    }
    

    resource.close() 线抛出一个 IOException 因此必须被抓住或宣布。它也在抱怨 oos fos 可能尚未初始化(尽管进行了空检查)。

    这似乎有点奇怪,因为整个问题是如何阻止 就在那里。

    我下意识的解决方法是:

    } finally {
        try {
            if (oos != null) oos.close();
            if (fos != null) fos.close();
        } catch (IOException ex) { }
    }
    

    我来自一个C#背景,在那里我只会利用 using 布洛克,所以我不确定处理这个问题的“正确”方法是什么。

    什么 处理这个问题的正确方法?

    8 回复  |  直到 15 年前
        1
  •  55
  •   Stephen C    7 年前

    如果您试图从源代码捕获并报告所有异常,则更好的解决方案是:

    ObjectOutputStream oos = null;
    try {
       oos = new ObjectOutputStream(new FileOutputStream(file));
       oos.writeObject(shapes);
       oos.flush();
    } catch (FileNotFoundException ex) {
        // complain to user
    } catch (IOException ex) {
        // notify user
    } finally {
        if (oos != null) {
            try {
                oos.close();
            } catch (IOException ex) {
                // ignore ... any significant errors should already have been
                // reported via an IOException from the final flush.
            }
        }
    }
    

    笔记:

    • 标准的Java包装流、读写器都会传播 close flush 所以你只需要关闭或冲洗最外层的包装。
    • 在try块末尾显式刷新的目的是使 IOException 查看任何写入失败 .
    • 当对输出流执行关闭或刷新操作时,有一种“千载难逢”的可能性,即由于磁盘错误或文件系统已满而引发异常。 .

    如果您经常不得不“关闭一个可能为空的流,忽略IOExceptions”,那么您可以为自己编写一个这样的helper方法:

    public void closeQuietly(Closeable closeable) {
        if (closeable != null) {
            try {
                closeable.close();
            } catch (IOException ex) {
                // ignore
            }
        }
    }
    

    然后,您可以将上一个finally块替换为:

    } finally {
        closeQuietly(oos);
    }
    

    closeQuietly 方法已在ApacheCommons库中可用。。。如果您不介意为10行方法向项目添加依赖项。 更新 :请注意,这些方法在API的2.6版中已弃用。)

    悄悄地 在IO异常的流上 真正地 都无关紧要。

    1-这在使用try with resources时是不必要的。


    关于 flush() close() 人们在问:

    • 关闭() 导致刷新所有缓冲输出。你 应该 发现所有其他执行输出缓冲的(标准)输出类的行为都将相同。所以,对于一个标准类,调用 刷新() 紧接着 关闭()
    • 对于自定义类和第三方类,您需要进行调查(例如,阅读javadoc,查看代码),但任何 关闭() 不刷新缓冲数据的方法可以说是 破碎的
    • 最后一个问题是 刷新() OutputStream ...)

      如果此流的预期目的地是底层操作系统(例如文件)提供的抽象,则刷新该流仅保证先前写入该流的字节被传递给操作系统进行写入;它不能保证它们实际上被写入物理设备,如磁盘驱动器。

      所以。。。如果你希望/想象一下 保证您的数据将持续存在, 你错了! (如果你需要这样做,看看 FileChannel.force 方法…)


    如果您的新代码没有使用Java 7或更高版本,那么您很可能深陷其中。

        2
  •  26
  •   Phil    13 年前

    try/catch/finally涉及可关闭对象(例如文件)的当前最佳实践是使用Java 7的try with resource语句,例如:

    try (FileReader reader = new FileReader("ex.txt")) {
        System.out.println((char)reader.read());
    } catch (IOException ioe) {
        ioe.printStackTrace();
    }
    

    http://ppkwok.blogspot.com/2012/11/java-cafe-2-try-with-resources.html

    官方Java描述如下:

    http://docs.oracle.com/javase/7/docs/technotes/guides/language/try-with-resources.html

        3
  •  14
  •   Mike Clark    11 年前

    Java 7将添加 Automatic Resource Management 阻碍。它们与C#非常相似 using .

    乔希·布洛赫写道 the technical proposal

    下面是一个Asker的代码示例,它被转换成ARM形式:

    try (FileOutputStream fos = new FileOutputStream(file);
            ObjectOutputStream oos = new ObjectOutputStream(fos)) 
    {
        oos.writeObject(shapes);
    }
    catch (FileNotFoundException ex) 
    {
        // handle the file not being found
    }
    catch (IOException ex) 
    {
        // handle some I/O problem
    }
    
        4
  •  4
  •   maximdim    15 年前

    我通常有一个小类IOUtil,其方法如下:

    public static void close(Closeable c) {
        if (c != null) {
            try {
                c.close();
            }
            catch (IOException e) {
                // ignore or log
            }
        }
    }
    
        5
  •  3
  •   RAY    15 年前

    这些人呢?没有空支票,没有惊喜。所有东西在出口时都被清理干净。

    try {
        final FileOutputStream fos = new FileOutputStream(file);
        try {
            final ObjectOutputStream oos = new ObjectOutputStream(fos);
            try {
                oos.writeObject(shapes);
                oos.flush();
            }
            catch(IOException ioe) {
                // notify user of important exception
            }
            finally {
                oos.close();
            }
        }
        finally {
            fos.close();
        }
    }
    catch (FileNotFoundException ex) {
        // complain to user
    }
    catch (IOException ex) {
        // notify user
    }
    
        7
  •  1
  •   Mike    15 年前

    你做得对。我也很烦。您应该显式地将这些流初始化为null—这是常见的约定。你能做的就是加入俱乐部 using .

        8
  •  0
  •   CurtainDog    15 年前

    这不是对你观点的直接回答,但这是一个不幸的事实,因为 finally catch try 人们认为他们属于一起。最好的设计 尝试 积木要么有一个 抓住 最后 但不是两者都有。

    在这种情况下,你的评论暗示出了问题。为什么,在处理文件IO的方法中,我们会向用户抱怨任何事情。我们可能在某个服务器上运行很长时间,却看不到用户。

    所以,上面的代码应该有 最后 抓住

    推荐文章