try and catch as much as you can. The second leader will help you understand the exception mechanism of java

preface

Do you understand the exception mechanism of java? The article of the second leader takes you in simple terms and has a thorough understanding.

Article by White hat of the second leader https://le-yi.blog.csdn.net/ Blog original, reprint, please indicate the source, thank you~

Program exception

Exceptions are essentially program errors. Errors often occur in the process of writing programs, including errors during compilation and running.

  1. Error during compilation:

    It's usually a syntax error. This is not the point. After all, nothing big will happen to a program that can't run.

  2. Errors during operation:

    Some wrong programs cannot be managed, some wrong programs should not be managed, and some wrong programs must be managed. If the runtime errors are not handled properly, there will be problems in the business. It is likely to be out of control.

How to manage errors during operation

When something goes wrong, we have to know first. How do we know? In java, try catch statements are used to deal with errors during running, the keyword try is used to execute code that may cause errors, and then the keyword catch is used to catch errors during program running.

public class Test {

    private static void printDiv(int dividend, int divisor) {
        System.out.println(dividend + " / " + divisor + " = " + (dividend / divisor));
    }

    public static void main(String[] args) {
        printDiv(10, 5);
        printDiv(10, 0);
        printDiv(10, 2);
    }
}


The above code outputs the results of 10 / 5, 10 / 0 and 10 / 2 respectively. However, the running results show that the program exits abnormally when executing 10 / 0, and 0 cannot be used as a divisor.

Next, we modify the code to make our program compatible with exceptions.

public class Test {

    private static void printDiv(int dividend, int divisor) {
        try {
            System.out.println(dividend + " / " + divisor + " = " + (dividend / divisor));
        } catch(ArithmeticException e) {
            System.out.println(dividend + " / " + divisor + " An error occurred:" + e.getMessage());
        }
    }

    public static void main(String[] args) {
        printDiv(10, 5);
        printDiv(10, 0);
        printDiv(10, 2);
    }
}


After handling the error with try catch syntax, you can see that when the program executes to the wrong code line, it will jump to the catch block to continue execution, and the program will not exit abnormally.

What errors can catch

The cache captures the Throwable class and its subclasses. Under Throwable, there are two major classes: Error and Exception. Other errors or exceptions should be inherited from both of them.

  1. Error:

    Error is a system level error, which should not be captured and processed by the application. It should be captured and processed by the system or framework.

  2. Exception:

    Exception is a program level error, which should be captured and processed by the program module.

How to initiate an exception

Our own code will also have exceptions. How to initiate an exception? You can use the throw keyword.

public class Test {
	private static void someMethod() {
		// Throw an exception using the throw keyword
		throw new RuntimeException("Exception occurred during operation");
	}

	public static void main(String[] args) {
		someMethod();
		System.out.println("Main method execution completed");
	}
}


We can also throw Error and its subclasses, but usually we should throw Exception and its subclasses. I've talked about their differences before.

public class Test {
	private static void someMethod() {
		// Use the throw keyword to throw an error
		throw new Error("An error occurred during operation");
	}

	public static void main(String[] args) {
		someMethod();
		System.out.println("Main method execution completed");
	}
}

We throw another exception and fail to compile.

import java.sql.SQLException;

public class Test {
	private static void someMethod() {
		// Throw an exception using the throw keyword
		throw new SQLException("Exception occurred during operation");
	}

	public static void main(String[] args) {
		someMethod();
		System.out.println("Main method execution completed");
	}
}

checked exceptions and unchecked exceptions

  1. checked exceptions

    Except RuntimeException and its subclasses, other Exception classes and their subclasses belong to searchable exceptions. The Java compiler will check this kind of Exception. When this kind of Exception may appear in the program, either catch it with a try catch statement or throw it with a throw clause declaration, otherwise the compilation will not pass.

  2. unchecked exceptions

    Non searchable exceptions (exceptions that the compiler does not require forced handling): including runtime exceptions (RuntimeException and its subclasses) and errors (errors).

It turns out that SQLException belongs to checked exceptions. We must use try catch to catch or declare to throw. We have already caught it. How can we declare to throw it? You can use the throws keyword.

import java.sql.SQLException;

