The abnormal English word is exception, and the literal translation means "accident, exception", which means abnormal situations. In fact, exceptions are essentially program errors, including program logic errors and system errors.
A preface
Everyone is familiar with Java exception handling, in general, there are two points:
1. Throw exception: throw exception
class SimpleException{ public void a() throws Exception{ throw new Exception(); };}2.Catch exceptions:
public class MyException { public static void main(String[] args){ MyException e = new MyException(); SimpleException se = new SimpleException(); try { se.a(); } catch (Exception e1) { e1.printStackTrace(); } }}class SimpleException{ public void a() throws Exception{ throw new Exception(); };}This article will discuss some details on this more in-depth basis.
Two custom exception class
The Java language provides us with many exception classes, but sometimes we still have to customize the exception classes for the convenience of writing code:
class SimpleException extends Exception {};
After creation, we can use try catch to capture it:
public class MyException { public static void main(String[] args){ MyException e = new MyException(); try { ea(); } catch (SimpleException e1) { e1.printStackTrace(); } } public void a() throws SimpleException{ throw new SimpleException(); }}class SimpleException extends Exception {};We define a method a() in MyException, let it throw a SimpleException exception, then we call this method in main() and catch this exception using try catch:
SimpleException at MyException.a(MyException.java:15) at MyException.main(MyException.java:8) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)Process finished with exit code 0
The result after compilation and execution is mainly based on the first three lines. Here are some points to be explained:
1. Specify the exception type: (exception specification)
When we need to throw an exception in a method, we use throw and add an instance of an exception class. The program will throw the corresponding exception to the client program (the program that calls this code) and exit here (equivalent to return). Also note that we must specify the exception type when defining this method. For example, the following code will throw a SimpleException exception
public void a() throws SimpleException
2. Throw multiple exceptions:
public void a() throws SimpleException,AException,BException{ throw new SimpleException(); }Different exception classes can be separated by commas. In this case, we do not have to throw each exception class instance (), but the client code must catch each exception class:
public class MyException { public static void main(String[] args){ MyException e = new MyException(); try { ea(); } catch (SimpleException e1) { e1.printStackTrace(); } catch (BException e1) { e1.printStackTrace(); } catch (AException e1) { e1.printStackTrace(); } } public void a() throws SimpleException,AException,BException{ throw new SimpleException(); }}class SimpleException extends Exception {};class AException extends Exception{}class BException extends Exception{} Three stack trace
Whether it is throwing exceptions or catching and handling exceptions, our purpose is to write a more robust program, which depends to a large extent on the exception information provided by the Java exception mechanism, and its carrier is stack trace.
In the previous code, we directly use printStackTrace() to print out exception information. In fact, we can also use getStackTrace() method to get a collection of StackTraceElement. If you have IDEA on hand, you can first search for the StackTraceElement class, and you can find that it implements the interface Serializable, and then take a look at its class description:
/** * An element in a stack trace, as returned by {@link * Throwable#getStackTrace()}. Each element represents a single stack frame. * All stack frames except for the one at the top of the stack represent * a method invocation. The frame at the top of the stack represents the * execution point at which the stack trace was generated. Typically, * this is the point at which the throwable corresponding to the stack trace * was created. * * @since 1.4 * @author Josh Bloch */It is clear that each instance of this class is an element of the stack trace, representing a stack frame, and the stack trace is returned by the getStackTrace() method. I tried to translate the following several times, but I felt it was not good, so I could just write the code directly to explain it:
public class MyException { public static void main(String[] args){ MyException e = new MyException(); ea(); public void a(){ try { throw new Exception(); } catch (Exception e) { StackTraceElement[] ste = e.getStackTrace(); System.out.println(ste.length); } }}We define method a, let it throw an Exception exception while capturing it, and then we get a StackTraceElement array through the getStackTrace() method and print out the length of the array:
7
Process finished with exit code 0
We changed the code a little and stopped catching exceptions in a. We redefine a method b so that it catches exceptions while calling a:
public class MyException { public static void main(String[] args){ MyException e = new MyException(); eb(); } public void b(){ try { a(); } catch (Exception e) { StackTraceElement[] ste = e.getStackTrace(); System.out.println(ste.length); } } public void a() throws Exception{ throw new Exception(); }}The results are as follows:
8
Process finished with exit code 0
Don't worry, let's take a look at something interesting:
public class MyException { public static void main(String[] args){ MyException exception = new MyException(); try { exception.c(); } catch (Exception e) { StackTraceElement[] ste = e.getStackTrace(); System.out.println(ste.length); System.out.println("---------------------------------------------------------------"); for (StackTraceElement s : e.getStackTrace()){ System.out.println(s.getClassName()+":method "+s.getMethodName()+" at line"+s.getLineNumber()); } System.out.println("---------------------------------------------------------------"); } } public void c() throws Exception{ try { a(); }catch (Exception e){ throw e; } } public void a() throws Exception{ throw new Exception(); }}Here are the results:
8---------------------------------------------------------------MyException:method a at line43MyException:method c at line39MyException:method main at line9sun.reflect.NativeMethodAccessorImpl:method invoke0 at line-2sun.reflect.NativeMethodAccessorImpl:method invoke at line57sun.reflect.DelegatingMethodAccessorImpl:method invoke at line43java.lang.reflect.Method:method invoke at line606com.intellij.rt.execution.application.AppMain:method main at line144---------------------------------------------------------------Process finished with exit code 0
That is to say, getStackTrace() returns a stack, which contains some basic information from the caller (main()) to the initial throwing exception (a()). In the above code, we catch the exception when calling the a method in the c method and throw it again through throws. The method calling the c method can catch and handle the exception, or you can choose to continue throwing to let higher-level callers (near the bottom of the stack) handle it. Although rethrow is very convenient, there are some problems. Let's look at the following code:
public class MyException { public static void main(String[] args){ MyException exception = new MyException(); try { exception.c(); } catch (Exception e) { e.printStackTrace(System.out); } } public void c() throws Exception{ try { a(); }catch (Exception e){ throw e; } } public void a() throws Exception{ throw new Exception("Exception from a()"); }}java.lang.Exception: Exception from a() at MyException.a(MyException.java:40) at MyException.c(MyException.java:30) at MyException.main(MyException.java:21)We re-throw e in c and print it out using e.printStackTrace() in main. You can see that the printed stack trace still belongs to a. If we want to turn stack trace into c, we can write it like this:
public class MyException { public static void main(String[] args){ MyException exception = new MyException(); try { exception.c(); } catch (Exception e) { e.printStackTrace(System.out); } } public void c() throws Exception{ try { a(); }catch (Exception e){// throw e; throw (Exception)e.fillInStackTrace(); } } public void a() throws Exception{ throw new Exception("Exception from a()"); }}java.lang.Exception: Exception from a() at MyException.c(MyException.java:22) at MyException.main(MyException.java:10) Four exception chaining
Let’s take a look at a scenario:
public class TestException { public static void main(String[] args){ TestException testException = new TestException(); try { testException.c(); } catch (CException e) { e.printStackTrace(); } } public void a() throws AException{ AException aException = new AException("this is a exception"); throw aException; } public void b() throws BException{ try { a(); } catch (AException e) { throw new BException("this is b exception"); } } public void c() throws CException{ try { b(); } catch (BException e) { throw new CException("this is c exception"); } }}class AException extends Exception{ public AException(String msg){ super(msg); }}class BException extends Exception{ public BException(String msg){ super(msg); }}class CException extends Exception{ public CException(String msg){ super(msg); }}Three exception classes AException, BException, and CException are created, then AException is thrown in a(), AException is caught in b() and BException is thrown, and finally BException is caught in c() and CException is thrown, and the result is printed as follows:
CException: this is c exception at TestException.c(TestException.java:31) at TestException.main(TestException.java:8)
OK, we only see the information of CException, AException and BException have been lost, and the function of the exception chain will come out, see the code:
public class TestException { public static void main(String[] args){ TestException testException = new TestException(); try { testException.c(); } catch (CException e) { e.printStackTrace(); } } public void a() throws AException{ AException aException = new AException("this is a exception"); throw aException; } public void b() throws BException{ try { a(); } catch (AException e) {// throw new BException("this is b exception"); BException bException = new BException("this is b exception"); bException.initCause(e); throw bException; } } public void c() throws CException{ try { b(); } catch (BException e) {// throw new CException("this is c exception"); CException cException = new CException("this is c exception"); cException.initCause(e); throw cException; } }}class AException extends Exception{ public AException(String msg){ super(msg); }}class BException extends Exception{ public BException(String msg){ super(msg); }}class CException extends Exception{ public CException(String msg){ super(msg); }}We use the initCause() method to connect the exception information, and the result is as follows:
CException: this is c exception at TestException.c(TestException.java:35) at TestException.main(TestException.java:8) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)Caused by: BException: this is b exception at TestException.b(TestException.java:24) at TestException.c(TestException.java:32) ... 6 moreCaused by: AException: this is a exception at TestException.a(TestException.java:15) at TestException.b(TestException.java:21) ... 7 moreProcess finished with exit code 0
The Five Postscript
In fact, there are still many things to discuss about Java exception handling, but because I have limited experience, I cannot understand it too deeply, and the most commonly used one is
try { ... }catch (Exception e){ ... } finally { //Code that will be executed regardless of whether the exception will be caught or processed, such as closing IO operations}But no matter what, we still have to thank Java for providing us with the exception mechanism. It is like an elder, guiding us from time to time, and making us less bored when encoding :)