[Java] takes you from zero to a series of 18 exceptions

Foreword: in addition to learning so much basic knowledge of Java, there is one thing we can't avoid when typing code, that is exception. So in this article, let's learn about exceptions to see what exceptions are in Java, what they are used for, and where they come from.

Each picture:

I Abnormal background

1. Initial knowledge abnormality

First of all, let's take a look at some exceptions we have encountered before. Let's recall the exception:
For example:
(1). Divide by 0

    public static void main(String[] args) {
        System.out.println(10/0);
        //0 cannot be a divisor
    }
    

Abnormal operation result report:

(2). Accessing null objects

    public static void main(String[] args) {
        String str = null;
        System.out.println(str.length());
        //str points to null and has no length
    }

Abnormal operation result report:

So the so-called exception ------- >

The so-called exception refers to a mechanism for the program to notify the caller when an error occurs at runtime. The keyword is "runtime". Some errors, such as system out. Println is misspelled and written as system out. Println at this time, there will be an error in the compilation process, which is an error in the "compilation period".

2. Defensive programming

Then we need to know: errors exist objectively in the code! No matter how good the software is, no matter how good the code is, there will be bugs in it, so we should face these bugs objectively. Therefore, we should inform the program in time when there is a problem with the program We have two main ways:

1.LBYL: Look Before You Leap. Make adequate checks before operation.

2.EAFP: It's Easier to Ask Forgiveness than Permission. "It's easier to get forgiveness afterwards than to get permission in advance.". That is, operate first and deal with problems later.

These two points seem a little difficult to understand, but let's give an example to make it easy to understand. For example, when boys start falling in love, they want to hold hands when shopping with girls. The first is our LBYL. Before operation, make preparations and ask girls if I can hold your hand? This is obviously a little subtle; The second is to take the initiative to hold hands, and then naturally hold them together, or be scolded by girls at most, which is to deal with problems.

So the core idea of exception is EAFP!

Let's take another example of the game:

In the afternoon, I was bored in the dormitory. I turned on my mobile phone to pick up the king (we use pseudo code to demonstrate the process of starting a game of King glory). If it is LBYL style code (no exceptions):

        boolean ret = false;
        ret = Login game();
        if (!ret) {
            Handling login game errors;
            return;
        }
        ret = Start matching();
        if (!ret) {
            Processing matching errors;
            return;
        }
        ret = Game confirmation();
        if (!ret) {
            Processing game confirmation errors;
            return;
        }
        ret = Choose Heroes();
        if (!ret) {
            Handling selection hero errors;
            return;
        }
        ret = Load game screen();
        if (!ret) {
            Handling load game errors;
            return;
        }
        ......

This is a step-by-step method. Let's look at EAFP style code (using exceptions):

        try {
            Login game();
            Start matching();
            Game confirmation();
            Choose Heroes();
            Load game screen();
             ...
        } catch (Abnormal login game) {
            Handling login game exceptions;
        } catch (Start matching exception) {
            Processing start matching exception;
        } catch (Abnormal game confirmation) {
            Handling game confirmation exceptions;
        } catch (Select hero exception) {
            Handle select hero exception;
        } catch (Abnormal loading of game screen) {
            Handling loading game screen exception;
        }
           ......

Here is the code with exception handling. When there is something wrong, we will deal with it. Generally speaking, using the first method, the code of normal process and error handling process are mixed together, and the code is chaotic as a whole. In the second way, the normal process and error process are separated, which makes it easier to understand the code.

II Basic syntax of exceptions

Next, let's learn the basic syntax of exceptions.

1. Catch exception

Basic syntax:

        try{
            Statements with possible exceptions ;
        }[catch (Exception type exception object) {
        } ... ]
        [finally {
            Abnormal exit
        }]

This is the syntax for exception handling in the above code for starting the king. Here are some points to explain:

  1. The try code block contains the code with possible exceptions
  2. The catch code block contains the handling behavior after an exception occurs
  3. finally, the code in the code block is used to deal with the aftermath and will be executed at the end
  4. catch and finally can be added or not according to the situation

