IntelliJ IDEA
IntelliJ IDEA – the Leading Java and Kotlin IDE, by JetBrains
Easy Hacks: How to Throw Java Exceptions
Exceptions in Java are used to indicate that an event occurred during the execution of a program and disrupted the normal flow of instructions. When an exception occurs, the Java runtime automatically stops the execution of the current method. It passes an exception object with information about the error to the nearest catch
block that can handle the exception.
While it’s important to catch and handle exceptions gracefully, it’s equally important to know how to throw them effectively. In this blog post, we’ll explore the ins and outs of throwing Java exceptions, including the different types of exceptions, how to create custom exceptions, and more.
How to throw exceptions
To let the Java runtime know an exception has occurred in your code, you have to throw one. In Java, you can use the throw
keyword to invoke the exception machinery in the Java Virtual Machine (JVM):
throw new Exception("Something went wrong!");
When throwing an exception, you’re creating a new exception object. This object contains information about the event that occurred. This information is reflected by the exception type and several other properties, such as the exception message, which can describe what happened in more detail. The runtime will also enrich the exception object with information about where the exception was thrown, this is done in the form of a stack trace.
To illustrate this, consider the following program:
public void main() throws Exception { runFirstMethod(); } public void runFirstMethod() throws Exception { runSecondMethod(); } public void runSecondMethod() throws Exception { throw new Exception("Something went wrong!"); }
The main
method invokes runFirstMethod
, which in turn invokes runSecondMethod
. A new exception is thrown, with the message "Something went wrong!"
. When you run this program, you’ll see the message printed in the output, and a stack trace of where the exception occurred is printed to the console:
When an unhandled exception is thrown, the application will stop executing and return a non-zero exit code. To handle the exception, you can surround it with a try
/catch
block anywhere in the call stack. For example, you can “catch” the exception and log the error to the console in the main()
method, or in runFirstMethod()
:
public void main() throws Exception { try { runFirstMethod(); } catch (Exception ex) { // can catch here... System.out.println("An error occurred: " + ex.getMessage()); } } public void runFirstMethod() throws Exception { try { runSecondMethod(); } catch (Exception ex) { // ...or here System.out.println("An error occurred: " + ex.getMessage()); } } public void runSecondMethod() throws Exception { throw new Exception("Something went wrong!"); }
We’ll look into catching exceptions in more detail in a future post. For now, keep in mind that it is possible to catch and gracefully handle exceptions.
Which classes can be used to represent an exception? In Java, only classes that inherit java.lang.Throwable
can be thrown. If you look at the class hierarchy of Throwable
, you’ll find two subtypes: java.lang.Error
, and java.lang.Exception
.
The Error
class is used by the JVM and represents serious problems that applications usually should not try to catch, such as an OutOfMemoryError
that may occur when the JVM is unable to allocate an object. There’s no recovering from that situation!
The Exception
class is the typical ancestor for exceptions that can be recovered from using a try
/catch
block. For example, when an IOException
is thrown because a file could not be found, your code can show a message to the user of your application or ask to specify the file path again. There’s no need for such an exception to end the execution of your application.
You’ll find that most exceptions in the Java platform are subclasses of the Exception
class and indicate various types of exceptions that can occur. IOException
signals that an I/O operation was interrupted or has failed. FileNotFoundException
is a subclass of IOException
that will be thrown when a file with a specific pathname does not exist. Custom exception types in your application will also typically inherit from the Exception
class.
Some exceptions have a different superclass and inherit from RuntimeException
instead. Implementations of RuntimeException
are typically used to signal incorrect use of an API. IndexOutOfBoundsException
is such an exception type, which may be thrown when you try to access an index in an array or string that is out of range. Another example is NullPointerException
, which may be thrown when a variable that is not pointing to any object (and refers to nothing, or null
) is accessed.
Checked vs. unchecked exceptions
In Java, there are two types of exceptions: checked and unchecked. These are closely related to which superclass is used by any particular exception.
Unchecked exceptions are exceptions that are not checked at compile time. Exceptions that inherit the Error
or RuntimeException
classes are unchecked exceptions, meaning that even if the compiler can determine that such exceptions can occur, your code will still compile.
Checked exceptions are exceptions that are checked at compile time. If a method contains code that throws an exception of type Throwable
, Exception
, or inheritors of Exception
, then the method must either handle the exception itself or signal that the exception has to be handled by a calling method. If an exception is unhandled, your code will not compile.
Let’s take another look at the example code: you’ll see that all method signatures have the suffix throws Exception
:
public void main() throws Exception { runFirstMethod(); } public void runFirstMethod() throws Exception { runSecondMethod(); } public void runSecondMethod() throws Exception { throw new Exception("Something went wrong!"); }
The throws Exception
syntax informs the Java compiler that any calling code will have to handle exceptions of the type Exception
. In this example, the application’s main()
method doesn’t handle the exception, which means the application doesn’t handle the potential exception from this call stack and may error out.
If you were to throw a RuntimeException
, the compiler would not require you to list that exception using the throws
keyword. The exception will be unchecked.
You can see how checked exceptions are visualized in the editor when you remove one of the throws Exception
suffixes. Almost immediately, IntelliJ IDEA will display an error message saying there’s an “Unhandled exception” and that your code will not compile.
When you bring up the context menu (press ⌥⏎ on macOS or Alt+Enter on Windows/Linux), the IDE will suggest either adding the exception type to the method signature or wrapping the call to runSecondMethod()
in a try
/catch
block.
Note that this is also true for exceptions that may occur in third-party code, such as java.nio
. In the following screenshot, you’ll see the readString
method may throw an IOException
, and IntelliJ IDEA again suggests either adding the exception to the method signature (throws IOException
) or handling the exception using a try
/catch
block.
Checked exceptions and the throws
syntax are great ways to annotate your code’s programming interface. By making information about potential exceptions available, any caller can decide what to do with the exception: ignore it by adding a throws
to the method signature or catch
the exception and handle it.
Creating custom Java exception classes
The Java platform comes with a large number of exception classes you can use to signal when an error occurs in your code. However, if you need a specific exception type that isn’t already available, you may need to implement your own.
You can write custom exception classes that inherit from java.lang.Exception
so they are checked by the compiler, or java.lang.RuntimeException
if you want your custom exception to be unchecked. Most often, though, custom exception classes will be checked and will inherit java.lang.Exception
. It’s also common for exception class names to end in “Exception”.
Let’s create a custom exception that represents a banking overdraft. For example, when a user attempts to withdraw more money from their account than the available balance, this exception will be thrown. In its simplest form, the OverdraftException
could look like this:
public class OverdraftException extends Exception { public OverdraftException(String message) { super(message); } }
Throwing the OverdraftException
would be fairly straightforward: all this exception needs is a message
to describe what went wrong:
throw new OverdraftException("Attempt to withdraw $100 with balance of $50.");
When implementing custom exception classes, you may want to provide calling code with some additional information. For example, OverdraftException
could make the available balance and attempted withdrawal amount accessible through their corresponding properties:
public class OverdraftException extends Exception { private final BigDecimal amount; private final BigDecimal balance; public OverdraftException(BigDecimal amount, BigDecimal balance, String message) { super(message); this.amount = amount; this.balance = balance; } public BigDecimal getAmount() { return withdrawalAmount; } public BigDecimal getBalance() { return balance; } }
When throwing the exception, the amount and balance have to be passed into the OverdraftException
constructor. Calling code can now use the exception message, or use the extra information when handling the exception, for example, to prompt the user for a lower withdrawal amount:
try { throw new OverdraftException( BigDecimal.valueOf(100), BigDecimal.valueOf(5), "Attempt to withdraw $100 with balance of $50."); } catch (OverdraftException ex) { System.out.println(ex.getMessage()); System.out.println("Would you like to withdraw another amount?"); System.out.println("You can withdraw up to $" + ex.accountBalance); // ... }
Conclusion
In this post, we’ve seen that exceptions in Java are used to indicate errors in code. When an exception occurs, the Java runtime stops the execution of the current method and passes the exception object to the nearest catch
block that can handle it. To throw an exception, you can use the throw
keyword followed by the exception object.
There are two types of exceptions in Java: checked and unchecked. Checked exceptions are checked at compile time and must be handled or declared to be thrown. Unchecked exceptions, on the other hand, are not checked at compile time.
Custom exception classes can be created by inheriting from the Exception
class. These custom exceptions can provide additional information about the error. By communicating information about exceptions, the code’s programming interface becomes clearer, and callers can decide how to handle the exceptions.
Give it a try today with IntelliJ IDEA!