【问题标题】:Java File Handling, what did I do wrong?Java文件处理,我做错了什么?
【发布时间】:2010-05-27 18:45:24
【问题描述】:

为 Java 家庭作业作业编写了一个基本文件处理程序,当我收到作业时,我有一些关于未能捕获一些实例的注释:

  • 文件中的缓冲区可能为空。
  • 找不到文件
  • 文件流未关闭

这是用于打开文件的代码块:

/**
 * Create a Filestream, Buffer, and a String to store the Buffer.
 */
FileInputStream fin = null;
BufferedReader buffRead = null;
String loadedString = null;

/** Try to open the file from user input */
try
{
    fin = new FileInputStream(programPath + fileToParse);
    buffRead = new BufferedReader(new InputStreamReader(fin));
    loadedString = buffRead.readLine();
    fin.close();
}
/** Catch the error if we can't open the file */
catch(IOException e)
{
    System.err.println("CRITICAL: Unable to open text file!");
    System.err.println("Exiting!");
    System.exit(-1);
}

我从他那里得到的一条评论是 fin.close(); 需要在 finally 块中,而我根本没有。但我认为我创建 try/catch 的方式可以防止文件无法打开的问题。

让我澄清一些事情:这不是针对当前的作业(不是试图让别人做我自己的工作),我已经创建了我的项目并已对其进行评分。我自己并没有完全理解教授的推理。最后,我没有太多Java经验,所以我有点困惑为什么我的catch不够好。

【问题讨论】:

  • 虽然在长时间运行的程序中关闭系统资源很重要(当然最好练习一下),但如果你要关闭文件,这有点争议在你的 catch 块中调用 System.exit
  • @jasonmp85 这就是我的想法,但是看起来他确实希望我们标记它,就好像它会一直运行一样。现在这对我来说很有意义。

标签: java filehandle


【解决方案1】:
  • 文件中的缓冲区可能为空。

文件可能为空。也就是说,在打开文件时到达文件结尾。然后loadedString = buffRead.readLine() 将返回 null。

也许您应该通过添加类似 if (loadedString == null) loadedString = ""; 的内容来解决此问题

  • 找不到文件

正如FileInputStream(String) 的构造函数文档中所解释的,它可能会抛出FileNotFoundException。你确实在你的IOException 子句中发现了这一点(因为FileNotFoundException 是一个 IOException),所以没关系,但你也许可以这样做:

} catch (FileNotFoundException fnfe) {
    System.err.println("File not fonud!");
} catch (IOException ioex {
    System.err.println("Some other error");
}
  • 文件流未关闭

您确实调用了fin.close(),这在正常情况下会关闭文件流。也许他的意思是它并不总是关闭。 readLine 可能会抛出IOException,在这种情况下,close() 会被跳过。这就是将它放在finally 子句中的原因(确保无论try-block 中发生什么情况都会调用它。(*)


(*) 正如@mmyers 正确指出的那样,将close() 放入finally 块实际上是不够的,因为您在catch 块中调用System.exit(-1)。如果这确实是所需的行为,您可以在 catch 子句中设置错误标志,如果设置了该标志,则在 finally 子句之后退出。

【讨论】:

  • 如果出现异常,finally子句将不会被执行,因为catch块中的System.exit()。
  • 从技术上讲,FileInputStream 的 finalize-method 在终止时关闭流:-)
  • 而不是这个:'if (loadedString == null) loadedString = "";'我通常会使用 apache commons / lang commons.apache.org/lang/api/org/apache/commons/lang/… 求助于 StringUtils.stripToEmpty()
  • @seanizer:这是学校作业。
  • 谢谢!按照您的解释方式,这很有意义:)
【解决方案2】:

但是,如果您的程序在 try 块的第二行或第三行抛出异常怎么办?

buffRead = new BufferedReader(new InputStreamReader(fin));
loadedString = buffRead.readLine();

此时,文件句柄已打开并分配给fin。您可以捕获异常,但文件句柄将保持打开状态。

您需要将 fin.close() 语句移动到 finally 块:

} finally {
    try {
        if (fin != null) {
            fin.close();
        }
    } catch (IOException e2) {
    }
}

【讨论】:

  • 我认为你应该在尝试关闭它之前添加一个if (fin != null)(以防文件不存在)。
  • 或者,再次使用 apache commons(在这种情况下为 io),只写一行:'IOUtils.closeQuietly(fin);',这正是你正在做的事情,少了很多(本地)代码commons.apache.org/io/apidocs/org/apache/commons/io/…
【解决方案3】:

假设buffRead.readLine() 抛出异常,您的FileInputStream 会被关闭,还是会跳过该行? finally 块的目的是即使在特殊情况下,finally 块中的代码也会执行。

【讨论】:

    【解决方案4】:

    除了打开文件之外,还有很多其他错误可能发生。

    最后你可能会得到一个已定义或未定义的 fin ,你必须防止空指针错误,并且不要忘记关闭文件可能会引发新的异常。

    我的建议是在一个单独的例程中捕获它并让 IOExceptions 飞出它:

    类似

    private String readFile() throws IOException {
      String s;
      try {
        fin = new FileInputStream(programPath + fileToParse);
        buffRead = new BufferedReader(new InputStreamReader(fin));
        s = buffRead.readLine();
        fin.close();
      } finally {
        if (fin != null {
           fin.close()
        }
      }
      return s
    }
    

    然后在你需要的地方:

    try {
       loadedString = readFile();
    } catch (IOException e) {
       // handle issue gracefully
    }
    

    【讨论】:

    • 无论是否引发异常,都会执行finally 块。因此,在正常情况下,您有一个多余的fin.close()。可以删除try 块中的那个。
    • 哎呀,这个页面上几乎所有问题都有一个 apache commons 解决方案。使用 FileUtils.readFileToString(file),它更短,你可以重新使用它。 commons.apache.org/io/apidocs/org/apache/commons/io/…