For try, catch and finally, let's take an example:

    public static void main(String[] args) {
        int [] array ={1,2,3};
        System.out.println(array[5]);
        //The array here has no 5 subscript. It is obvious that the array is out of bounds
        System.out.println("I can't print it out");
        //And after we see the exception in the running result, the following code will not be executed
    }


Then we can modify the code to write the possible exception array out of bounds when we deal with the array:

    public static void main(String[] args) {
        int [] array ={1,2,3};
        try{
            System.out.println(array[5]);
            System.out.println("I can't print it out");
            //After the exception is thrown, the following code in try will not be executed
        }catch (ArrayIndexOutOfBoundsException e){
            System.out.println("I caught an array out of bounds exception. Let's deal with the exception here");
            //Array out of bounds exception printing
        }
            System.out.println("I can print here");
            //In this way, the following code will not affect normal printing

    }


Therefore, the advantage here is to handle the exception without affecting the later code execution. If we don't handle the exception, we will hand it over to the JVM for processing, and the JVM handler will terminate immediately!!

2. Exception handling process

Then, let's start with an example of the processing flow of catching exceptions:

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int arr = scanner.nextInt();
        //Simply enter a number of type int and print it
        System.out.println(arr);
    }

If we don't normally enter an integer and I enter a string, it will normally report an error, so let's take a look at the error analysis:

Then, for exception handling, we will follow the following steps:

Moreover, in the exception information stack here, we should first find the exception in blue font, because the gray part is the source code part. Can there be any errors in the source code part, so here is to correct the blue exception first and then run debugging.

The example here is just the tip of the iceberg. When dealing with exceptions, we should make different decisions under different scenarios:

For more serious problems (such as scenarios related to counting money), the program should be allowed to crash directly to prevent more serious consequences.
For less serious problems (most scenarios), you can record the error log and timely notify the program through the monitoring alarm program.
For problems that may be recovered (network related scenarios), you can try to retry.

For the second point, the error log we recorded is the method call information of exceptions, which can quickly let us find the location of exceptions.

for instance:

    public static void main(String[] args) {
        int [] array ={1,2,3};
        try{
            array = null;
            System.out.println(array[2]);
            System.out.println("I can't print it out");
        }catch (ArrayIndexOutOfBoundsException e){
            System.out.println("I caught an array out of bounds exception!!");
        }catch (NullPointerException e){
            System.out.println("I caught a null pointer exception!!");
        }
        System.out.println("I can print here");

    }

Although there are only two exceptions here, it is difficult to find if you write some more, three, four or 100, so we can add a line of code to the exception handling and print the error message:

    public static void main(String[] args) {
        int [] array ={1,2,3};
        try{
            array = null;
            System.out.println(array[2]);
            System.out.println("I can't print it out");
        }catch (ArrayIndexOutOfBoundsException e){
            e.printStackTrace();
            System.out.println("I caught an array out of bounds exception!!");
        }catch (NullPointerException e){
            e.printStackTrace();
            //Print error message
            System.out.println("I caught a null pointer exception!!");
        }
        System.out.println("I can print here");

    }

Operation results:

When we click the blue font, it will jump to the place where the error message appears, which makes it easier for us to find the error and modify the error.

For exception handling, we can jump to the following Java exception system. In fact, these exceptions have a system. Exceptions have a parent-child relationship. At this time, we need to consider what parent-child classes will be like in exception handling. Let's take a look:

For example, for the above array out of bounds exception, press and hold ctrl in the IDEA and click the exception information to jump to its source code. We can see that its parent class is the exception of IndexOutOfBoundsException:

In fact, we can write the above code for handling array out of bounds exceptions like this, because the parent class contains exceptions of subclasses:

    public static void main(String[] args) {
        int [] array ={1,2,3};
        try{
            System.out.println(array[5]);
            System.out.println("111111");
        }catch (IndexOutOfBoundsException e){
            e.printStackTrace();
            System.out.println("I caught an array out of bounds exception!!");
        }

        System.out.println("222222");

    }


If we continue to click IndexOutOfBoundsException, and then look at its parent class, and then click its parent class, what will be the last? If we continue to click, the following answer will appear. Finally, in fact, Exception class is the parent class of all Exception classes, so we can use this type to capture all exceptions.

