DEV Community

Jules Roadknight
Jules Roadknight

Posted on • Updated on

Error handling in Java

Compiled languages are very needy, but at least you know before you deploy the code that you've accounted for most edge cases. However, this definitely comes under 'don't just do what the IDE suggests', which has caused me problems before now, so let's establish some appropriate error handling.

What's to be done

Certain methods are more likely to fail than others, so to compile you have to use error-handling; in this case either try/catch or throws will get the job done.

Example 1

public class Database {
    Connection connection;
    public Database(String databaseUrl) {
        Class.forName(org.postgresql.Driver);
        connection = DriverManager.getConnection(databaseUrl, "postgresUser", "postgresPassword");
    }
}

Attempting to connect to a Postgresql database above will cause two compiler problems:

  • forName() Unhandled exception: java.lang.ClassNotFoundException
  • getConnection() Unhandled exception: java.sql.SQLException

These can be solved together or separately with the solutions I mentioned above.

Try/catch

public class Database {
    Connection connection;
    public Database(String databaseUrl) {
        try {
            Class.forName(org.postgresql.Driver);
            connection = DriverManager.getConnection(databaseUrl, "postgresUser", "postgresPassword");
        } catch(ClassNotFoundException | SQLException e) { // or just (Exception e)
            e.printStackTrace();
        }
    }
}

Throws

public class Database throws ClassNotFoundException, SQLException { // or, again, just Exception
    Connection connection;
    public Database(String databaseUrl) {
        Class.forName(org.postgresql.Driver);
        connection = DriverManager.getConnection(databaseUrl, "postgresUser", "postgresPassword");
    }
}

But which! When?

Let's quickly go over why this happens. The reason we need to handle anything is that there is a chance that one of the methods we're using will call throws. The forName() method comes with throws built in, so we either have to say that the method in which we use it (in this case, Database) could throw an error, or catch it.

If we choose throws, we're allowing the error to move on, and unless it is handled by another method - like a method that calls Database - it will cause the program to terminate with an error message.

However, when we use try/catch we're saying:

  • Yes, this code is risky but try it anyway
  • If it throws an error we'll catch it and do this instead. Usually 'do this instead' is an error message for the user.

And on top of try/catch is finally:

  • Whatever happens, we will do this afterwards

So...

To condense this, throw passes responsibility up to the next method to be handled (or ends the program if not handled eventually), and catch handles exceptions in a meaningful way now.

When to let it throw

Tests are a good time. In JUnit, you can assert that a method throws an error in whatever case, and if you didn't plan for it to throw, then your tests won't pass and you'll find out quickly.

Checked vs. Unchecked Exceptions

So far, everything we've covered has been about checked exceptions, which are exceptions found by the compiler before the program is run. There are also unchecked exceptions or 'Runtime Exceptions', which are only found when the program is running.

Checked Exception Examples

class Iterate {
    public static void main(String args[]) {
        String arr[] = {"Primo", "Two", "Third"}
        System.out.println(arr[10]);
    }
}

The above will compile, but then throw an IndexOutOfBoundsException as we're trying to access the tenth element of an array with only three items.

Similarly, if you call an instance that hasn't been instantiated and get a NullPointerException, that would be an unchecked exception. These exceptions should still be handled, you just won't have them pointed out by the compiler.

Summary

Checked exceptions are picked up by the compiler and must be handled. They tend to involve interactions outside of the program (reading files, database queries, input).

Unchecked exceptions happen during runtime and should be handled, but won't prevent compilation. They are mostly problems within the program (null, type incompatibility, out-of bounds, dividing by zero).

Discussion (0)