views:

106

answers:

4

Wrote up a basic file handler for a Java Homework assignment, and when I got the assignment back I had some notes about failing to catch a few instances:

  • Buffer from file could have been null.
  • File was not found
  • File stream wasn't closed

Here is the block of code that is used for opening a file:

/**
 * 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);
}

The one comment I had from him was that fin.close(); needed to be in a finally block, which I did not have at all. But I thought that the way I have created the try/catch it would have prevented an issue with the file not opening.

Let me be clear on a few things: This is not for a current assignment (not trying to get someone to do my own work), I have already created my project and have been graded on it. I did not fully understand my Professor's reasoning myself. Finally, I do not have a lot of Java experience, so I was a little confused why my catch wasn't good enough.

+1  A: 

Say buffRead.readLine() throws an exception, will your FileInputStream ever be closed, or will that line be skipped? The purpose of a finally block is that even in exceptional circumastances, the code in the finally block will execute.

spender
+3  A: 

But what if your program threw an exception on the second or third line of your try block?

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

By this point, a filehandle has been opened and assigned to fin. You could trap the exception but the filehandle would remain open.

You'll want to move the fin.close() statement to a finally block:

} finally {
    try {
        if (fin != null) {
            fin.close();
        }
    } catch (IOException e2) {
    }
}
mobrule
I think you should add a `if (fin != null)` before you try to close it (in case that the file didn't exist).
Chris
Thanks @Chris, fixed.
mobrule
or, again using apache commons (io in this case), write just one line: 'IOUtils.closeQuietly(fin);', this does exactly what you are doing in a lot less (local) code http://commons.apache.org/io/apidocs/org/apache/commons/io/IOUtils.html#closeQuietly%28java.io.InputStream%29
seanizer
+6  A: 
  • Buffer from file could have been null.

The file may be empty. That is, end-of-file is reach upon opening the file. loadedString = buffRead.readLine() would then have returned null.

Perhaps you should have fixed this by adding something like if (loadedString == null) loadedString = "";

  • File was not found

As explained in the documentation of the constructor of FileInputStream(String) it may throw a FileNotFoundException. You do catch this in your IOException clause (since FileNotFoundException is an IOException), so it's fine, but you could perhaps have done:

} catch (FileNotFoundException fnfe) {
    System.err.println("File not fonud!");
} catch (IOException ioex {
    System.err.println("Some other error");
}
  • File stream wasn't closed

You do call fin.close() which in normal circumstances closes the file stream. Perhaps he means that it's not always closed. The readLine could potentially throw an IOException in which case the close() is skipped. That's the reason for having it in a finally clause (which makes sure it gets called no matter what happens in the try-block. (*)


(*) As @mmyers correctly points out, putting the close() in a finally block will actually not be sufficient since you call System.exit(-1) in the catch-block. If that really is the desired behavior, you could set an error flag in the catch-clause, and exit after the finally-clause if this flag is set.

aioobe
The finally clause won't get executed if there's an exception, because of the System.exit() in the catch block.
Michael Myers
Good point! I didn't think of that! I'll update the answer.
aioobe
Technically though, the finalize-method of the FileInputStream *will* close the stream upon termination :-)
aioobe
instead of this: 'if (loadedString == null) loadedString = "";' I would usually resort to StringUtils.stripToEmpty() using apache commons / lang http://commons.apache.org/lang/api/org/apache/commons/lang/StringUtils.html#stripToEmpty%28java.lang.String%29
seanizer
@seanizer: It's a school assignment.
Skip Head
Thanks! That made a lot of sense the way you explained it :)
Urda
A: 

There are a lot of other errors which may happen other than opening the file.

In the end you may end up with a fin which is defined or not which you have to protect against null pointer errors, and do not forget that closing the file can throw a new exception.

My advice is to capture this in a separate routine and let the IOExceptions fly out of it :

something like

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
}

and then where you need it :

try {
   loadedString = readFile();
} catch (IOException e) {
   // handle issue gracefully
}
Peter Tillemans
The `finally` block is executed regardless of whether an exception was raised. Therefore, you have a redundant `fin.close()` under normal circumstances. The one in the `try` block can be removed.
Mark Peters
gee, there is an apache commons solution to almost every problem on this page. Use FileUtils.readFileToString(file), it's much shorter and you can resue it. http://commons.apache.org/io/apidocs/org/apache/commons/io/FileUtils.html#readFileToString(java.io.File)
seanizer