public class Test {
	private static void someMethod() throws SQLException {
		// Throw an exception using the throw keyword
		throw new SQLException("Exception occurred during operation");
	}

	public static void main(String[] args) throws SQLException {
		someMethod();
		System.out.println("Main method execution completed");
	}
}

In this way, it can be compiled and passed.

Custom exception

java already has many built-in Error and Exception types. I won't list them. It's easy to find out. When the built-in Exception types can't correctly express our errors, we can also customize the Error and Exception types. We only need to inherit Error or Exception.

public class Test {
	/**
	 * Custom exception type
	 */
	static class MyRuntimeException extends RuntimeException {
		public MyRuntimeException(String message) {
			super(message);
		}
	}

	private static void someMethod() {
		// Throw an exception using the throw keyword
		throw new MyRuntimeException("Exception occurred during operation");
	}

	public static void main(String[] args) {
		someMethod();
		System.out.println("Main method execution completed");
	}
}

try's posture

There are three general positions of try

public class Test {
    public static void main(String[] args) {
        // Posture one
        try {
            // The part that is likely to throw an exception
        } catch (Exception e) {
            // Handling of exceptions
        }

        // Pose two
        try {
            // The part that is likely to throw an exception
        } finally {
            // The part to be executed regardless of whether an exception occurs
        }

        // Posture three
        try {
            // The part that is likely to throw an exception
        } catch (Exception e) {
            // Handling of exceptions
        } finally {
            // The part to be executed regardless of whether an exception occurs
        }
    }
}

The above methods are all for unified processing of errors. If there may be multiple errors in a piece of code, what should I do for a particular error?

import java.io.IOException;

public class Test {

    /**
     * Write results to file
     * @param ret
     * @throws IOException
     */
    private static void writeFile(int ret) throws IOException {
        // I pretended to write the result into the file, but I didn't do that, but that's my business. Please keep it confidential
        throw new IOException("The disk is full");
    }

    private static void printDiv(int dividend, int divisor) {
        try {
            int ret = dividend / divisor;
            System.out.println(dividend + " / " + divisor + " = " + ret);
            writeFile(ret);
        } catch(Exception e) {
            if (e instanceof IOException) {
                System.out.println(dividend + " / " + divisor + " happen IO Error:" + e.getMessage());
            } else {
                System.out.println(dividend + " / " + divisor + " An error occurred:" + e.getMessage());
            }
        }
    }

    public static void main(String[] args) {
        printDiv(10, 5);
        printDiv(10, 0);
        printDiv(10, 2);
    }
}


The above code has achieved the desired effect, but it is a little silly. In fact, java already has syntax to deal with this situation directly. The following is the correct way. The execution result is the same as that above.

import java.io.IOException;

public class Test {

    /**
     * Write results to file
     * @param ret
     * @throws IOException
     */
    private static void writeFile(int ret) throws IOException {
        // I pretended to write the result into the file, but I didn't do that, but that's my business. Please keep it confidential
        throw new IOException("The disk is full");
    }

    private static void printDiv(int dividend, int divisor) {
        try {
            int ret = dividend / divisor;
            System.out.println(dividend + " / " + divisor + " = " + ret);
            writeFile(ret);
        } catch (IOException e) {
            System.out.println(dividend + " / " + divisor + " happen IO Error:" + e.getMessage());
        } catch(Exception e) {
            System.out.println(dividend + " / " + divisor + " An error occurred:" + e.getMessage());
        }
    }

    public static void main(String[] args) {
        printDiv(10, 5);
        printDiv(10, 0);
        printDiv(10, 2);
    }
}

It should be noted that if we need to catch the exceptions of parent and child classes at the same time, the child classes must catch first, because java matches according to the code order. If the catch block of the parent class is written in front, the catch block of the child class will never run.

What if I want to deal with a variety of exceptions in groups, some of which are handled in the first way, others in the second way, and others in the third way? Of course, we can cache multiple times, and then write the same content in several cache blocks, but that's still silly. Here's the right way.

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.WriteAbortedException;

public class Test {

