Hi, everyone, I’m developer FTD. Today we’ll talk about exception handling in the Java language.
It’s been 26 years since the Java language was born in 1995. As a relatively old language still has a strong vitality, Java in many aspects (such as high concurrency, portability, etc.) has obvious advantages, of course, in some aspects (such as image processing) also has deficiencies, today I want to introduce the exception is the Java language provides a powerful, Mechanisms that allow us to properly respond to errors in our programs.
First, abnormal introduction
What is an exception?
Exceptions refer to the abnormal events that occur in the process of program operation due to external problems. The occurrence of exceptions often interrupts the operation of programs. In Java, an object-oriented programming language, everything is an object, and an exception itself is an object. When an exception occurs in a program, an exception object will be generated.
Classification of anomalies
When we talk about the classification of exceptions, we can’t stop talking about the inheritance structure of Java exceptions. As shown in the figure below:
As can be seen from the figure, exceptions mainly consist of the following classes:
- Throwable
- Error
- Exception
Next, we will introduce the role of these base classes.
Throwable
The Throwable class is the top-level parent of all errors or exceptions in the Java language. All other exception classes inherit from this class. The Throwable class has two important subclasses: Exception and Error. Both are important subclasses of Java Exception handling, and each contains a large number of subclasses.
Only if the object is an instance of this class or a subclass of it can it be thrown using the Java Virtual Machine or a Java Throw statement. Similarly, only this class or a subclass of it can be a parameter type in a catch clause.
The Throwable object contains a snapshot of the execution stack of the thread when its thread was created. It also contains a message string that gives more information about the error.
And finally, it can include the cause: another throwable that caused the throwable to be thrown. This cause facility first appeared in version 1.4. It is also known as an anomaly chain facility, because causes themselves have causes, and so on, forming a chain of anomalies, each caused by another anomaly.
Error
Error is a subclass of Throwable, a serious problem that an application should not normally try to catch.
An Error is an Error that a program cannot handle and represents a serious problem in running an application. Most of the errors have nothing to do with what the code writer is doing, but represent problems with the JVM (Java Virtual Machine) while the code is running.
An example is a Java Virtual MachineError, an OutOfMemoryError that occurs when the JVM no longer has the memory resources needed to continue an operation. When these exceptions occur, the Java Virtual Machine (JVM) typically chooses to terminate the thread.
These errors indicate that a fault occurs within the Virtual machine itself, or when the Virtual machine attempts to execute an application, such as a Java Virtual MachineError, a NoClassDefFounderError, and so on. These errors are undetectable because they are outside the control and processing power of the application, and most of them are conditions that are not allowed to occur while the program is running. For a properly designed application, even if an error does occur, there should be no attempt by nature to handle the exception conditions it causes. In Java, errors are described as subclasses of Error.
Exception
Exception, along with its subclasses, represents various unexpected events sent by a program while it is running. Can be used by the Java exception handling mechanism and is the core of exception handling.
Exceptions fall into two main categories:
1. Unchecked Exceptions
Error and RuntimeException and their subclasses. The Java language does not prompt and find such exceptions at compile time, and it does not require that these exceptions be handled in the program. So we can write code in the program to handle (using try… The catch… Finally), or do nothing about it. Instead of going through the exception handler, we should fix the code for these errors or exceptions. Most likely, such exceptions occur because of a problem with our code logic.
Such as:
- ArithMeticException is thrown when the program divides a number by 0;
- During casting, an incorrect cast throws a ClassCastException cast exception.
- When using a collection when the array index cross-border will be thrown ArrayIndexOutOfBoundsException exception;
- NullPointerException (NullPointerException) is thrown when an empty object is used.
Common non-checkable abnormalities are:
abnormal | describe |
---|---|
ArithmeticException | An exception is thrown when an exception condition occurs. For example, when an integer is “divided by zero,” an instance of this class is thrown. |
ArrayIndexOutOfBoundsException | Exception thrown when accessing an array with an invalid index. If the index is negative or greater than the array size, the index is illegal. |
ArrayStoreException | The exception that is thrown when an object of the wrong type is attempted to store into an array of objects. |
ClassCastException | The exception that is thrown when an attempt is made to cast an object to an instance that is not of the same type or a subclass thereof. |
IllegalArgumentException | This exception is thrown when an invalid or incorrect parameter is passed to a method. |
IllegalMonitorStateException | Thrown when a thread has tried to wait for an object’s monitor, or notifies other threads that are waiting for that object’s monitor, and the thread itself has not obtained the specified monitor. |
IllegalStateException | A signal generated when a method is called at an illegal or inappropriate time. Or the Java environment or application is not in the appropriate state required by the requested operation. |
IllegalThreadStateException | Throws the exception if the thread is not in the appropriate state required by the requested operation. |
IndexOutOfBoundsException | An exception that is thrown when the index of a sort is out of range, for example, the sort of an array, string, or vector, etc. |
NegativeArraySizeException | This exception is thrown if the application attempts to create an array of negative size. |
NullPointerException | This exception is thrown when an application gets a NULL instance of an object when it needs to manipulate the object. |
NumberFormatException | This exception is thrown when an application attempts to convert a string to a numeric type, but the string cannot be converted to the appropriate format. |
SecurityException | An exception thrown by the security manager indicating a security violation. |
StringIndexOutOfBoundsException | This exception is thrown by the String method to indicate that the index is negative or exceeds the size of the String. |
2. Checked Exceptions
Exceptions other than Error and RuntimeException. The Java language forces programmers to do preparatory work for such exceptions (using try… The catch… Finally or throws). You can either catch it with a try-catch statement and process it, or throw it with a throws clause declaration, otherwise the compilation will not pass. Such exceptions are usually caused by the environment in which the program is running. Since a program may run in a variety of unknown circumstances, and the programmer has no control over how the user uses the program he writes, the programmer should be prepared for such exceptions. Such as SQLException, IOException, ClassNotFoundException, etc.
Checking exceptions are exceptions that the compiler requires to be handled at compile time and that you must handle at compile time.
Common diagnostic abnormalities are:
abnormal | describe |
---|---|
ClassNotFoundException | This exception is thrown when an application tries to load a class and finds no definition of the class when it looks up by name. |
CloneNotSupportedException | This exception is thrown when an object is cloned and found to not implement the Cloneable interface. |
IllegalAccessException | Throws an exception when an application is unable to access the definition of a class, member variable, or method while attempting to access a class, member variable or method by reflection. |
InstantiationException | This exception is thrown when an attempt is made to create an instance of a Class using the newInstance method in a Class Class and the specified Class object cannot be instantiated because it is an interface or an abstract Class. |
InterruptedException | Thrown when a thread is interrupted by another thread. |
NoSuchFieldException | Throws the exception when the specified variable field cannot be found, |
NoSuchMethodException | Throws the exception when the specified class method cannot be found. |
Two, the first knowledge of abnormality
Let’s take a simple example to give you a more intuitive understanding of Java exceptions.
The following code throws the famous nullPointerException: NullPointerException.
public class Test { private int a = 1; private int b = 2; public static void main(String[] args) { Test t1 = new Test(); Test t2 = null; System.out.println(t1.a); System.out.println(t2.a); System.out.println(t2.c()); } public String c() {return "WeChat ";} public String c() {return" WeChat "; }}
Run the program, the console output is as follows:
1
Exception in thread "main" java.lang.NullPointerException
at cc.devclub.ftd.Test.main(Test.java:11)
Process finished with exit code 1
From the console output as you can see, the program to print the “1”, and then raise the Java program line 11 position. Lang. NullPointerException, then the program terminates.
3. Exception handling mechanism
When you write code to handle exceptions, there are two different ways to handle checking exceptions:
- Using a try… The catch… Finally… Statement block processing
- The throws/throw keyword is used in the method to pass the exception to the method caller
try… catch… Finally… The keyword
- Exceptions can be caught using the try and catch keywords.
- The try/catch blocks are placed where exceptions are likely to occur.
The code in a try/catch block is called guard code. The syntax for using a try/catch block is as follows:
try {
...
} catch (IOException ioException) {
...
} catch (Exception exception) {
...
} finally {
...
}
Try block:
- The try block contains code that might cause an exception.
- If the try finishes and no exceptions occur, then the code in the finally block and the code after finally, if any, is executed.
- If an exception occurs, an attempt is made to match the corresponding catch block.
Catch block:
- Each catch block is used to catch and handle a particular exception, or a subclass of that exception type. In Java7, multiple exceptions can be declared in a catch.
- The parentheses following the catch define the exception type and the exception parameters. If the exception matches and is found first, the virtual machine will use the catch block to handle the exception.
- You can use the exception parameters of the catch block to get information about the exception. Exception parameters are local variables within the catch block that cannot be accessed by other blocks.
- If the exception that occurs in the current try block is not caught in any subsequent catch, then finally is executed and the external caller to the method matches the exception handler.
- If no exception occurs in the try, then all catch blocks are ignored.
Something to watch out for
1. Local variables ina try block, local variables ina catch block (including exception variables), and local variables ina finally block cannot be shared.
2. Each catch block handles an exception. Exception matches are found from the top down in the order of catch blocks, and only the first match of the catch is executed. As a result, if there is a parent-child relationship between various catch types under the same try block, the subclass exception should be placed first and the superclass exception should be placed after. In this way, each catch block should have its own meaning.
In Java, the task of exception handling is to move the flow of execution control from the place where an exception occurs to the place where it can be handled. That is, when an exception occurs in a statement of a method, subsequent statements in that statement are no longer executed and lose focus. The execution stream jumps to the nearest matching exception handling catch block. When the exception is handled, the execution stream continues after the catch block that handled the exception.
Finally block:
- Finally blocks are not required and are usually optional.
- The code in finally executes whether an exception occurs or not and whether an exception match is handled.
- A try must have at least one catch block. Otherwise, there must be at least one finally block. But finally is not used to handle exceptions. Finally does not catch and handle exceptions.
- Finally mainly does some cleanup work, such as stream closure, database connection closure, and so on.
- The finally block must execute if the corresponding try executes, regardless of whether an exception occurs. There is only one way for a finally block not to execute: System.exit().
A good programming practice is to open resources ina try block and clean and release them ina finally block to avoid memory leaks.
Points to pay attention to:
1. In the same try… The catch… Finally… If an exception is thrown ina try block and a matching catch block exists, the catch block is executed first, then the finally block is executed. If no catch block matches, finally is executed first, and then the caller in the upper layer looks for the appropriate catch block.
2. What do you mean by…? The catch… Finally… If an exception occurs in the try block and the matching catch block also throws an exception when it is handled, then a finally block is executed: first the finally block is executed, and then the caller in the upper layer looks for the appropriate catch block.
Throws/throw keywords
- Throws keyword
If a method’s internal code throws checked exceptions that the method itself does not fully handle, the Java compiler will require you to declare the possible exceptions on the method’s signature using the throws keyword.
Throws is another way to handle exceptions, unlike try… The catch… Finally… Throws an exception that may occur in a method to the caller, but does not handle it.
The reason for this exception handling might be that the method itself does not know how to handle such an exception, or that it is better for the caller, who is responsible for the exception that might occur.
- Throw a keyword
We can also explicitly throw an exception manually by using a throw statement. The throw must be followed by an exception object. The syntax is as follows:
throw exceptionObject
The throw must be written in the method. The place where the throw is executed is the exception throw point, which is no different from the exception throw point automatically created by the JRE.
Public void save(User User) {if (User == null) throw new IllegalArgumentException("User object is empty "); / /... }
Execution order of try-catch-finally
The try-catch-finally order is a common question in any interview, especially if the finally block contains a return statement. Let’s get right to some of the interview questions:
Interview Question 1:
public static void main(String[] args) {
int result = test1();
System.out.println(result);
}
public static int test1() {
int i = 1;
try {
i++;
System.out.println("try block, i = " + i);
} catch (Exception e) {
i--;
System.out.println("catch block i = " + i);
} finally {
i = 10;
System.out.println("finally block i = " + i);
}
return i;
}
You might as well calculate what the end result of the programmer is.
The output is as follows:
try block, i = 2
finally block i = 10
10
This is a fairly simple problem, no pits, let’s change it a little bit:
public static int test2() {
int i = 1;
try {
i++;
throw new Exception();
} catch (Exception e) {
i--;
System.out.println("catch block i = " + i);
} finally {
i = 10;
System.out.println("finally block i = " + i);
}
return i;
}
The output is as follows:
catch block i = 1
finally block i = 10
10
As expected, the program throws an exception, which is then caught and handled by the method’s catch block.
Interview Question 2:
public static void main(String[] args) { int result = test3(); System.out.println(result); } public static int test3() {// int I = 1;} public static int test3() {// int I = 1; try { i++; System.out.println("try block, i = " + i); return i; } catch (Exception e) { i++; System.out.println("catch block i = " + i); return i; } finally { i = 10; System.out.println("finally block i = " + i); }}
The output is as follows:
try block, i = 2
finally block i = 10
2
Confused? Why do I finally execute the code in the finally block when there is a return in the try block?
Let’s decompile this class and look at the compiled bytecode implementation of the test3 method:
0: iconst_1 // Load 1 into operand stack 1: istore_0 // Save the element at 0 of operand stack into local variable scale 2: iinc 0, 1 // Add the element at 0 of local variable scale directly (I =2) 5: GetStatic #3 // Line 5-27 println method 8: new #5 11: dup 12: invokespecial #6 15: LDC #7 17: invokevirtual #8 20: Iload_0 21: Invokevirtual #9 24: Invokevirtual #10 27: Invokevirtual #11 30: Iload_0 // Loads elements at locally variable scale 0 positions on the operation stack (2) 31: Istore_1 // Stores the element at the top of the operation stack at position 1 32: bipush 10 // Load a constant to the operation stack (10) 34: istore_0 // Stores 10 to the local variable scale 0 35: GetStatic #3 // Line 35-57 println method in finally 38: new #5 41: dup 42: invokespecial #6 45: LDC #12 47: invokevirtual #8 50: iload_0 51: invokevirtual #9 54: invokevirtual #10 57: invokevirtual #11 60: Iload_1 // Load elements at locally-variable table 1 into the operation stack (2) 61: Ireturn / / will return to (2) the operation stack elements -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- try + finally end -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- here is the catch + finally, Similar -- -- -- -- -- -- -- -- -- -- -- -- 62, 63: astore_1 iinc 0, 1... .
As we can see from our analysis, the reason why the finally block is always executed, regardless of whether the program has an exception or not, is that the compiler makes two copies of the code in the finally block and adds them, one after the try and one after the catch.
Some people may be confused that originally our I is stored in the local variable table position 0, and finally the code in finally does fill the slot 0 position with the value 10, but why does the program still return the value 2 in the end?
If you take a closer look at the bytecode, you’ll see that before the return statement returns, the virtual machine will push the return value onto the operand stack and wait for it to return. Even if the finally block changes I, the value already exists on the operand stack, so it won’t affect the return result.
Interview Question 3:
Public static int test4() {//finally int I = 1; try { i++; System.out.println("try block, i = " + i); return i; } catch (Exception e) { i++; System.out.println("catch block i = " + i); return i; } finally { i++; System.out.println("finally block i = " + i); return i; }}
Running results:
try block, i = 2
finally block i = 3
3
You see the process in terms of its bytecode instructions, rather than just memorizing its execution.
You’ll notice that the program will end up using the return in the finally block, ignoring the return in the try block.
Custom exception
All exceptions defined in Java’s exception mechanism cannot foresee all possible errors, and in certain situations, we need to customize the exception type to report some error information up.
Custom Exception types are fairly simple. You can choose to inherit Throwable, Exception, or any of their subclasses, even if you don’t need to implement or override any of the methods of the parent class to define an Exception type.
Such as:
public class MyException extends RuntimeException{ }
public class MyException extends Exception{ }
By international convention, custom exceptions should always contain the following constructor:
- A constructor without arguments
- A constructor that takes a String argument and is passed to the constructor of the parent class.
- One takes a String argument and the Throwable argument, and both are passed to the parent constructor
- A constructor that takes the Throwable argument and is passed to the constructor of the parent class.
Here is the full source code of the IOException class for reference:
public class IOException extends Exception { static final long serialVersionUID = 7818375828146090155L; public IOException() { super(); } public IOException(String message) { super(message); } public IOException(String message, Throwable cause) { super(message, cause); } public IOException(Throwable cause) { super(cause); }}
Exceptional precautions
When a subclass overrides a superclass’s throws method, the exception declared must be within the control of the superclass’s exceptions. The same exception handler used to handle the superclass’s throws method must also be used for the subclass’s throws method. This is to support polymorphism.
For example, if a parent method throws two exceptions, a subclass cannot throw three or more exceptions. Throws IOException or a subclass of IOException.
Java programs can be multithreaded. Each thread is a separate stream of execution, a separate stack of function calls. If the program has only one thread, an exception that is not handled by any code can cause the program to terminate. If it is multithreaded, an exception that is not handled by any code will simply cause the thread in which the exception is located to terminate.
That is, exceptions in Java are thread-independent, and the problem should be resolved by the thread itself, rather than being delegated to the outside world, and not directly affecting the execution of other threads.
Common errors when using exceptions
1. Display the exception directly on the page or the client
It is not uncommon for exceptions to be printed directly to the client. When an exception occurs, by default, the container prints the exception stack directly to the page. From the perspective of customers, any exception has no practical significance, and the vast majority of customers cannot understand the exception information at all. Software development should also try to avoid presenting the exception directly to users, and it must be displayed after encapsulating the exception in the front-end display layer. Most applications today use a front-end separation mode, and the ability to print exceptions directly has improved a lot, but we still need to pay attention to this principle when coding.
2. Ignore exceptions
The following exception handling simply outputs the exception to the console and has no meaning. And when an exception occurs, the program is not interrupted, and the calling code continues to execute, causing more exceptions.
public void retrieveObjectById(Long id) { try { //.. Throws SQLException} catch (SQLException Ex) {/** * Note that the error stack is not printed. * In the Production environment, the error stack needs to be printed to the log. */ ex.printStackTrace (); */ ex.printStackTrace (); }}
Exceptions can be caught and handled, which is a big no-no when we write code, and can be recomposed:
public void retrieveObjectById(Long id) { try { //.. some code that throws SQLException } catch (SQLException ex) { throw new RuntimeException("Exception in RetieveObjectById ", the ex); } finally { //clean up resultset, statement, connection etc } }
3. Include the exception in the loop block
As shown in the code below, the exception is contained in the for loop statement block.
for (int i = 0; i < 100; i++) { try { } catch (XXXException e) { //.... }}
We all know that exception handling takes up system resources. At first glance, everyone thought it would be impossible to make such a mistake. To put it another way, class A executes A loop that calls A method of class B, and the method called in class B contains A try-catch block. Strip away the class hierarchy, and the code looks exactly like the above.
Use Exception to catch all potential exceptions
A method execution throws several different types of exceptions. For code brevity, use the base class Exception to catch all potential exceptions, as shown in the following example:
public void retrieveObjectById(Long id) { try { //... Code call that throws IOException //... } catch (Exception E) {// Use the base class Exception to catch all potential exceptions. If multiple levels of Exception catch this way, Throw new RuntimeException("Exception in RetieveObjectById ", e); }}
In order to save trouble and convenience, a top-level Exception is directly used to catch all possible exceptions. In this way, although the exception can be guaranteed to be caught, the program cannot handle the different error exceptions correctly, so it can be re-constructed:
public void retrieveObjectById(Long id) { try { //.. some code that throws RuntimeException, IOException, SQLException} catch (IOException E) {// only catch the IOException throw new RuntimeException(/* specify the error code for the IOException */code here, "The Exception in retieveObjectById", e); } catch (SQLException e) {// only catch SQLException throw new RuntimeException(/* specify the error code for this SQLException */code, "The Exception in retieveObjectById", e); }}
5. The information contained in the exception is not sufficient to locate the problem
Exception not only lets developers know what’s going wrong, but more often than not, developers need to know what’s causing the problem. We know that java.lang.Exception has a constructor for a string type parameter that can be customized into an easy-to-understand prompt message.
Simple customization information. The developer can only know where an exception has occurred, but in many cases, the developer needs to know what parameters are causing the exception. At this point we need to append the parameter information of the method call to the custom information. The following example enumerates only one argument. In the case of multiple arguments, you can write a utility class to organize the string.
public void retieveObjectById(Long id) { try { //.. Throws some code that throws SQLException} catch (SQLException Ex) {throw new RuntimeException("Exception in ") retieveObjectById with Object Id :"+ id, ex); }}
conclusion
Exception as an important error handling mechanism in the Java language, but also as an important guarantee to find the cause of the program, improve the robustness of the program, front-end product good experience, so master the use of exception is very necessary, I hope this article can help you, if you have any questions or problems welcome harassment at any time.
It is not easy to create, if you like this article, welcome thumb up, forward, your concern is our motivation to move forward
reference
- Misunderstandings and lessons learned about Java exception handling
Technical man, technical soul, a daily liver technical article, ヾ(l ° 1.6 ° sunrise) ha ha ~
About the author
- GitHub:https://github.com/ForTheDevelopers
- The Denver nuggets: https://juejin.cn/user/1204720472953022
- CSDN:https://blog.csdn.net/ForTheDevelopers
- Zhihu: https://www.zhihu.com/people/forthedevelopers
- segmentfault:https://segmentfault.com/u/for_the_developers
Contact the author
- WeChat ID: FortheDeveloper
- Official account: ForTheDevelopers