It is worth noting here that:

  1. When catch performs type matching, it will not only match Exception objects of the same type, but also catch subclass objects of the target Exception type. As in the previous code, NullPointerException and ArrayIndexOutOfBoundsException are subclasses of Exception, so they can be caught.

  2. For catch that contains both subclass exception handling and parent exception handling, if the order is that the subclass is first and the parent is last, it will run normally; If the parent class is in front, an error will be reported, because there is no need for subclass processing after the parent class.

  3. Although the Exception class is the parent of all Exception classes and can be used to catch all exceptions, this content is too large, and if you don't pay attention, you can't clearly indicate what the Exception is, so it's not recommended to use it.


Finally, let's talk about our finally. Finally means the final aftermath work, such as releasing resources.

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        try{
            int arr = scanner.nextInt();
            System.out.println(arr);
        }catch (InputMismatchException e){
            e.printStackTrace();
            System.out.println("Wrong input!");
        }catch (ArithmeticException e){
            e.printStackTrace();
            System.out.println("Arithmetic exception, possibly 0 as divisor");
        }finally{
            //Typically used as a resource closure
            scanner.close();
            System.out.println("finally Yes");
        }
    }

Note: for finally, no matter whether it is finished or not, it will be executed. For example, when we encounter return, the return will be finished, but the finally part will still be executed. Therefore, try to avoid writing return in finally.

