Appearance
Throw Exception
abnormal spread
When a method throws an exception, if the current method does not catch the exception, the exception will be thrown to the upper calling method until it encounters a try ... catch
is caught:
java
// exception
public class Main {
public static void main(String[] args) {
try {
process1();
} catch (Exception e) {
e.printStackTrace();
}
}
static void process1() {
process2();
}
static void process2() {
Integer.parseInt(null); // Will throw NumberFormatException
}
}
The call stack of the method can be printed out through printStackTrace()
, similar to:
sh
java.lang.NumberFormatException: null
at java.base/java.lang.Integer.parseInt(Integer.java:614)
at java.base/java.lang.Integer.parseInt(Integer.java:770)
at Main.process2(Main.java:16)
at Main.process1(Main.java:12)
at Main.main(Main.java:5)
printStackTrace()
is very useful for debugging errors. The above information indicates NumberFormatException
is thrown in the java.lang.Integer.parseInt
method. Looking from bottom to top, the calling hierarchy is:
main()
callsprocess1()
;process1()
callsprocess2()
;process2()
callsInteger.parseInt(String)
;Integer.parseInt(String)
callsInteger.parseInt(String, int)
.
Looking at the Integer.java
source code, we can see that the method code for throwing an exception is as follows:
java
public static int parseInt(String s, int radix) throws NumberFormatException {
if (s == null) {
throw new NumberFormatException("null");
}
...
}
Moreover, the line number of the source code is given for each layer of call, which can be directly located.
throw an exception
When an error occurs, for example, the user enters an illegal character, we can throw an exception.
How to throw an exception? Referring to Integer.parseInt()
method, there are two steps to throwing an exception:
- Create an instance of Exception ;
- Throw using throw statement.
Here's an example:
java
void process2(String s) {
if (s==null) {
NullPointerException e = new NullPointerException();
throw e;
}
}
In fact, most of the code that throws exceptions will be combined into one line:
java
void process2(String s) {
if (s==null) {
throw new NullPointerException();
}
}
If a method catches an exception and then throws a new exception in the catch
clause, it is equivalent to "converting" the thrown exception type:
java
void process1(String s) {
try {
process2();
} catch (NullPointerException e) {
throw new IllegalArgumentException();
}
}
void process2(String s) {
if (s==null) {
throw new NullPointerException();
}
}
When process2()
throws NullPointerException
, it is caught by process1()
, and then throws IllegalArgumentException()
.
If IllegalArgumentException
is caught in main()
, let's look at the printed exception stack:
java
public class Main {
public static void main(String[] args) {
try {
process1();
} catch (Exception e) {
e.printStackTrace();
}
}
static void process1() {
try {
process2();
} catch (NullPointerException e) {
throw new IllegalArgumentException();
}
}
static void process2() {
throw new NullPointerException();
}
}
The printed exception stack is similar to:
sh
java.lang.IllegalArgumentException
at Main.process1(Main.java:15)
at Main.main(Main.java:5)
This shows that the new exception has lost the original exception information, and we can no longer see the information of the original exception NullPointerException
.
In order to track the complete exception stack, when constructing the exception, pass the original Exception
instance in, and the new Exception
can hold the original Exception
information. Improvements to the above code are as follows:
java
public class Main {
public static void main(String[] args) {
try {
process1();
} catch (Exception e) {
e.printStackTrace();
}
}
static void process1() {
try {
process2();
} catch (NullPointerException e) {
throw new IllegalArgumentException(e);
}
}
static void process2() {
throw new NullPointerException();
}
}
Running the above code, the printed exception stack is similar to:
sh
java.lang.IllegalArgumentException: java.lang.NullPointerException
at Main.process1(Main.java:15)
at Main.main(Main.java:5)
Caused by: java.lang.NullPointerException
at Main.process2(Main.java:20)
at Main.process1(Main.java:13)
NullPointerException
IllegalArgumentException
Caused Main.process2()
Caused by: Xxx
To get the original exception in code, use the Throwable.getCause()
method. If null
is returned, it means that it is already a "root exception".
With complete exception stack information, we can quickly locate and fix code problems.
Best practices
When an exception is caught and thrown again, the original exception must be retained, otherwise it will be difficult to locate the first crime scene!
If we throw an exception in the try
or catch
statement block, will the finally
statement be executed? For example:
java
public class Main {
public static void main(String[] args) {
try {
Integer.parseInt("abc");
} catch (Exception e) {
System.out.println("catched");
throw new RuntimeException(e);
} finally {
System.out.println("finally");
}
}
}
The results of executing the above code are as follows:
sh
catched
finally
Exception in thread "main" java.lang.RuntimeException: java.lang.NumberFormatException: For input string: "abc"
at Main.main(Main.java:8)
Caused by: java.lang.NumberFormatException: For input string: "abc"
at ...
The first line prints catched
, indicating that the catch
statement block has been entered. The second line prints finally
, indicating that the finally
statement block is executed.
Therefore, throwing an exception in catch
will not affect the execution of finally
. The JVM will execute finally
first and then throw an exception.
Exception shielding
If an exception is thrown when executing the finally
statement, can the exception in the catch
statement continue to be thrown? For example:
java
public class Main {
public static void main(String[] args) {
try {
Integer.parseInt("abc");
} catch (Exception e) {
System.out.println("catched");
throw new RuntimeException(e);
} finally {
System.out.println("finally");
throw new IllegalArgumentException();
}
}
}
Executing the above code, the exception information is found as follows:
sh
catched
finally
Exception in thread "main" java.lang.IllegalArgumentException
at Main.main(Main.java:11)
This shows that after finally
throwing an exception, the exception originally prepared to be thrown in catch
"disappears" because only one exception can be thrown. An exception that is not thrown is called a "Suppressed Exception".
In rare cases, we need to know all exceptions. How to save all exception information? The method is to first use origin
variable to save the original exception, then call Throwable.addSuppressed()
to add the original exception, and finally
throw it:
java
public class Main {
public static void main(String[] args) throws Exception {
Exception origin = null;
try {
System.out.println(Integer.parseInt("abc"));
} catch (Exception e) {
origin = e;
throw e;
} finally {
Exception e = new IllegalArgumentException();
if (origin != null) {
e.addSuppressed(origin);
}
throw e;
}
}
}
When both catch
and finally
throw exceptions, although catch
exception is blocked, the exception thrown by finally
still contains it:
sh
Exception in thread "main" java.lang.IllegalArgumentException
at Main.main(Main.java:11)
Suppressed: java.lang.NumberFormatException: For input string: "abc"
at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.base/java.lang.Integer.parseInt(Integer.java:652)
at java.base/java.lang.Integer.parseInt(Integer.java:770)
at Main.main(Main.java:6)
All Suppressed Exception
can be obtained through Throwable.getSuppressed()
.
In most cases, do not throw exceptions in finally
. Therefore, we usually don't need to care about Suppressed Exception
.
Post an exception when asking a question
The detailed stack information printed by the exception is the key to finding the problem. Many beginners only post the code and not the exception when asking questions, which is equivalent to only reporting the crime without giving clues, and there is nothing Holmes can do.
Some children's shoes only post part of the abnormal information, and the most critical Caused by: xxx
is omitted. This is an incorrect way of asking questions and must be changed.
Summary
Calling printStackTrace()
can print the exception propagation stack, which is very useful for debugging;
When catching an exception and throwing a new exception again, the original exception information should be kept;
Generally don't throw exceptions in finally
. If an exception is thrown in finally
, the original exception should be added to the original exception. The caller can obtain all added Suppressed Exception
through Throwable.getSuppressed()
.