Yesterday Sir Old source in the interview room waiting for the candidate, accidentally heard the interviewer in the next room asked the candidate a question: “Java Exception and Error are different?” . Old source Sir Heard here heart hey hey smile. This is a common serial question. Unexpectedly, the candidate stumped on his first question. Old source Sir Can not help but for the candidate pinched a sweat ah, this problem is actually quite basic… At that time, I made up my mind to write an article to explain the abnormality of Java, so that we are no longer afraid of meeting this kind of problem in the interview.

Throw statement

Those of you with some Java background probably know the throw statement (if you don’t, you can click the cross in the upper right corner 😂). We all know what a throw does. It throws an object subclass of the throwable, and the virtual machine does a series of things to that object, either catching it or not, and eventually stopping the thread in which the statement was created.

So what does the JVM actually do? Let’s give it a try:

Throw a RuntimeException:

package com.company;

public class TestException {
    public static void main(String[] args) {
        throw newRuntimeException(); }}Copy the code

After compiling, go to the directory where the class file is located and use javap-verbose to open the.class file:

 javap -verbose TestException
Copy the code

You can see some bytecode. We find the bytecode corresponding to the testException. main function:

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: new           #2                  // class java/lang/RuntimeException
         3: dup
         4: invokespecial #3                  // Method java/lang/RuntimeException."<init>":()V
         7: athrow
      LineNumberTable:
        line 5: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       8     0  args   [Ljava/lang/String;
Copy the code

If you look at the code section, the first three lines actually correspond to new RuntimeExcpetion(), which I won’t expand here for theme reasons. The main thing is the athrow. What does it do?

1. Check the top element, which must be a reference to a java.lang.Throwable subclass object; 2. Refer to the stack above and search the exception table of this method to see if there is a handler to handle this exception; 2.1 If the corresponding Handler is found, use this handler to handle exceptions. 2.2 If the corresponding handler cannot be found, the current method will exit the stack frame (exit the current method), and search the exception table in the method calling this method (repeat 2); 3. If the handler cannot be found, the current thread terminates and exits with an exception and a stack.Copy the code

You can see that throwing has several possible side effects:

  1. Terminates the current method call and passes an exception message to the method caller;
  2. If the exception is never found in the method’s exception tablehandler, eventually causing the thread to exit.

Okay, so we’ve figured out what Throw does. But notice that the athrow directive looks for a reference to a java.lang.Throwable subclass object, meaning that the throw statement can only be followed by a java.lang.Throwable subclass object, otherwise the compilation will fail. So what exactly is a Throwable?

Throwable class cluster

A Throwable is an object that can be thrown. It is the parent of all exceptions in Java:

public class Throwable implements Serializable {... }Copy the code

The exception class in the JDK looks like this:

First, you can see that Throwable falls into two broad categories, Exception and Error

Error

An Error is an Error thrown by the Java VIRTUAL machine. It is a serious problem that occurs during program running.

For example, if the virtual machine runs out of heap memory, an OutOfMemoryError is thrown. The code for these exceptional users does not need to be captured because it is useless to do so.

It’s like a broken ship, and the passengers can’t help it even if they know it’s broken, because it’s not their problem to solve.

Exception

Exceptions are possible predictable, recoverable problems in your application.

What does that mean? That is, exceptions are thrown at the user code level. In other words, these anomalies can be solved by the passengers themselves. For example, the common null pointer exception NullPointerException, take will throw ArrayIndexOutOfBoundException array subscript crossing the line.

These are problems caused by the wrong operation of the “passenger”, so the “passenger” can be solved.

Here, the old source Sir Heard the interview question, is it already solved?

CheckedException and UncheckedException

Old source Sir Also said before, this is a common serial problem. So, having solved the first question, what will the interviewer ask next?

As you can see from the previous section, the Throwable system itself is more relevant to programmers than Exception and its subclasses. Because the passengers on the ship are created by the programmers, so their misbehavior, the programmers still need to understand more thoroughly.

Exceptions fall into two categories, CheckedException and UncheckedException.

  • UncheckedException

As the name suggests, an UncheckedException is an exception that can go unchecked. The JVM specifies that exceptions inherited from RuntimeException are UncheckedException.

  • CheckedException

All exceptions that are not runtimeExceptions.

So the question is, what is an exception being examined? Who to check?

Throws the statement

Consider the following development scenario:

  • Student A wrote A tool class, compiled into A JAR package for student B to use
  • When student B called this tool class, many different types of exceptions were thrown due to different application scenarios. Every time a new exception appeared, student B had to modify the code to adapt it, which was very painful.

To solve the problem in this scenario, the JVM specifies that each function must know what exceptions it wants to throw and declare the exceptions that the function may throw with the throws statement:

 public Remote lookup(String name)
        throws RemoteException, NotBoundException, AccessException;
Copy the code

This declaration is the checked exception mentioned earlier.

What about exceptions that can go unchecked?

The main reason why the CheckedExcpetion is checked is because the caller has it and you handle it.

Take the java.net.URL constructor for example:

public final class URL implements java.io.Serializable {...public URL(String protocol, String host, int port, String file,
               URLStreamHandler handler) throws MalformedURLException {...if (port < -1) {
                throw new MalformedURLException("Invalid port number :"+ port); }}}Copy the code

MalformedURLException is a checked exception. When the input port < -1, the program throws a MalformedURLException so that the caller can correct the port input to get the correct URL.

But there are cases, such as the following function:

public void method(a){
   int [] numbers = { 1.2.3 };
   int sum = numbers[0] + numbers[3];
}
Copy the code

Only three elements as an array of Numbers, but the function is the fourth element, so calling method () will throw an exception when ArrayIndexOutOfBoundsException. But the exception caller cannot fix it.

In this case, the JVM specifically specifies that the UnchekcedException of RuntimeException and its subclasses may not be declared by the THROWS statement and will not be reported at compile time.

Exception handling

Above we have introduced the definition of exceptions and how to throw them, so how to catch and handle exceptions? This is where the try Catch Finally comes in. Here’s an example:

public void readFile(String filePath) throws FileNotFoundException {
    FileReader fr = null;
    BufferedReader br = null;
    try{
        fr = new FileReader(filePath);
        br = new BufferedReader(fr);
        String s = "";
        while((s = br.readLine()) ! =null){ System.out.println(s); }}catch (IOException e) {
        System.out.println("Error reading file:" + e.getMessage());
    } finally {
        try {
            br.close();
            fr.close();
        } catch (IOException ex) {
            System.out.println("Error closing file:"+ ex.getMessage()); }}}Copy the code

This is a function that prints the contents of the file line by line. CheckedException FileNotFoundException is thrown when the entered filePath does not exist.

In the process of file reading, some unexpected situations may cause some IOexceptions, so the code carries out try catch finally processing for possible IOexceptions. Inside the try block is normal business code, catch is handling exceptions, and finally is code that executes whether or not a try is an exception. In the case of readFile, you close the file handle to prevent memory leaks.

The uncomfortable part here is that since the FR br needs to be executed ina finally block, it must be declared before the try. Is there a more elegant way to write it?

Here are some new features in JDK 7:

try-with-resources

Try-with-resources is not a feature, but a set of solutions to make exception catching statements more elegant.

For any class that implements the java.io.Closeable interface, the JVM automatically adds a finally block to execute these Closeable close() methods as soon as it is initialized in () after the try.

Closable is defined as follows:

public interface Closeable extends AutoCloseable {

    /**
     * Closes this stream and releases any system resources associated
     * with it. If the stream is already closed then invoking this
     * method has no effect.
     *
     * <p> As noted in {@link AutoCloseable#close()}, cases where the
     * close may fail require careful attention. It is strongly advised
     * to relinquish the underlying resources and to internally
     * <em>mark</em> the {@code Closeable} as closed, prior to throwing
     * the {@code IOException}.
     *
     * @throws IOException if an I/O error occurs
     */
    public void close(a) throws IOException;
}
Copy the code

Since both FileReader and BufferedReader implement Closeable, our readFile function can be rewritten as follows:

    public void readFile(String filePath) throws FileNotFoundException {
        try(
                FileReader fr = new FileReader(filePath);
                BufferedReader br = new BufferedReader(fr)
        ){
            String s = "";
            while((s = br.readLine()) ! =null){ System.out.println(s); }}catch (IOException e) {
            System.out.println("Error reading file:"+ e.getMessage()); }}Copy the code

Is it a lot fresher?

Several special cases of finally

Finally, let’s talk about Finally 😂.

For a complete try catch finally block, it is executed in the following order: try –> catch –> finally.

But there’s always some really weird code out there that’s worth investigating:

    public static void returnProcess(a) {
        try {
            return;
        } finally {
            System.out.println("Hello"); }}Copy the code

Can anyone guess if the code in finally executes? If you execute a try block first, finally will not be executed again. But the reality is that code in finally is executed after a return.

Take a look at the following code and guess if the code in finally will execute:

    public void exitProcess(a) {
        try {
            System.exit(1);
        } finally {
            System.out.println("Hello"); }}Copy the code

As some of you may have said before, the code in finally executes whether or not the code ina try throws an exception. But that’s not accurate. In this example, the code in try calls System.exit(1), which exits the Java process directly. The process exits, and finally blocks are no longer executed.

Take a look at the following code:

    public static int returnInt(a) {
        int res = 10;
        try {
            res = 30;
            return res;
        } finally {
            res = 50; }}public static void main(String[] args) {
        System.out.println(returnInt());
    }
Copy the code

As a rule of thumb in code 1, when statements in finally execute, res should be set to 50, so the program should print 50.

But it’s not. It’s still going to print 30.

What!!

Return statements should be viewed not from the perspective of a function, but from the perspective of the JVM. From a function perspective, return is always the last line of the statement executed. But from the JVM’s point of view, it’s just a normal statement.

Then what if I return in finally?

    public static int returnInt(a) {
        int res = 10;
        try {
            res = 30;
            return res;
        } finally {
            res = 50;
            returnres; }}public static void main(String[] args) {
        System.out.println(returnInt());
    }
Copy the code

Ha ha, this time will output 50!

What the hell! I am in the wind!

This is because return statements are actually “overwritten”. That is, when a return statement appears in finally, all return statements elsewhere are invalid. The return statement in finally comes after the assignment res = 50, so it returns 50.

As you can see, using a return ina finally block is a very dangerous thing:

Don’t use return in finally!

summary

This paper mainly introduces the following contents:

  • JavaThe working principle of exceptions;
  • JavaAbnormal class cluster;
  • Exception handling process;
  • finallyA few special cases of the statement.

Hopefully, after reading this article, you won’t be hit by any more interview questions about Java exceptions! Because the level is limited, if there is any deficiency, you are also welcome to discuss in the message area!