0

I know how to check for existence of file via if/else, and it worked in my previous assignment:

File f = new File("file.txt");
    if (f.exists() && f.canRead()) {
        // Code goes here.
    } else { } // Error message.

However, if I do the same with buffered image, like so:

private BufferedImage img;

File f = new File("pic.png");
    if (f.exists() && f.canRead()) {
        img = ImageIO.read(f);
    } else { } // Error message.

Then compilers demands try/catch or @throws which defeats the purpose. What am I doing wrong? Would it be better to package all my sprites in a .jar and call them as resources (which I am planning on doing once finished)?

If it at all matters, I am running on JRE 1.6, and the file check is inside switch case statement.

theUg
  • 1,284
  • 2
  • 9
  • 14
  • I use try/catch as it is right now and it works, but my professor insists that I must try to avoid exceptions altogether if I can help that. – theUg Nov 27 '12 at 06:38
  • 1
    @theUG.. I think, by saying `try to avoid exception` - your professor meant, handle them. – Rohit Jain Nov 27 '12 at 06:41
  • 1
    You will need to clarify with your professor what he means by avoiding exceptions. Most IO methods will throw an IOException – BevynQ Nov 27 '12 at 06:42
  • @RohitJain We discussed it with him in some detail, and he was saying something along the lines that if I know exactly what is going to happen I should try to handle it without resorting to try/catch fall-back. – theUg Nov 27 '12 at 06:46
  • @RohitJain Where did your answer go? I thought it was the best so far, and I wanted to clarify something with more questions. – theUg Nov 27 '12 at 06:48
  • You've done a good job by testing for the things that you can test for in advance. However, you can't avoid catching or throwing an _IOException_ when you call _ImageIO.read()_. – jahroy Nov 27 '12 at 06:50
  • @theUg.. Ah! Sorry. I thought you knew that thing. So, I deleted it. But I undeleted it now. See it. :) – Rohit Jain Nov 27 '12 at 07:38

4 Answers4

2

As its a checked exception you have to catch it. It doesn't matter if its actually thrown or not. Your manual error handling won't be able to handle all cases. Consider you are reading from a network drive and the connection could be interrupted while reading.

Update Looking at your comments above. As told you have to build a try/catch block as the exception could be thrown and handling of the checked exceptions it required per specifaction. With your prior checking you are already avoiding the risk of a FileNotFoundException. Sometimes you even have I classed where the implementation doesn't throw any error at all, but as its in the interface you still have to handle it e.g. ByteArryOutputStream.close().

Udo Held
  • 12,314
  • 11
  • 67
  • 93
  • So, I figured the difference is that mandated @throws declaration (it just sunk in what I was reading before about exceptions propagating up the stack until handled or program crash. So, in my case, while I would not worry about remote or removable drives, realistic problem that would handle is, say, corrupted file that prevents it from reading, for instance? – theUg Nov 27 '12 at 07:08
2

The use of a try/catch block doesn't defeat the purpose at all.

Even though the file exists and is readable, there are plenty of other Exceptions that can be thrown.

Possible examples:

  • the file is deleted while it's being read
  • permission to the file is revoked while it's being read
  • the disk drive is unplugged/unmounted while reading

The method ImageIO.read() throws an IOException.

There is no way to avoid either catching it or throwing it.

jahroy
  • 22,322
  • 9
  • 59
  • 108
  • @UdoHeld - I know... I guess I was having a hard time thinking of more examples. Feel free to add some by editing. – jahroy Nov 27 '12 at 06:52
2

We discussed it with him in some detail, and he was saying something along the lines that if I know exactly what is going to happen I should try to handle it without resorting to try/catch fall-back.

If your instructor is telling you that you should test to avoid IO exceptions, then I'm afraid that he is simply wrong.

  1. Most I/O operations are declared as throwing IOException which is a checked exception. When you call those methods, your code has to either handle or propagate the exception. You have no choice. The Java language mandates this for all checked exceptions.

  2. It is impractical to test for all possible IOExceptions. For instance, this code can fail:

      if (f.exists() && f.canRead()) {
          os = new FileInputStream(f);
      }
    

    Why? Because there are other reasons that the constructor might throw an IOException, such as a hard disc error, a network error (if the file is on a remote mounted file system), a mandatory access control failure, and so on. The underlying conditions often cannot be detected in any other way than attempting to open the file.

  3. Then there is the problem of race conditions. In the above example, there is a small time window between testing that the file is readable, and trying to open it. In that time window, it is possible that some other thread, or some external process will delete the file or change its access, so that the open will fail. There is no way to close that time window.

Then there is the question of why you would even want to avoid the exception. I suspect that your teacher is an "exception denier"; i.e. one of those people who takes the "exceptions should only be used for exceptional things" advice to an illogical extreme.

The "exceptions should only be used for exceptional things" advice is basically saying that you shouldn't overuse exceptions because they are kind of expensive. But expensive is relative.

In this example, the calls to File.exists() and to File.canRead() are also expensive because they each entail a system call, and that is going to take thousands of clock cycles. And worse still, unless there is something unusual about your application those two tests are going to succeed ... so you have just done two unnecessary system calls to avoid an exception that wasn't going to be thrown anyway. Unless there is a high probability (i.e. > 50%) that those tests are going to fail, it is more efficient to skip the test, attempt the open and handle the exception if it occurs.


I would not label my prof as such. We did not talk about intricacies of I/O specifically, it was more in the general terms in vein such that if I can avoid it, I should avoid it. I did not know (and he did not go into that detail) that there are exceptions which are very much unavoidable.

OK. So your Prof didn't say that. Good for him.

And yes, they are unavoidable in more than one sense.

As for rejecting the use of .exists() check, is the performance hit that significant?

Yes. See this - Syscall overhead

Or if you don't believe that, write a micro-benchmark and measure it. And compare it to the overhead of throwing and catching an exception.

Would it be issue of, say, desktop v. mobile and the like?

Nope. Or certainly not for a mobile where the operating system uses virtual memory to stop one "app" from interfering with another.

Is the trade off against clarity for troubleshooting/debugging (if I can pinpoint the problem better while avoiding crashing the programm) worth it?

Well, I would argue that this code:

try (InputStream is = new FileInputStream(f)) {
    // use file
} catch (IOException ex) {
    System.err.println("IO error: ": ex.getMessage());
}

... is simpler and easier to maintain and debug than this:

if (!f.exists()) {
    System.err.println("File " + f + " does not exist");
} else if (!f.isDirectory()) {
    System.err.println("File " + f + " is a directory");
} else if (!f.canRead()) {
    System.err.println("File " + f + " is not readable");
} ...
} else {
    try (InputStream is = new FileInputStream(f)) {
        // read
    } catch (IOException ex) {
        System.err.println("IO error: "+ ex.getMessage());
    }
}