    /**
     * Write results to file
     * @param ret
     * @throws IOException
     */
    private static void writeFile(int ret) throws FileNotFoundException, WriteAbortedException {
        // I pretended to write the result into the file, but I didn't do that, but that's my business. Please keep it confidential
        int i = (int) (Math.random() * 3);

        switch (i) {
            case 0:
                throw new FileNotFoundException("file does not exist");
            case 1:
                throw new FileNotFoundException("File writing interrupted");
            case 2:
                throw new FileNotFoundException("The disk is full");
        }
    }

    private static void printDiv(int dividend, int divisor) {
        try {
            int ret = dividend / divisor;
            System.out.println(dividend + " / " + divisor + " = " + ret);
            writeFile(ret);
        } catch (FileNotFoundException | WriteAbortedException e) {
            System.out.println(dividend + " / " + divisor + " File write error:" + e.getMessage());
        } catch (IOException e) {
            System.out.println(dividend + " / " + divisor + " happen IO Error:" + e.getMessage());
        } catch(Exception e) {
            System.out.println(dividend + " / " + divisor + " An error occurred:" + e.getMessage());
        }
    }

    public static void main(String[] args) {
        printDiv(10, 5);
        printDiv(10, 0);
        printDiv(10, 2);
    }
}

In what cases do you need to use a finally block

First look at the following code

import java.io.FileWriter;
import java.io.IOException;

public class Test {

	/**
	 * write file
	 * @param text
	 * @throws IOException
	 */
	private static boolean writeFile(String text) throws IOException {
		try {
			System.out.println("Apply for resources");
			FileWriter fw = new FileWriter("test.txt");
			// Exceptions may occur after this
			fw.write(text);
			if (true) {
				throw new RuntimeException("Exceptions during operation");
			}
			// The stream resource is closed here, but an exception may occur before that
			System.out.println("close resource");
			fw.close();
			return true;
		} catch (Exception e) {
			System.out.println("Exception handling:" + e.getMessage());
			return false;
		}
	}

	public static void main(String[] args) throws IOException {
		writeFile("test");
	}
}

System resources such as IO streams need to be released manually after the application from the system is used up, otherwise leakage will occur. In the above code, if an exception occurs before closing the resource, the program will jump to the catch block and continue to execute, and the code that closes the resource will never execute until the program exits. What should I do? finally is needed at this time.

import java.io.FileWriter;
import java.io.IOException;

public class Test {

	/**
	 * write file
	 * @param text
	 * @throws IOException
	 */
	private static boolean writeFile(String text) throws IOException {
		FileWriter fw = null;
		try {
			System.out.println("Apply for resources");
			fw = new FileWriter("test.txt");
			// Exceptions may occur after this
			fw.write(text);
			if (true) {
				throw new RuntimeException("Exceptions during operation");
			}
			return true;
		} catch (Exception e) {
			System.out.println("Exception handling:" + e.getMessage());
			return false;
		} finally {
			if (fw != null) {
				System.out.println("close resource");
				fw.close();
			}
		}
	}

	public static void main(String[] args) throws IOException {
		writeFile("test");
	}
}

The above code has been modified to put the code for closing resources into the finally block. The code in finally will be executed regardless of whether an exception occurs in try. Not only is the resource closed, any logic that should be executed regardless of whether an exception occurs can be placed in the finally block.

Also note that exceptions may occur in the catch block, but the code in the finally block will still be executed.

import java.io.FileWriter;
import java.io.IOException;

public class Test {

	/**
	 * write file
	 * @param text
	 * @throws IOException
	 */
	private static boolean writeFile(String text) throws IOException {
		FileWriter fw = null;
		try {
			System.out.println("Apply for resources");
			fw = new FileWriter("test.txt");
			// Exceptions may occur after this
			fw.write(text);
			if (true) {
				throw new RuntimeException("Exceptions during operation");
			}
			return true;
		} catch (Exception e) {
			System.out.println("Exception handling:" + e.getMessage());
			throw new RuntimeException("An exception occurred while processing an exception");
//			return false;
		} finally {
			if (fw != null) {
				System.out.println("close resource");
				fw.close();
			}
		}
	}

	public static void main(String[] args) throws IOException {
		writeFile("test");
	}
}


However, if an exception occurs in the finally block, the subsequent code will not continue to execute.

import java.io.FileWriter;
import java.io.IOException;

public class Test {

