Java Exception Basics

Java Exception Basics

The software developers knows more than everything that one of the things which is completely impossible to avoid are the errors. Unfortunately, since that computers are things that doesn't think by themselves (they are kind of donkey things that does everything that programers tells to do it, even if you tell them do autodestroy each other), we have to live with the risks of an unpredictable error. So according to that, you can't allow that an unpredictable error crashes a software execution. This would be something very bad, since that makes the software something unstable, and also very dangerous depending of its existence purpose. Let's imagine a software which was built to avoid the colision of 2 aircrafts in the air. For some reason, an unpredictable error occurs in runtime, and the software crashes.

Can you imagine tons of aircrafts becoming lost in the air, without any coordination info from the towers, only because the software tried, somehow, to execute a division by 0? This would be something insane, don't you think? To put tons of life beings at risk only because a damn wrong mathematical division procedure?

And what about a software responsible to communicate a cirurgical device from one country, to another robot device placed in another country, and during the cirurgical procedure, it crashes, giving a bug on the robot that makes it to start to do crazy movements, tearing apart the patient body? Something incredibly freak too, don't you think?

Well, as you can see, those unpredictable errors can promote the chaos if not handled. And yes, this is possible. It's possible to handle errors without letting the software to create chaos whatever it is working, and even the ones that is impossible to predict.

We can have two kinds of "Exceptions": the checked ones and the unchecked ones.

Checked Exceptions

The checked exceptions are the "exceptions" which the developer predicts that it can occur, in other words, they are verified on compiler timer, which means that when the compiler compiles the code, it gives an error if the exception aren't being handled. For instance, an algorithm that calculates the division between two numbers. The developer can verify if the remainder is 0, and if so, it throws an exception informing that is not possible to execute divisions by 0. If this verification isn't made, an exception would be thrown anyway, but sometimes these exceptions thrown by other sources can throw messages that doesn't explain exactly the reason that it was thrown, so like this example, it would be good to check it even so, and create a more specific message telling the reason that the exception were thrown. So considering that it has a possibility to have 0 as a remainder, this verification is made and the method signature advises the developer when he uses the method: "Hey broh! I can throw an exception here! So be aware with what you are doing, okay? What you want to do with that risk? Handle it here or leave this responsibility to someone else?". In this part, the developer is forced to choose those paths because as I said before, the compiler checks it during compile time, and the nice thing is, the developer is forced to be aware that the error can occurs when uses IDE such as Eclipse, because the IDE marks the statement asking for attention from the developer.

So, in Java, let's simulate this situation:

class Calculator {
   public double div(final double quotient, final double remainder) throws InvalidAttributeValueException {
       if (remainder == 0d) {
           throw new InvalidAttributeValueException("Hey fella! Are you nuts? It's impossible to divide any number by 0!");
       }
       return quotient/remainder;
   }
}

Then you ask me: "But what makes you choose this InvalidAttributeValueException exception type, instead of Exception, for instance?"

I will explain better about that in the course of this text, in the section Exceptions Hierarchy below, but for now let's accept the fact that I've chosen it because it is a subclass of a checked exception, and also its name explain very well the fact that an invalid attribute value was used for the method purpose, which is to return the division of 2 double values.

As you can see at the code example, you find a "throw new" statement inside the "if" block, and also a "throws" statement at the div method signature. If you remove this throws statement together with the exception subsequent class name, you will see that the compiler will force to:

  • OR remove the throw new InvalidAttributeValueException statement;
  • OR put back the throws InvalidAttributeValueException statement again, at the method signature;

And that's the magic of the thing. This behavior forces the developer to handle what to do when the exception is thrown, or to give this responsibility to who will be using the method. That's the reason that it is a checked exception, because it forces the handling of it, predicting that the error can occur for some reason.

So let's simulate the div method usage:

class DivMethodTesting {
   public static void main(final String[] args) {
       final Calculator calc = new Calculator();
       final double result = calc.div(4, 2);
       System.out.println("The result is: " + result);
   }
}

A focus on the "final double result = calc.div(4, 2);" part of the example above. When you use the "calc.div(4, 2)" method, the compiler will force two things:

  • OR surround the statement with a "try/catch" block:
class DivMethodTesting {
   public static void main(final String[] args) {
       final Calculator calc = new Calculator();
       try {
           final double result = calc.div(4, 2);
       } catch (final Exception e) {
           e.printStackTrace();
       }
       System.out.println("The result is: " + result);
   }
}
  • OR add a "throws" statement on the method signature:
class DivMethodTesting {
   public static void main(final String[] args) throws InvalidAttributeValueException {
       final Calculator calc = new Calculator();
       final double result = calc.div(4, 2);
       System.out.println("The result is: " + result);
   }
}