Wouldn't you agree?

Community
  • 1
  • 1
Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • I would not label my prof as such. We did not talk about intricacies of I/O specifically, it was more in the general terms in vein such that if I can avoid it, I should avoid it. I did not know (and he did not go into that detail) that there are exceptions which are very much unavoidable. – theUg Nov 27 '12 at 08:15
  • As for rejecting the use of `.exists()` check, is the performance hit that significant? Would it be issue of, say, desktop v. mobile and the like? Is the trade off against clarity for troubleshooting/debugging (if I can pinpoint the problem better while avoiding crashing the programm) worth it? – theUg Nov 27 '12 at 08:21
1

If you see the declaration of ImageIO.read method: -

public static BufferedImage read(File input)
                          throws IOException

It declares an IOException to be thrown, which is a CheckedException. So, when invoking that method, you either need to handle it in a try-catch block. Or, declare it to be thrown in the method you are using it: -

File f = new File("pic.png");
if (f.exists() && f.canRead()) {
    try {
        img = ImageIO.read(f);
    } catch (IOException e) {
        e.printStackTrace();
    }
} else { } // Error message.

However, if you are not handling it as in the above case, then you have to declare that method in the throws clause of the method, so as to propagate it to the caller, so that it handles it.

Generally, any checked exception should either be handled, or propagated to the caller, which gradually reaches the main method, in which case, if main method does not handle it, it is handled by the JVM, thus resulting in the termination of your program.

Generally, it's a good idea to handle the exception in the method itself where it was thrown, and return the appropriate message to the caller if required.

Rohit Jain
  • 209,639
  • 45
  • 409
  • 525
  • Yes, I did not realize that `read` method demands that the exception must be handled. Before I had to deal mostly with avoiding FileNotFound and NullPointer exceptions, but I was not familiar with some scenarios that could cause `IOException` as exampled in some of the answers here. – theUg Nov 27 '12 at 08:07
  • 1
    @theUg.. You can always go through the documentation to check what all exceptions a particular method throw. Be friend with docs. They should be the first you go, in case you face any problems with the `API`. – Rohit Jain Nov 27 '12 at 08:11
  • @theUg.. You can also read more about Checked and Unchecked exception on internet. As in Checked Exception, Unchecked Exception like `StackOverflowError`, need not be handled, or declared to be thrown. Because you don't know how to handle those exceptions. Actually, you shouldn't handle them unless you know exactly why they occurred. – Rohit Jain Nov 27 '12 at 08:12
  • I try to read the docs, is just that in this case I would not even know to pay attention to the `@throws` declaration. but now I know. :) – theUg Nov 27 '12 at 08:24
  • @theUg.. Well, then it's fine. And now that you understood the issue. You can mark one of the answers here as accepted, by clicking the arrow besides them. – Rohit Jain Nov 27 '12 at 08:26