1
try {
        throw new FileNotFoundException();
    } catch (IOException e) {
        e.printStackTrace();
    }
    catch (Exception e) {
        e.printStackTrace();
    }

Can someone tell me why the second catch block is not considered as unreachable code by the compiler? But in the following case:

try {
        throw new FileNotFoundException();
    } catch (Exception e) {
        e.printStackTrace();
    }
    catch (IOException e) {
        e.printStackTrace();
    }

The second catch block is considered unreachable?

After all, FileNotFoundException comes under IOException, just as it comes under Exception.

Edit Please clarify: The compiler will know an exception is thrown by a method based on the method's throws clause. But it may not necessarily know the specific type of exception (under that class of exception). So if a method throws exception 'A', compiler won't know if the actual exception is 'A' or a subtype of 'A' because this is only determined at runtime. The compiler however will know that an exception of type 'X' is never thrown, so giving a catch block for X is erroneous. Is this right?

user1825770
  • 359
  • 1
  • 6
  • 17
  • You might want to clarify if your question is about the 1st or the 2nd case. I think your question is about the `catch (Exception e)` in your first case, and why the compiler doesn't consider it unreachable. But it looks like most people here think you are asking about your 2nd example. Which is it? – sstan Dec 10 '15 at 00:53
  • I think it is sufficiently clear to everyone who has answered. The question is about both cases i.e. why there is no error in the first case but an error in the second case. – user1825770 Dec 10 '15 at 01:34

3 Answers3

4

The compiler cannot assume that the only possible exception thrown from your try block will be a FileNotFoundException. That's why it doesn't consider the 2nd catch block to be unreachable in your first code sample.

What if, for some unknown reason, a RuntimeException gets thrown while creating the FileNotFoundException instance (entirely possible)? What then?

In your first code sample, that unexpected runtime exception would get caught by the 2nd catch block, while the 1st block would take care of the FileNotFoundException if it gets thrown.

However, in your 2nd code sample, any and all exceptions would get caught by the 1st catch block, making the 2nd block unreachable.

EDIT:

To better understand why the catch(Exception e) block in your first code is not considered unreachable by the compiler, try the following code, and notice how the the 2nd catch is definitely reachable:

public class CustomIOException extends IOException {
    public CustomIOException(boolean fail) {
        if (fail) {
            throw new RuntimeException("the compiler will never know about me");
        }
    }
}

public static void main(String[] args) {
    try {
        throw new CustomIOException(true);
    } catch(IOException e) {
        System.out.println("Caught some IO exception: " + e.getMessage());
    } catch(Exception e) {
        System.out.println("Caught other exception: " + e.getMessage());
    }
}

Output:

Caught other exception: the compiler will never know about me

sstan
  • 35,425
  • 6
  • 48
  • 66
2

TL;DR

The compiler considers that FileNotFoundException() may not be the only Exception thrown.


Explaination

JLS§11.2.3 Exception Checking

A Java compiler is encouraged to issue a warning if a catch clause can catch (§11.2) checked exception class E1 and the try block corresponding to the catch clause can throw checked exception class E2, a subclass of E1, and a preceding catch clause of the immediately enclosing try statement can catch checked exception class E3 where E2 <: E3 <: E1.

That means that if the compiler considers that the only exception possibly thrown by your catch block is a FileNotFoundException(), it will warn you about your second catch block. Which is not the case here.

However, the following code

    try{
        throw new FileNotFoundException();
    } catch (FileNotFoundException e){
        e.printStackTrace();
    } catch (IOException e){ // The compiler warns that all the Exceptions possibly 
                             // catched by IOException are already catched even though
                             // an IOException is not necessarily a FNFException
        e.printStackTrace();
    } catch (Exception e){
        e.printStackTrace();
    }

This happens because the compiler evaluates the try block to determine which exceptions has the possibility to be thrown.

As the compiler does not warn us on Èxception e, it considers that other exceptions may be thrown (e.g RunTimeException). Since it is not the compiler's work to handle those RunTimeExceptions, it lets it slip.


Rest of the answer is intersting to read to understand the mechanism behind exception-catching.


Schema

As you may see, Exception is high in the hierarchy so it has to be declared last after IOException that is lower in the hierarchy.

enter image description here


Example

Imagine having an IOException thrown. As it is inherited from Exception, we can say IOException IS-A Exception and so, it will always be catched within the Exception block and the IOException block will be unreachable.


Real Life Example

Let's say, you're at a store and have to choose pants. The seller tells you that you have to try the pants from the largest ones to the smallest ones and if you find one that you can wear (even if it is not your size) you must take it.

You'll find yourself buying pants too large for your size and you'll not have the chance to find the pants that fits you.

You go to another store : there, you have the exact opposite happening. You can choose your pants from smallest to largest and if you find one you can wear, you must take it.

You'll find yourself buying pants at your exact size.

That's a little analogy, a bit odd but it speaks for itself.


Since Java 7 : multi-catch

Since Java 7, you have the option to include all the types of Exceptions possibly thrown by your try block inside one and only catch block.

WARNING : You also have to respect the hierarchy, but this time, from left to right.

In your case, it would be

try{
    //doStuff
}catch(IOException | Exception e){
    e.printStackTrace();
}

The following example, which is valid in Java SE 7 and later, eliminates the duplicated code:

catch (IOException|SQLException ex) {
    logger.log(ex);
    throw ex;
}

The catch clause specifies the types of exceptions that the block can handle, and each exception type is separated with a vertical bar (|).

Yassin Hajaj
  • 21,337
  • 9
  • 51
  • 89
0

First case:

catch (IOException e) { // A specific Exception
    e.printStackTrace();
}
catch (Exception e) { // If there's any other exception, move here
    e.printStackTrace();
}

As you can see, first IOException is catched. This means we are aiming at just one specific exception. Then in second catch, we aim for any other Exceptions other than IOException. Hence its logical.

In second:

catch (Exception e) { // Move here no matter whatever exception
    e.printStackTrace();
}
catch (IOException e) { // The block above already handles *Every exception, hence this won't be reached.
    e.printStackTrace();
}

We catched any exception (whether its IOException or some other Exception), right in the first block. Hence second block will not be reached because everything is already included in first block.

In other words, in first case, we aim at some specific exception, than at any other exceptions. While in second case, we first aim at all/any Exception, than at a specific exception. And since we already dealt will all exceptions, having a specific exception later won't make any logical sense.

Jaskaranbir Singh
  • 2,034
  • 3
  • 17
  • 33