And again, that's the magic of a checked exception, it forces you to do something with it. The only thing on the second option is that when you put the throws on the main method, you don't get control of the exception thrown by the method, while the first option (using try/catch block), you get control of it and you avoid the application to crash when it is thrown, specifying what you want to do when the exception is thrown.

Unchecked Exception

The unchecked exceptions, are the ones that you can't predict, in other words, the compiler doesn't thrown an error if you don't handle the exception being thrown. Let's imagine the famous NullPointerException. It is an exception that you can't predict because it can happen for several reasons. It happens in bad programming design mostly, and even so it's an exception that doesn't make sense to handle it, since that it shows that a statement are being executed in a moment or the previous implementation has bad code implementation, letting a null object flow on the rest of the code, even because it is possible to handle this situation with null variables just checking if the corresponding variable is null or not.

Another unchecked exception to be used as an example is the IndexOutOfBoundsException. This exception is thrown when a statement tries to access an index that is minor than 0 or when it is greater than the array size minus 1 (array index is 0-based). This access is another example of bad code implementation, since that is not a "normal" situation. The algorithm must consider the risks and need to handle all the possibilities to avoid such error. So, as you can see, it doesn't make any sense to make this exception as a checked one, since that this situation shouldn't be something predictable, because it is a result of a bad code implementation, not a situation of the environment that the application could be subject, and can be predicted and handled.

Multicatch Block

In the Java 7, we had an addition on the try/catch block implementation. Let’s start with the basics. So, basically a try/catch block is like the template below:

try {
   // Code which can throw any exception
} catch (<ExceptionClass> e) {
   // Instructions of what to doif an exception is thrown
}

As you can see, we have a block which is where we implement the code that can throw an exception, and then the catch block which is responsible to handle it.

The multicatch block idea is inherent in the catch block obviously, the place where we defined as <ExceptionClass> placeholder on the template code above. Its function is to receive the exception class that the block must handle. Before Java 7, we had two alternatives to put on these catch statements:

  • Put a parent class to ensure that all of its subclasses could be handled, for instance, put a java.lang.Exception there, where all exceptions could be handled. This approach is used in most cases;
  • Put the specific class to ensure that only this exception should be handled. In this approach, is common to se more than one catch block on the "try/catch" just like the example below:
try {
   // Code block 1
} catch (Exception1 e) {
   // Code block 2
} catch (Exception2 e) {
   // Code block 3
} catch (Exception3 e) {
   // Code block 4
}

As you can see, in this situation with more than one catch block, you are able to control more than one exception that can be thrown by the same code block.

The multicatch works exactly on this second possibility. It’s related with dealing with more than one exception of the same code block, but as you can see, the code became a bit verbose, since that each catch must be in a different line. In a situation with lots of possibilities of exception throwing, you’ll have to handle with a giant list of catching blocks. So, after the Java 7, we got a new way to deal with that, called by multicatch.

Considering the example above, with the multicatch way, we can have (now I mean, now with Java 7 and beyond):

try {
   // Code block 1
} catch (Exception1 | Exception2 | Exception3 | Exception4 e) {
   // Code block 2
}

This is pure magic, isn’t? A cleaner code with less lines. You just have to concatenate all the exceptions that you want to handle, separating them with pipes “|”, and then putting the code inside with whatever you want to do to handle them.

It’s important to specify that the exceptions specified in this way, should not be parents, I mean, you can’t put a class and it’s subclasses for instance, whatever the hierarchy level that they have, which is completely clear since that if you specify an exception class there, the handling will be valid for all of its subclasses, so it doesn’t make any sense to list parents there, only the highest exception parents.

Exceptions Hierarchy

The following hierarchy structure is intended to show the path which defines the exceptions as a checked or unchecked ones. Basically, we can define as a checked exception, the one which is subclass of java.lang.IOException lineage, while the ones that are subclass of java.lang.RuntimeException, are the ones which is unchecked. Let’s take a look at the diagram below:

So, as you can see on the diagram above, to find a checked exception, you just have to find across the java.lang.IOException subclasses, because it is the one which forces the exception to be handled when implemented.

So, in resume:

  • It’s a RuntimeException subclass? It’s an unchecked one;
  • It’s an Exception subclass but not a subclass of RuntimeException? It’s a checked one;

Done!

To view or add a comment, sign in

More articles by Daniel Chiuratto Seabra

  • Algorithms and Data Structures - Vectors

    This article is a brief overview across these two domains, which has a huge importance in software development, where…

  • The Functional Programming Paradigm Arrives

    Since that I started to become interested on functional programming, specifically with Scala language, I started to do…

Others also viewed

Explore content categories