11 Mistakes Java Developers make when Using Exceptions

If you use Exceptions in the wrong way, bugs will be very difficult to find. If you always use generic Exceptions, how can other developers know what error has occurred? You have to understand why we use Exceptions and avoid Exception mistakes with Java!

See the 11 mistakes Java Developers make when using Exceptions.

First, let’s see the hierarchy of the Exception classes.

Checked Exceptions / Unchecked Exceptions

1 – Using only the Exception class

It’s a common mistake that developers specifically catch the Exception class for any error. It’s much more difficult to identify the error if you see only an Exception being caught. The solution to this problem is to create specific Exceptions – but watch out, not too specific!

2 – Creating lots of specific Exceptions

Don’t create Exceptions for everything. Your application will be full of classes, useless repetition and you will create unnecessary work. Instead, create Exceptions for really important business requirements. For example, if you are developing a bank system one possible Exception would be when trying to withdraw money and the balance is zero: BalanceNotAvailableException. Another Exception would be transferring money to another person and the account does not exist, you can create: BankAccountNotFoundException and show an understandable Exception message to the user.

RuntimeException could be used when the bank’s server is out of service. Here you can use, for example: ServerNotAvailableException. The system must crash for this kind of error. There is no recovery.

3 – Creating a log for every catch

Logging every Exception catch will pollute your code. To prevent this, just log once and throw your Exception in the last catch. You won’t lose your Stacktrace if you wrap the Exception. If you are working with web applications, you should create a catch on your controller layer and log the error.

4 – Not knowing the difference between Checked and Unchecked Exceptions

When should Checked Exceptions be used? Use Checked when there is a recoverable error or an important business requirement.

The most common Checked Exception is the Exception class. Two related classes from Exception are FileNotFoundException and SQLException.You are obligated to handle or declare these exceptions. You must throw or catch the Exception or else it won’t compile.

When should Unchecked Exceptions be used? Use Unchecked when there is no recovery. For example, when the memory of the server is overused.

RuntimeExceptionis used for errors when your application can not recover. For example, NullPointerException and ArrayOutOfBoundsException. You can avoid a RuntimeException with an ‘if’ command. You should not handle or catch it.

There is also the class Error. It is an Unchecked Exception too. Never try to catch or handle this kind of Exception. They are errors from the JVM and are the most serious kind of Exception in Java. You must analyze the cause of Exceptions like this and change your code.

5 –  Silencing Exceptions

Never catch the Exception and do nothing, for example:

try {
    System.out.println("Never do that!");
} catch (AnyException exception) {
    // Do nothing
}

The catch will be useless. It’s impossible to know what happened and the Exception will be silenced. The developer will be obliged to debug the code and see what happened. If we create a good log, the time-consuming analysis won’t be necessary.

6 – Not following the principle “throw early, catch late”

If you have to handle Exception, for example, in your Service, you should do two things:

  1. Wrap your Exception
  2. Throw the Exception to the last catch and handle it.
7 – Not using clear messages on the Exceptions

Always use clear messages on your Exceptions. Doing this will help a lot when finding errors. Even better, create a Properties File with all Exception messages. You can use the file on your View layer and show users messages about the business requirements.

8- Not cleaning up after handling the Exception

After using resources like files and database connection, clean them and close them so that you won’t harm the system’s performance. You can use the finally block to do it.

9 – Not documenting Exceptions with javadoc

To avoid headaches, always Document why the Exception is being thrown in your method. Document your Exception and explain why you created it.

10 – Losing the Stack Trace (root cause) 

When wrapping an Exception in another one, don’t just throw the other Exception, keep the Stacktrace.

Bad code:

try {
    // Do the logic
} catch (BankAccountNotFoundException exception) {
    throw new BusinessException();
}

Good code:

try {
    // Do the logic
} catch (BankAccountNotFoundException exception) {
    throw new BusinessException(exception);
}
11 – Not organizing the hierarchy of specific Exceptions

