Skip to content
On this page

Exceptions in Java

Various errors always occur during the running of computer programs.

There are some errors caused by users. For example, the user is expected to enter an age of type int , but the user's input is abc :

java
String s = "abc";
int n = Integer.parseInt(s); // NumberFormatException!

The program wants to read and write the contents of a file, but the user has deleted it:

java
String t = readFile("C:\\abc.txt"); // FileNotFoundException!

There are also some errors that occur randomly and can never be avoided. for example:

  • The network was suddenly disconnected and the remote server could not be connected;
  • Memory is exhausted and the program crashes;
  • The user clicks "Print", but there is no printer;

Therefore, a robust program must handle a wide variety of errors.

The so-called error means that when the program calls a certain function, if it fails, it means an error.

How does the caller know that the call failed? There are two methods:

Method 1: Agree to return an error code.

For example, when processing a file, if 0 is returned, it indicates success, and other integers are returned, indicating the agreed error code:

java
int code = processFile("C:\\test.txt");
if (code == 0) {
    // ok:
} else {
    // error:
    switch (code) {
    case 1:
        // file not found:
    case 2:
        // no read permission:
    default:
        // unknown error:
    }
}

Because the error code of type int is used, it is very troublesome to deal with it. This approach is common in low-level C functions.

Method 2: Provide an exception handling mechanism at the language level.

Java has a built-in exception handling mechanism, and exceptions are always used to indicate errors.

Exception is a class , so it has type information itself. Exceptions can be thrown anywhere, but they only need to be caught at the upper level, so that they are separated from the method call:

java
try {
    String s = processFile(“C:\\test.txt”);
    // ok:
} catch (FileNotFoundException e) {
    // file not found:
} catch (SecurityException e) {
    // no read permission:
} catch (IOException e) {
    // io error:
} catch (Exception e) {
    // other error:
}

Because Java's exception is class , its inheritance relationship is as follows:

                     ┌───────────┐
                     │  Object   │
                     └───────────┘


                     ┌───────────┐
                     │ Throwable │
                     └───────────┘

                 ┌─────────┴─────────┐
                 │                   │
           ┌───────────┐       ┌───────────┐
           │   Error   │       │ Exception │
           └───────────┘       └───────────┘
                 ▲                   ▲
         ┌───────┘              ┌────┴──────────┐
         │                      │               │
┌─────────────────┐    ┌─────────────────┐┌───────────┐
│OutOfMemoryError │... │RuntimeException ││IOException│...
└─────────────────┘    └─────────────────┘└───────────┘

                    ┌───────────┴─────────────┐
                    │                         │
         ┌─────────────────────┐ ┌─────────────────────────┐
         │NullPointerException │ │IllegalArgumentException │...
         └─────────────────────┘ └─────────────────────────┘

It can be seen from the inheritance relationship: Throwable is the root of the exception system, and it inherits from Object . Throwable has two systems: Error and Exception . Error represents a serious error, and the program generally cannot do anything about it, for example:

  • OutOfMemoryError : memory exhausted
  • NoClassDefFoundError : Unable to load a Class
  • StackOverflowError : stack overflow

Exception is a runtime error that can be caught and handled.

Some exceptions are part of application logic processing and should be caught and handled. For example:

  • NumberFormatException : Numeric type format error
  • FileNotFoundException : File not found
  • SocketException : Failed to read network

There are also some exceptions caused by incorrect programming of program logic, and the program itself should be repaired. For example:

  • NullPointerException : Calling a method or field on a null object
  • IndexOutOfBoundsException : Array index out of bounds

Exception are divided into two categories:

  • RuntimeException and its subclasses;
  • Non-RuntimeException (including IOException , ReflectiveOperationException etc.)

Java stipulates:

  • Exceptions that must be caught include Exception and its subclasses, but do not include RuntimeException and its subclasses. This type of exception is called Checked Exception.
  • Exceptions that do not need to be caught include Error and its subclasses, RuntimeException and its subclasses.

Notice

The compiler does not impose mandatory catching requirements for RuntimeException and its subclasses. This does not mean that the application itself should not catch and handle RuntimeException. Whether it needs to be captured should be analyzed in detail.

catch exception

To catch exceptions, use try...catch statement, put the code that may cause exceptions in try {...} , and then use catch to capture the corresponding Exception and its subclasses:

