The first class, CheckedException, is a simple example of a checked exception. This exception extends the java.lang.Exception exception class directly (does not extend
RuntimeException
) and is thus a checked exception.CheckedException.java
package exceptions;
public class CheckedException extends Exception
{
public CheckedException(final String aMessage)
{
super(aMessage);
}
}
The second code listing is for UncheckedException. This class is nearly identical with our definition for class
CheckedException
above except for the fact that it, UncheckedException
extends java.lang.RuntimeException. This seemingly minor difference (extending RuntimeException
rather than extending Exception
directly) makes a significant difference! This amazing difference is even more remarkable when you consider that RuntimeException
actually extends Exception
.UncheckedException.java
package exceptions;
public class UncheckedException extends RuntimeException
{
public UncheckedException(final String aMessage)
{
super(aMessage);
}
}
The exception class that I named
UncheckedException
is an unchecked exception because it extends RuntimeException
. The exception class that I named CheckedException
is a checked exception because it does not in anyway extend RuntimeException
, but does extend Exception
.The next code listing provides a test that will demonstrate clearly the difference between how a checked exception and an unchecked exception are handled in Java.
ExceptionTest.java
package exceptions;
public class ExceptionTest
{
/**
* Calculate quotient by dividing provided dividend by provided divisor.
*
* @param aDividend Dividend for division.
* @param aDivisor Divsor in division.
* @return Quotient of division (dividend divided by divisor).
*/
public double calculateQuotient(final int aDividend, final int aDivisor)
{
if ( aDivisor == 0 )
{
throw new UncheckedException("Cannot have zero in the divisor.");
}
return aDividend / aDivisor;
}
/**
* Calculate quotient by dividing provided dividend by provided divisor.
*
* @param aDividend Dividend for division.
* @param aDivisor Divsor in division.
* @return Quotient of division (dividend divided by divisor).
*/
public double calculateQuotient(final double aDividend, final double aDivisor)
{
if ( aDivisor == 0 )
{
throw new CheckedException("Cannot have zero in the divisor.");
}
return aDividend / aDivisor;
}
public static void main(final String[] aArguments)
{
ExceptionTest me = new ExceptionTest();
final double quotientWithInts = me.calculateQuotient(10,2);
final double quotientWithDoubles = me.calculateQuotient(10.2,2.0);
System.out.println("Quotient with ints: " + quotientWithInts);
System.out.println("Quotient with doubles: " + quotientWithDoubles);
}
}
In the
ExceptionTest
code above, both the CheckedException
and the Unchecked
exception are used in the same manner in the test code. Both are thrown when a divisor passed into a division method is zero.When we try to compile the three classes shown above, we see the following compiler error (click on image to see larger version):
The compiler error reports: "exceptions\ExceptionTest.java:32: unreported exception exceptions.CheckedException; must be caught or declared to be thrown." The compiler error message goes on to show that the error occurred in the first character of the statement
throw new CheckedException("Cannot have zero in the divisor.");
.Someone not familiar with the difference between a Java checked exception and a Java unchecked exception might wonder why the compiler error occurred only for
CheckedException
and not UncheckedException
when they are both used identically in the ExceptionTest
code. The difference is exactly what is pointed out in the compiler error: checked exceptions must be either captured in a catch
block in the method in which they can be possibly thrown or there must be a throws
clause added to that method's definition.One way to address this compiler error is to have the method that throws the checked exception declare that it throws CheckedException as part of its signature. This is shown in the next code listing, which is mostly the same as the listing above for ExceptionTest, but with the changes made to make the code compile highlighted.
ExceptionTest.java with Checked Exception Throws Clause Added to Method that Might Throw It
package exceptions;
public class ExceptionTest
{
/**
* Calculate quotient by dividing provided dividend by provided divisor.
*
* @param aDividend Dividend for division.
* @param aDivisor Divsor in division.
* @return Quotient of division (dividend divided by divisor).
*/
public double calculateQuotient(final int aDividend, final int aDivisor)
{
if ( aDivisor == 0 )
{
throw new UncheckedException("Cannot have zero in the divisor.");
}
return aDividend / aDivisor;
}
/**
* Calculate quotient by dividing provided dividend by provided divisor.
*
* @param aDividend Dividend for division.
* @param aDivisor Divsor in division.
* @return Quotient of division (dividend divided by divisor).
*/
public double calculateQuotient(final double aDividend, final double aDivisor)
throws CheckedException
{
if ( aDivisor == 0 )
{
throw new CheckedException("Cannot have zero in the divisor.");
}
return aDividend / aDivisor;
}
public static void main(final String[] aArguments)
{
ExceptionTest me = new ExceptionTest();
final double quotientWithInts = me.calculateQuotient(10,2);
double quotientWithDoubles = 0;
try
{
quotientWithDoubles = me.calculateQuotient(10.2,2.0);
}
catch (CheckedException checkedException)
{
System.err.println("Checked Exception: " + checkedException.getMessage());
}
System.out.println("Quotient with ints: " + quotientWithInts);
System.out.println("Quotient with doubles: " + quotientWithDoubles);
}
}
The addition of the throws clause that indicates the method throws a checked exception makes the code compile, but also required that exception to be handled in the caller (the
main
function in this case). The output from running ExceptionTest
is shown next:Because zero was not passed in as a divisor to either method, neither
CheckedException
nor UncheckedException
were ever thrown. Note that even in the code that did not compiler earlier, zero was never passed in to the function capable of throwing a checked exception, but the need to capture or explicitly state that the checked exception was being thrown was still required by the compiler.Adding the throws clause to the method that is capable of throwing the checked exception is only one way to handle the situation. The other approach is to actually catch the checked exception in the method rather than throwing it to clients. This approach is shown in the next code example. This is similar to the two previous code listings for
ExceptionTest
, but has changes made for catching the error at its source highlighted.ExceptionTest.java with Checked Exception Caught And Handled
package exceptions;
public class ExceptionTest
{
/**
* Calculate quotient by dividing provided dividend by provided divisor.
*
* @param aDividend Dividend for division.
* @param aDivisor Divsor in division.
* @return Quotient of division (dividend divided by divisor).
*/
public double calculateQuotient(final int aDividend, final int aDivisor)
{
if ( aDivisor == 0 )
{
throw new UncheckedException("Cannot have zero in the divisor.");
}
return aDividend / aDivisor;
}
/**
* Calculate quotient by dividing provided dividend by provided divisor.
*
* @param aDividend Dividend for division.
* @param aDivisor Divsor in division.
* @return Quotient of division (dividend divided by divisor).
*/
public double calculateQuotient(final double aDividend, final double aDivisor)
{
try
{
if ( aDivisor == 0 )
{
throw new CheckedException("Cannot have zero in the divisor.");
}
}
catch (CheckedException checkedException)
{
System.err.println("ERROR: attempted to divide by zero!");
}
return aDividend / aDivisor;
}
public static void main(final String[] aArguments)
{
ExceptionTest me = new ExceptionTest();
final double quotientWithInts = me.calculateQuotient(10,2);
double quotientWithDoubles = me.calculateQuotient(10.2,2.0);
System.out.println("Quotient with ints: " + quotientWithInts);
System.out.println("Quotient with doubles: " + quotientWithDoubles);
}
}
With this approach, because the exception was handled nearer to its source, the client code (the
main
function in this case) did not need to handle the exception. When possible, I prefer this because the client does not then need to handle the error. Note that this example is particularly contrived because it is silly to throw the CheckedException
just to catch it again, but it does display how a checked exception works and the two choices Java developers have when dealing with checked exceptions.By the way, running the third example of
ExceptionTest
produces the same output as the second example of ExceptionTest
.Besides the links included above to other blogs and articles on checked versus unchecked exceptions, see also Best Practices for Exception Handling and Designing with Exceptions: When and How to Use Exceptions for additional details on exception handling in Java and the differences between checked and unchecked exceptions. I intentionally avoided the checked versus unchecked exception debate here and instead focused on what the difference is between the two.
No comments:
Post a Comment