Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Java Exception Concept and How to Handle Them

DZone's Guide to

Java Exception Concept and How to Handle Them

Need help handling exceptions in your Java code?

· Java Zone ·
Free Resource

Download Microservices for Java Developers: A hands-on introduction to frameworks and containers. Brought to you in partnership with Red Hat.

We all know about errors, exceptions, and exception handling in Java, but still, there are few grey spots when it comes to the correct use of exceptions. Today, I am going to address some of the confusion.

Java exceptions consist of two types:

Checked Exception (Compile Time Exception)

These are exceptions that use the compiler to validate code during compilation and make sure there is no place in the code where these exceptions are left unattended.

Unchecked Exception (Runtime Exception)

These are the exceptions where the compiler does not validate at the time of compilation.

Why Do We Have Two Types of Exceptions?

Java is an OOPS-based language (although, it’s not 100 percent OOPS), so its core design is inspired by real-world entities and scenarios.

To better understand why Java exceptions are divided into two categories, we need to look at a real-world scenario. If a person commits a mistake, other people in the world see it as their fault and say he is responsible for the mistake, he has to handle the consequences. On the other hand, if a person is suffering (or there is, at least, a possibility that he may suffer) due to mistakes made by someone else, then other people in the world try to help or warn them to avoid any consequences or suffering.

The same goes for Java. These people represent the developer, and the world represents the Java compiler. The Java compiler only takes care of the exceptional scenarios, for example, the IOException, which can occur due to other components of the system, and there is no fault of the developer. In that scenario, the compiler helps the developer identify and handle exceptions. On the other hand, if an exceptional condition, for example, an ArrayIndexOutOfBoundsException occurs due to the fault of the developer, then the compiler does not warn the developer and, therefore, leaves him to face the consequences.

We can say, however, that the compiler should be humble and help the developer handle unchecked exceptions. But, the issue here is that case there will be many catches/throws that will reduce the program’s readability. This is the reason why Java encourages developers to identify and resolve unchecked exception scenarios.

When to Catch an Exception and When to Throw It?

Additionally, there is confusion over when to catch or throw an exception.

This question is important for identifying the culprit of the exception. If you are getting an IOException to fetch a file and the file name is one of the input parameters of your method, then, obviously, you need to tell the calling method that the file name it passed is not valid.

public void throwExceptionExample(String fileName) throws IOException {
    Resource resource = new ClassPathResource(fileName);
    String fileContent = IOUtils.toString(resource.getInputStream());
    System.out.println(fileContent);
  }


On the other hand, if getting filename  is part of your method’s logic, then you need to catch it and handle it gracefully. If you throw an IOException, in this case, then the developer of the calling method will not be able to do anything with that exception. In fact, they will probably laugh at you.

public void catchExceptionExample() {
    Resource resource = new ClassPathResource("fileName");
    String fileContent = null;
    try {
      fileContent = IOUtils.toString(resource.getInputStream());
    } catch (IOException e) {
      e.printStackTrace(); //in real world, we should use loggers, and discourage printStackTrace
    }
    System.out.println(fileContent);
  }


Are calling and called Methods in the Same Tier or Not?

Well, when calling and called methods are in a different tier, for example, calling is a UI tier and called is backend, in that case, we do not encourage throwing exceptions because the meaning of the exception may not be useful in that case. In such scenarios, we should try to gracefully handle the exception in a called method, and if not possible, then before throwing back to the called method, make sure the exception you are throwing is making sense (use the Custom exception if required) for the calling method.

When to Throw or Catch Unchecked Exceptions

For many of you, the above question does not make sense because we have learned that unchecked exceptions should always be handled by adding conditions or validations in code and we should never catch or throw them. Though such scenarios occur less frequently, you may face these situations, regardless of when you should throw unchecked exceptions using ‘throws’ or catch them. 

If you are getting a NullPointerException because one of the values in the input parameter object is null and, in this case, you avoid this exception using validation or handle it by catching it, then you may end up implementing the wrong logic or saving the wrong state of object, so we should throw the exception back to the calling method. The calling method should take proper action and provide the correct object.

public void throwUncheckedExceptionExample(String stringToPrint) throws NullPointerException{
    if(stringToPrint != null) {
      System.out.println(stringToPrint.toString());
    }
    else{
      throw new NullPointerException("input parameter: "+stringToPrint+" is null");
    }
  }


Additionally, let’s say, there can be two types of objects in the input of your method. You want to process only one type and leave another type because it's not processable. To identify the type of object, you need to complete a lot of parsing, object conversions, etc. In place of writing heavy code for validation and investing so much memory, we can process all objects in the same way. We can throw a runtime exception for the object that could not be processed, and in the catch block, we can log the message. This approach will be more concise and consume less memory.

Summary (Rules of Exception Handing):

  • Throw exceptions using throws if they are caused due to data coming from the calling method, and the calling method should take action to avoid this exception.
  • Catch exceptions if they are caused by the logic of your method or some other reason, like the SQL server is down.
  • We should try to avoid throwing and catching unchecked exceptions, but there are scenarios where it is preferred to throw and catch an unchecked exception. In some scenarios, this involves avoiding them using validations (as explained above).
  • Don’t log and throw an exception. Log the exception only where you are finally handling it. Multiple log writes for one exception are confusing and also have an impact on performance.
  • Create custom exceptions where the original exception does not make sense for the calling method.

Thanks for reading!

Download Building Reactive Microservices in Java: Asynchronous and Event-Based Application Design. Brought to you in partnership with Red Hat

Topics:
java ,exception handling ,checked exceptions ,tutorial ,called ,calling ,exceptions ,unchecked

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}