java
// try...catch
import java.io.UnsupportedEncodingException;
import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        byte[] bs = toGBK("中文");
        System.out.println(Arrays.toString(bs));
    }

    static byte[] toGBK(String s) {
        try {
            // Convert String to byte[] using specified encoding:
            return s.getBytes("GBK");
        } catch (UnsupportedEncodingException e) {
            // If the system does not support GBK encoding, UnsupportedEncodingException will be caught:
            System.out.println(e); // Print exception information
            return s.getBytes(); // Try using the default encoding
        }
    }
}

If we do not catch UnsupportedEncodingException , compilation failure will occur:

java
// try...catch
import java.io.UnsupportedEncodingException;
import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        byte[] bs = toGBK("中文");
        System.out.println(Arrays.toString(bs));
    }

    static byte[] toGBK(String s) {
        return s.getBytes("GBK");
    }
}

The compiler will report an error with a message similar to: unreported exception UnsupportedEncodingException; must be caught or declared to be thrown, and accurately indicate that the statement that needs to be caught is return s.getBytes("GBK"); This means that Checked Exceptions like UnsupportedEncodingException must be caught.

This is because the String.getBytes(String) method definition is:

java
public byte[] getBytes(String charsetName) throws UnsupportedEncodingException {
    ...
}

When defining a method, use throws Xxx to indicate the types of exceptions that may be thrown by the method. The caller must be forced to catch these exceptions when calling, otherwise the compiler will report an error.

In toGBK() method, because the String.getBytes(String) method is called, UnsupportedEncodingException must be caught. We can also not capture it, but use throws at the method definition to indicate that toGBK() method may throw UnsupportedEncodingException , so that toGBK() method can pass the compiler check:

java
// try...catch
import java.io.UnsupportedEncodingException;
import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        byte[] bs = toGBK("中文");
        System.out.println(Arrays.toString(bs));
    }

    static byte[] toGBK(String s) throws UnsupportedEncodingException {
        return s.getBytes("GBK");
    }
}

The above code will still get a compilation error, but this time, the compiler will not prompt the problem of calling return s.getBytes("GBK"); but byte[] bs = toGBK("中文"); ;. Because in the main() method, toGBK() is called, UnsupportedEncodingException that it declares may be thrown is not caught.

The fix is to catch the exception in the main() method and handle it:

java
import java.io.UnsupportedEncodingException;
import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        try {
            byte[] bs = toGBK("中文");
            System.out.println(Arrays.toString(bs));
        } catch (UnsupportedEncodingException e) {
            System.out.println(e);
        }
    }

    static byte[] toGBK(String s) throws UnsupportedEncodingException {
        // Convert String to byte[] using specified encoding:
        return s.getBytes("GBK");
    }
}

It can be seen that as long as the Checked Exception declared by the method is not captured at the calling layer, it must also be captured at a higher calling layer. All uncaught exceptions must eventually be caught in the main() method, and try will not be missed. This is guaranteed by the compiler. The main() method is also the last opportunity to catch Exception .

If it is test code, the above writing method is a little troublesome. If you don’t want to write any try code, you can directly define main() method as throws Exception :

java
import java.io.UnsupportedEncodingException;
import java.util.Arrays;

public class Main {
    public static void main(String[] args) throws Exception {
        byte[] bs = toGBK("中文");
        System.out.println(Arrays.toString(bs));
    }

    static byte[] toGBK(String s) throws UnsupportedEncodingException {
        // Convert String to byte[] using specified encoding:
        return s.getBytes("GBK");
    }
}

Because the main() method declares that Exception may be thrown, it also declares that all Exception may be thrown, so there is no need to catch it internally. The price is that once an exception occurs, the program will exit immediately.

There are also some children's shoes that like to "digest" exceptions inside toGBK() :

java
static byte[] toGBK(String s) {
  try {
    return s.getBytes("GBK");
  } catch (UnsupportedEncodingException e) {
    // do nothing
  }
  return null;
}

This method of not processing after catching is very bad. Even if nothing can be done, the exception must be recorded first:

java
static byte[] toGBK(String s) {
  try {
    return s.getBytes("GBK");
  } catch (UnsupportedEncodingException e) {
    e.printStackTrace();
  }
  return null;
}

All exceptions can call printStackTrace() method to print the exception stack. This is a simple and useful method to quickly print exceptions.

Summary

Java uses exceptions to represent errors and catch exceptions through try ... catch ;

Java exceptions are class and inherit from Throwable ;

Error is a serious error that does not need to be caught, Exception is a handleable error that should be caught;

RuntimeException does not need to be forced to catch, non-RuntimeException (Checked Exception) needs to be forced to catch, or declared with throws ;

Catching exceptions without handling them is not recommended.

Exceptions in Java has loaded