I have a very simple question about re-throwing exception in Java.
Here is the code snippet:
public static void main(String[] args) throws FileNotFoundException {
try {
FileReader reader = new FileReader("java.pdf");
} catch (FileNotFoundException ex) {
throw ex;
}
}
public static void main(String[] args) throws FileNotFoundException {
FileReader reader = new FileReader("java.pdf");
}
Why do we need to re-throw ex
in the first version while the second version looks more elegant? What might be the benefits and which version is preferred over the other?
You are right. Second version is better. Moreover the first version does not make any sense. It does the same except the stack trace of the exception will be "wrong".
There are the following reasons to "re-throw" exceptions:
- If you have something to do before.
- If you catch exception of one type and throw exception of other type:
example:
try {
// do something
} catch (IOException ioe) {
throw new IllegalStateException(ioe);
}
In the example given, re-throwing the Exception
serves no purpose.
Doing this can be useful if the method that catches and then re-throws the exception needs to take some additional action upon seeing the Exception
, and also desires that the Exception
is propagated to the caller, so that the caller can see the Exception
and also take some action.
I would only catch/rethrow an exception (instead of just throwing it) if I wanted to do something else in the catch block - for example, write a logging statement before rethrowing.
In addition to wanting to do something with the exception before exiting - like logging, the other time you would do something like that is if you want to wrap it as a different exception, like:
try {
FileReader reader = new FileReader("java.pdf");
} catch (FileNotFoundException ex) {
throw new ServletException(ex);
}
The question is why you think you need to rethrow the exception. Did Eclipse suggest surrounding with try-catch
? In practice we rarely rethrow the same exception, but very often catch one and throw another that wraps the first one, especially if the wrapper exception is unchecked. This happens whenever you have calls declaring checked exceptions, but the method you write those calls in doesn't declare those exceptions:
public int findId(String name) {
try {
return db.select("select id from person where name=?", name);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
The flow of execution stops immediately after the throw
statement; any subsequent statements are not executed. The nearest enclosing try
block is inspected to see if it has a catch
statement that matches the type of exception.
If it does find a match, control is transferred to that statement. If not, then the next enclosing try
statement is inspected and so on. If no matching catch
is found, then the default exception handler halts the program and prints the stack trace.
If the method is capable of causing an exception that it does not handle, it must specify this behavior so that callers of the method can guard themselves against that exception.
One can do this by including a throws
clause in method's declaration. A throws
clause lists the types of exceptions that a method might throw. This is necessary for all exceptions, except those of type Error
or RuntimeException
, or any of their subclasses. All other exceptions that a method can throw must be declared int the throws
clause. If they are not, a compile-time error will result.
Both versions will output the same stacktrace
without try/catch
import java.io.FileNotFoundException;
import java.io.FileReader;
public class Test {
public static void main(String[] args) throws FileNotFoundException {
// try {
FileReader reader = new FileReader("java.pdf");
// } catch (FileNotFoundException ex) {
// throw ex;
// }
}
}
will output
Exception in thread "main" java.io.FileNotFoundException: java.pdf (The system cannot find the file specified)
at java.io.FileInputStream.open0(Native Method)
at java.io.FileInputStream.open(FileInputStream.java:195)
at java.io.FileInputStream.<init>(FileInputStream.java:138)
at java.io.FileInputStream.<init>(FileInputStream.java:93)
at java.io.FileReader.<init>(FileReader.java:58)
at Test.main(Test.java:7)
with try/catch/throw same exception
import java.io.FileNotFoundException;
import java.io.FileReader;
public class Test {
public static void main(String[] args) throws FileNotFoundException {
try {
FileReader reader = new FileReader("java.pdf");
} catch (FileNotFoundException ex) {
throw ex;
}
}
}
will output exactly the same as before
Exception in thread "main" java.io.FileNotFoundException: java.pdf (The system cannot find the file specified)
at java.io.FileInputStream.open0(Native Method)
at java.io.FileInputStream.open(FileInputStream.java:195)
at java.io.FileInputStream.<init>(FileInputStream.java:138)
at java.io.FileInputStream.<init>(FileInputStream.java:93)
at java.io.FileReader.<init>(FileReader.java:58)
at Test.main(Test.java:7)
try/catch/throw wrapping exception
An advisable approach is to throw your own exceptions.
wrap the exception you just caught into it if you want to provide details of the root cause (might be wanted or not wanted)
import java.io.FileNotFoundException;
import java.io.FileReader;
public class Test {
public static void main(String[] args) {
try {
FileReader reader = new FileReader("java.pdf");
} catch (FileNotFoundException ex) {
throw new RuntimeException("Error while doing my process", ex);
}
}
}
You can clearly see the top-level issue (my process did not complete), and the root cause that led to it (java.pdf file not found)
Exception in thread "main" java.lang.RuntimeException: Error while doing my process
at Test.main(Test.java:9)
Caused by: java.io.FileNotFoundException: java.pdf (The system cannot find the file specified)
at java.io.FileInputStream.open0(Native Method)
at java.io.FileInputStream.open(FileInputStream.java:195)
at java.io.FileInputStream.<init>(FileInputStream.java:138)
at java.io.FileInputStream.<init>(FileInputStream.java:93)
at java.io.FileReader.<init>(FileReader.java:58)
at Test.main(Test.java:7)