Introduction to JVM Languages
上QQ阅读APP看书,第一时间看更新

Exceptions and errors (java.lang.Exception and java.lang.Error)

Every JVM developer should know how runtime errors are managed on a JVM. Since all languages offer their own mechanisms to handle runtime errors, we will not cover how to handle errors here. We will describe what happens when a runtime error occurs.

When a runtime error occurs inside a method, an exception or Error object is created and thrown. In the Java language, this is done using the throw keyword. Let's look at an example when a class throws a generic Exception class:

    throw new Exception("Oops!");

Java comes with a lot of built-in classes that inherit either Exception or Error classes. When you consider creating a new Exception subclass, it makes sense to first look for an exception that can be reused. For example, if your method cannot except null references, it should throw a java.lang.NullPointerException object instance when null is passed; all good Java APIs do this when null references are provided to a method that cannot support them.

If you study the class diagram at the beginning of this section carefully, you'll see that both the classes inherit the Throwable class. A Throwable object can be thrown, but usually, their subclasses are used as they are more convenient to use.

The distinction between the Exception and Error class is as follows:

  • Exceptions are thrown by classes when there's a good chance that the program can handle the error and continue running.
  • Errors are thrown when a problem is detected that cannot be really anticipated. Many errors are thrown by the JVM itself.

When an exception or error object is thrown (an exception is assumed from now on), JVMs look at the method that threw the exception. If it contains an error handler that can handle the error, control is transferred to that error handler. If the method does not handle any errors, or not this particular one, then the caller of that method is checked for an error handler. This continues until a method is found that could handle the error without throwing a new one or the first method call is reached. In the latter case, the JVM instance will crash and produce a stack trace such as the following:

    Exception in thread "main" java.lang.Exception: Oops
at ExceptionDemo.method3(ExceptionDemo.java:37)
at ExceptionDemo.method2(ExceptionDemo.java:33)
at ExceptionDemo.method1(ExceptionDemo.java:29)
at ExceptionDemo.main(ExceptionDemo.java:25)

Many languages compile their source code filenames and line numbers with the generated Java bytecode. This has the advantage that source code line numbers can be returned to the stack trace. This results in readable stack traces.

Java has strict rules for throwing exceptions; not every class can throw all exceptions. Many other JVM languages are much more relaxed in this regard. Java's rules and the important role of the java.lang.RuntimeException class in the Java language will be explained in the next chapter.