	/**
	 * write file
	 * @param text
	 * @throws IOException
	 */
	private static boolean writeFile(String text) throws IOException {
		FileWriter fw = null;
		try {
			System.out.println("Apply for resources");
			fw = new FileWriter("test.txt");
			// Exceptions may occur after this
			fw.write(text);
			if (true) {
				throw new RuntimeException("Exceptions during operation");
			}
			return true;
		} catch (Exception e) {
			System.out.println("Exception handling:" + e.getMessage());
			return false;
		} finally {
			if (true) {
				throw new RuntimeException("finally Block exception");
			}
			if (fw != null) {
				System.out.println("close resource");
				fw.close();
			}
		}
	}

	public static void main(String[] args) throws IOException {
		writeFile("test");
	}
}


finally, the code of a block usually has to be executed, so to catch possible exceptions, the try catch syntax can be nested.

import java.io.FileWriter;
import java.io.IOException;

public class Test {

	/**
	 * write file
	 * @param text
	 * @throws IOException
	 */
	private static boolean writeFile(String text) throws IOException {
		FileWriter fw = null;
		try {
			System.out.println("Apply for resources");
			fw = new FileWriter("test.txt");
			// Exceptions may occur after this
			fw.write(text);
			if (true) {
				throw new RuntimeException("Exceptions during operation");
			}
			return true;
		} catch (Exception e) {
			System.out.println("Exception handling:" + e.getMessage());
			return false;
		} finally {
			try {
				throw new RuntimeException("finally Block exception");
			} finally {
				if (fw != null) {
					System.out.println("close resource");
					fw.close();
				}
			}
		}
	}

	public static void main(String[] args) throws IOException {
		writeFile("test");
	}
}

Try with resources syntax

There are many situations of limited system resources such as IO streams, which are similar to each other. They all need to manually call and close after the final use. It's a bit silly and repetitive to write code with a general template every time. Moreover, in order for the finally block and the try block to access that variable, it must be declared outside the try catch, which leads to the fact that the code behind the finally block can also access that variable, which is unreasonable. So later, java has a new syntax to deal with this situation. All classes that implement the autoclosable interface can be closed automatically.

import java.io.IOException;

public class Test {
	static class SomeResource implements AutoCloseable {
		public void use() {
			System.out.println("Use resources");
		}

		@Override
		public void close() {
			System.out.println("close resource");
		}
	}

	/**
	 * write file
	 * @param text
	 */
	private static boolean writeFile(String text) {
		try (SomeResource resource = new SomeResource();) {
			resource.use();
			// Exceptions may occur after this
			if (true) {
				throw new RuntimeException("Exceptions during operation");
			}
			return true;
		}
	}

	public static void main(String[] args) throws IOException {
		writeFile("test");
	}
}

You can see that after using resources, the above code does not call the closing method manually. The closing method is called automatically. The FileWriter we used before has implemented the autoclosable interface, so it can also be closed automatically.

Multiple resources that can be automatically closed can be used in a block, and the order is the reverse order of initialization.

import java.io.IOException;

public class Test {
	static class SomeResource implements AutoCloseable {
		private final String name;

		SomeResource(String name) {
			this.name = name;
			System.out.println("Initialize resource:" + name);
		}

		public void use() {
			System.out.println("Use resources:" + name);
		}

		@Override
		public void close() {
			System.out.println("Close resource:" + name);
		}
	}

	/**
	 * write file
	 *
	 * @param text
	 */
	private static boolean writeFile(String text) {
		try (
				SomeResource resource1 = new SomeResource("Test resource 1");
				SomeResource resource2 = new SomeResource("Test resource 2");
				SomeResource resource3 = new SomeResource("Test resource 3");
		) {
			resource2.use();
			resource1.use();
			resource3.use();
			// Exceptions may occur after this
			if (true) {
				throw new RuntimeException("Exceptions during operation");
			}
			return true;
		}
	}

	public static void main(String[] args) throws IOException {
		writeFile("test");
	}
}

Epilogue

The second leader has tried to write comprehensively. If there is anything incomplete or inaccurate, please forgive me and welcome the discussion in the comment area. In addition, please reward three companies. Thank you.

Keywords: Java

Added by tsilenzio on Fri, 28 Jan 2022 01:59:09 +0200