There are also some codes that will be passed up along the call stack if there is no appropriate method to handle exceptions in this method. If there is no appropriate method to handle exceptions all the time, it will be handed over to the JVM for processing and the program will terminate abnormally (the same as when we didn't use try catch at first):

    public static void main(String[] args) {
        try {
            func();
        } catch (ArrayIndexOutOfBoundsException e) {
            e.printStackTrace();
        }
        System.out.println("after try catch");
    }
    public static void func() {
        int[] arr = {1, 2, 3};
        System.out.println(arr[100]);
        //Wrong, but there is no way to deal with it 
    }

Therefore, combining all the above contents, a complete set of exception handling process is obtained:

  1. The program executes the code in try first
  2. If an exception occurs in the code in the try, the code in the try will be ended to see if it matches the exception type in the catch
  3. If a matching exception type is found, the code in catch is executed
  4. If no matching exception type is found, the exception is passed up to the upper caller
  5. Whether or not a matching exception type is found, the code in finally is executed to (before the end of the method)
  6. If the upper caller does not handle the exception, it will continue to pass it up
  7. Until the main method has no appropriate code to handle exceptions, it will be handed over to the JVM for processing, and the program will terminate abnormally

3. Throw exception and exception description

In addition to some exceptions thrown by Java built-in classes, programs can also throw an exception manually. Use the throw keyword to do this:

public class TestDemo {
    public static int func(int x,int y) throws ArithmeticException{
        if(y == 0){
            throw new ArithmeticException("by zero!");
            //Throw custom exception
        }
        return x/y;
    }

    public static void main(String[] args) {
        System.out.println(func(10, 0));
    }
}

In this code, we can throw the required exception according to the actual situation, and specify some descriptive information when constructing the exception object.

In the above code, throws refers to the declaration of throwing custom exceptions that we write below, and then throwing custom exceptions is guided by throw. The two usually appear in pairs.

III Exception system of Java

The exception system in Java is as follows:

In addition to what is shown here, there are actually many subclasses that are not shown, but there are too many exceptions. We only need to check them when we need them, and there is no need to remember them all. Back here, the top-level class Throwable derives two important subclasses - Error and Exception.

Error refers to the internal error and resource exhaustion error of Java runtime. The application does not throw such an exception Once this internal error occurs, there is no power except to inform the user and terminate the program. This kind of situation rarely occurs, and the programmer must deal with the code by himself.

Exception is the parent of the exception class used by our program. Exception has a subclass called RuntimeException, which derives many common exception classes, such as NullPointerException, indexoutofboundsexception, etc.

The Java language specification calls all exceptions derived from the Error class or RuntimeException class non checked exceptions, and all other exceptions are called checked exceptions.

So our diagram should look like this:

If it is the checked exception code:

    public static void main(String[] args) {
        System.out.println(readFile());
    }
    public static String readFile() {
        // Try opening the file and reading one line
        File file = new File("d:/test.txt");
        // Use file objects to construct Scanner objects
        Scanner sc = new Scanner(file);
        return sc.nextLine();
    }

The display in IDEA is as follows:

Obviously, we can see that the Scanner reports a red line, and then the prompt is an unreported exception error Java io. FileNotFoundException; It must be captured or declared in order to be thrown. Exceptions such as FileNotFoundException are checked exceptions Compilation cannot pass without explicit processing.

Of course, we can click Scanner and alt + enter to adapt IDEA to a solution,
For example, we choose the first method to add exception handling:

Then we add an exception declaration, which can be compiled:

Similarly, you can also use the above-mentioned try catch to package:

    public static void main(String[] args) {
        System.out.println(readFile());
    }
    public static String readFile() {
        File file = new File("d:/test.txt");
        Scanner sc = null;
        try {
            sc = new Scanner(file);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        return sc.nextLine();
    }

IV Custom exception class

Although there are many built-in exception classes in Java, there may be some situations in our actual scene that require us to extend the exception class and create exceptions that meet our actual situation.

For example, we need to implement a user login process:

    public class Test {
        private static String userName = "admin";
        private static String password = "123456";
        public static void main(String[] args) {
            login("admin", "123456");
        }
        public static void login(String userName, String password) {
            if (!Test.userName.equals(userName)) {
                //Processing user name error
            }
            if (!Test.password.equals(password)) {
                //Processing password error
            }
            System.out.println("Login successful");
        }
    }

At this time, we may need to throw two kinds of exceptions when dealing with user name and password errors. We can extend (inherit) the existing exception classes and create exception classes related to our business. That is, handle user name errors and password errors.

    class UserError {
        //Processing user name error
    }
    class PasswordError {
      	//Processing password error
    }

But in fact, there will still be errors in writing like this, because if we want to customize our own exceptions, we need to inherit them into the exceptions contained in Java itself, otherwise they will not be recognized, so we should write as follows:

    class UserError extends Exception {
    //Inherit from exception
        public UserError(String message) {
            super(message);
        }
    }
    class PasswordError extends Exception {
    //Inherit from exception
        public PasswordError(String message) {
            super(message);//
        }
    }

Then the source code is changed to:

class UserError extends Exception {
    public UserError(String message) {
        super(message);//Provide construction method
    }
}
class PasswordError extends Exception {
    public PasswordError(String message) {
        super(message);//Provide construction method
    }
}

public class TestDemo2 {
    private static String userName = "admin";//Default user name
    private static String password = "123456";//Default password
    public static void main(String[] args) {
        try {
            login("admin", "123456");//Call method
        } catch (UserError userError) {
            userError.printStackTrace();//User name exception handling
        } catch (PasswordError passwordError) {
            passwordError.printStackTrace();//Password exception handling
        }
    }
    public static void login(String userName, String password) throws UserError,
            PasswordError {
        if (!TestDemo2.userName.equals(userName)) {
            throw new UserError("User name error");//The user names are not equal, and the user name error exception is caught
        }
        if (!TestDemo2.password.equals(password)) {
            throw new PasswordError("Password error");//The passwords are not equal, and the password error exception is caught
        }
        System.out.println("Login successful");//If everything is normal, the login is successful
    }

}

But inheritance will also pay attention to. Remember the above diagram of Java architecture? Yes, it is the following one. If we inherit Exception, we inherit the checked Exception, that is, we should wrap the Exception handling with try catch or refer to it with throws and throw. If we inherit Runtime Exception, it is non checked Exception.

Finally, precautions:

  1. Custom exceptions usually inherit from Exception or RuntimeException
  2. Exceptions inherited from Exception are checked exceptions by default
  3. Exceptions inherited from RuntimeException are non checked exceptions by default

This is the whole content of this exception. It is the end of the series from zero to Java. Next, I will write some blogs about data structure. Welcome to pay attention. Study together and work together! You can also look forward to the next blog in the next series.

Link: It's all here! Java SE takes you from zero to a series

There is another thing:

Keywords: Java Back-end JavaSE

Added by pedrolopes10 on Wed, 26 Jan 2022 10:54:35 +0200