If you don’t organize the hierarchy of your Exceptions, the relationship will be difficult between the parts of the system. You will have lots of problems.

You should use a hierarchy similar to this one:

Exception

BusinessException

AccountingException

HumanResourcesException
BillingCodeNotFoundException

  EmployeeNotFoundException

Now that you saw all the mistakes Java developers make when using Exceptions, I recommend you to try out to solve Java code challenges:

Error, RuntimeException, Exception Java Code Challenge
Exception Chaos Java Challenge

Keep practicing and improving your craft as a software engineer. Remember, never stop learning!

Written by
Rafael del Nero
Join the discussion

25 comments
  • Great post. You might want to add to point 8, that starting with Java 8, you can use the try-with construct to clean up resources.

  • Awesome post! You might also like to add this another common mistake:

    Concurrent Modification Exception

    This exception occurs when a collection is modified while iterating over it using methods other than those provided by the iterator object. For example, we have a list of hats and we want to remove all those that have ear flaps:

    List hats = new ArrayList();
    hats.add(new Ushanka()); // that one has ear flaps
    hats.add(new Fedora());
    hats.add(new Sombrero());
    for (IHat hat : hats) {
    if (hat.hasEarFlaps()) {
    hats.remove(hat);
    }
    }

    If we run this code, “ConcurrentModificationException” will be raised since the code modifies the collection while iterating it. The same exception may occur if one of the multiple threads working with the same list is trying to modify the collection while others iterate over it. Concurrent modification of collections in multiple threads is a natural thing, but should be treated with usual tools from the concurrent programming toolbox such as synchronization locks, special collections adopted for concurrent modification, etc. There are subtle differences to how this Java issue can be resolved in single threaded cases and multithreaded cases.

    Reference link: https://www.toptal.com/java/top-10-most-common-java-development-mistakes

    • Wow!!! Really awesome!! I forgot this error!! Thank you very much for this, Cesar! I will add this mistake, it’s really important because I already been through this situation.

    • List hats = new ArrayList();
      hats.add(new Ushanka()); // that one has ear flaps
      hats.add(new Fedora());
      hats.add(new Sombrero());
      for (IHat hat : hats) {
      if (hat.hasEarFlaps()) {
      hats.remove(hat);
      }
      }
      ——-
      when you run above code you will get “ConcurrentModificationException”. This means, you are removing element from the list which is already in processing. You will get this error when you try to modify any object while you are iterating. If you want to remove while iterating you have to store/copy that existingList into another new list and then you should perform delete/remove operation on newly created list. LIke this:

      List hats = new ArrayList();
      hats.add(new Ushanka()); // that one has ear flaps
      hats.add(new Fedora());
      hats.add(new Sombrero());

      List newList = new ArrayList(hats);

      for (IHat hat : hats) {
      if (hat.hasEarFlaps()) {
      newList.remove(hat);
      }
      }

  • Concerning mistake 10, title should be “Loose the root cause stacktrace”.
    If you say “never loose”, that’s not a mistake 🙂

  • “RuntimeException could be used when the bank’s server is out of service. Here you can use, for example: ServerNotAvailableException.”

    I think a checked exception would be fine, if a user may be able to try again or use another bank, compare with IOException which may happen when a server is not available.

    RuntimeException should be used for programming errors, like NullPointerException or ArrayIndexOutOfBoundsException. This typically happens when a developer has called a method with invalid data, IllegalArgumentException, or in the wrong order and the object is in an invalid state, IllegalStateException.

    • I disagree with you Erik. Up to me, all technical errors should be RuntimeException.
      And this is not only my mind : Spring, Hibernate, and Java SE itself (since few time) have this mind.

      You can look at TransactionException (Spring), HibernateException, WebServiceException (JAX-WS).
      You spoke about IOException, well, since java 8, there is UncheckedIOException.

      • Discussions! They are always welcome, it makes us think better about the concepts. We can see RuntimeExceptions in a simple way and just ask the question:

        Is the system supposed to recover itself after the Exception happens? If the answer is “no” the best option is to use RuntimeExeption.

        • I think it is. When we make the custom exception I always prefer to go with runtime exception. Its because if I forget to handle exception then jvm automatically handle the exception hence it will not affect to system but we in this case we will not able to identify what was the type of exception and location of the exception .

  • Languages like phyton, Ruby does not have support for the checked exception and proven to have robust production software.
    As per the clean code book by Robert C Martin, the debate between checked and unchecked exception is over.
    The Checked Exception is a violation of open close principle. If you throw checked exception from the method from your code then all the caller of the method either should declare or throw the exception.
    This means that change at low level will impact on many higher level.

    Checked Exception has benefits (Critical Library), but comes with the cost of dependency. The dependency cost outweighs the benefit of checked exception in general application.

  • You can avoid a RuntimeException with an ‘if’ command.
    – just curious how do you avoid ConcurrentModificationException with an ‘if’ command?

    • Hi Siva,thanks for the comment. You can use one of those solutions:

      List integers = newArrayList(1, 2, 3);
      List
      toRemove = newArrayList();

      for (Integer integer : integers) {
      if(integer == 2) {
      toRemove.add(integer);
      }
      }

      or

      List integers = newArrayList(1, 2, 3);

      integers.removeIf(i -> i == 2);

      assertThat(integers).containsExactly(1, 3);

      You can see more at the Baeldung article:
      https://www.baeldung.com/java-concurrentmodificationexception

  • Rafael, I’m in doubt about exceptions so I’m gonna give you a scenario to clarify my doubt.

    I’m developing an API using Java Spring Boot. There is an User module in my application. There are also three classes: UserController.java, UserService.java and UserRepository.java. I know that I must deal with business rules in Service layer. Currently I use to throw my business exceptions from my Service to my Controller without catching that.

    I have two questions:
    1. Do you think the way I throw business exceptions is right?
    2. About dealing with Unchecked exception I usually catch all RuntimeException through my exception handler’s class and I show a generic message to clients like “Internal server error, please get in touch with the development team”. Do you think it’s right?

    That’s an excellent post. I’ve learned a lot!

    • Hi Italo, I wouldn’t say that there is a right way to deal with Exceptions, it depends on the project necessities.

      Considering that you are showing the following message “Internal server error, please get in touch with the development team” to the user, it should be fine. The user doesn’t need to know what is happening. You can also show some other generic messages to the user as long as the user can understand, you can increment this a bit.

      But you must have a clear message in the project console, preferably with a specific Exception.

      There are some Spring implementations where you can manipulate Exceptions and also return the right HTTP code to the client with a clear Exception message.

      Our goal when dealing with Exceptions is to make developers’ life easier in case something wrong happens. Therefore, if you are throwing Exceptions with a good log message, that itself will be already awesome.

  • Great post Rafa!

    I have a few questions about topics 4 and 9.

    4. Error is really considered a RuntimeException? If we just look at Java Throwable´s hierarchy of classes, it seens to be a mistake saying that Errors are a kind of Exceptions.

    9. If we are developing following Clean Code and OOP principles, should we really document using Javadoc? I consider unit testing as a great documentation instead of Javadoc usage. What do you think about it?

    • Thanks, Gabriel! 🙂

      4. Very good catch, Error is not an Exception, it’s only an error! I will change this detail in the article!

      9. Good point, I would say it depends on the project and the patterns you agreed with your development team. If you agreed that you will document Exceptions in Unit Tests and everyone can easily understand it, then it should be good enough. But if not every developer is developing the tests in a way you can easily understand why the Exceptions are being thrown, then that would be a situation to use comments.

      Also, you can also realize that in the Java JDK code, all methods have comments including Exception comments. I know it’s not a business project but even still we can use the same principles.

      To conclude, I believe the most important action for a development team is to decide what way to go and stick to it. If you decided to document methods in the Unit Tests and everyone agrees, that’s a great solution! What can’t exist is inconsistency in the code, there must be a pattern to be followed, otherwise, it’s very difficult to maintain the code.