7.5. Common exception classes and categories

[8.5] “Recognize common exception classes (such as NullPointerException, ArithmeticException, ArrayIndexOutOfBoundsException, ClassCastException)”

In this section, we’ll take a look at common exception classes and categories of exceptions. You’ll also learn about the scenarios in which these exceptions are thrown and how to handle them.

For this exam, you should be familiar with the scenarios that lead to these commonly thrown exception classes and categories and how to handle them. Table 7.2 lists common errors and exceptions. Although the exam specifically lists four runtime exceptions, you might see the other common exception and error classes on the exam.

Table 7.2. Common errors and exceptions

Runtime exceptions

Errors

ArrayIndexOutOfBoundsException ExceptionInInitializerError
IndexOutOfBoundsException StackOverflowError
ClassCastException NoClassDefFoundError
IllegalArgumentException OutOfMemoryError
ArithmeticException  
NullPointerException  
NumberFormatException  

The OCA Java SE 8 Programmer I exam objectives require that you understand which of the previously mentioned errors and exceptions are thrown by the JVM and which should be thrown programmatically. From the discussion of errors earlier in this chapter, you know that errors represent issues associated with the JRE, such as OutOfMemoryError. As a programmer, you shouldn’t throw or catch these errors—leave them for the JVM. The definition of runtime exceptions notes that these are the kinds of exceptions that are thrown by the JVM, which shouldn’t be thrown by you programmatically.

Let’s review each of these in detail.

7.5.1. ArrayIndexOutOfBoundsException and IndexOutOfBoundsException

As shown in figure 7.17, ArrayIndexOutOfBoundsException and IndexOutOfBounds-Exception are runtime exceptions, which share an IS-A relationship. IndexOutOfBoundsException is subclassed by ArrayIndexOutOfBoundsException.

Figure 7.17. Class hierarchy of ArrayIndexOutOfBoundsException

An ArrayIndexOutOfBoundsException is thrown when a piece of code tries to access an array out of its bounds (either an array is accessed at a position less than 0 or at a position greater than or equal to its length). An IndexOutOfBoundsException is thrown when a piece of code tries to access a list, like an ArrayList, using an illegal index.

Assume that an array and list have been defined as follows:

String[] season = {"Spring", "Summer"};
ArrayList<String> exams = new ArrayList<>();
exams.add("SCJP");
exams.add("SCWCD");

The following lines of code will throw an ArrayIndexOutOfBoundsException:

The following lines of code will throw an IndexOutOfBoundsException:

Why do you think the JVM has taken the responsibility on itself to throw this exception? One of the main reasons is that this exception isn’t known until runtime and depends on the array or list position that’s being accessed by a piece of code. Most often, a variable is used to specify this array or list position, and its value may not be known until runtime.

Note

When you try to access an invalid array position, ArrayIndexOutOfBoundsException is thrown. When you try to access an invalid ArrayList position, IndexOutOfBoundsException is thrown.

You can avoid these exceptions from being thrown if you check whether the index position you’re trying to access is greater than or equal to 0 and less than the size of your array or ArrayList.

7.5.2. ClassCastException

Before I start discussing the example I’ll use for this exception, take a quick look at figure 7.18 to review the class hierarchy of this exception.

Figure 7.18. Class hierarchy of ClassCastException

Examine the code in the next listing, where the line of code that throws the ClassCastException is shown in bold.

Listing 7.2. An example of code that throws ClassCastException

A ClassCastException is thrown when an object fails an IS-A test with the class type to which it’s being cast. In the preceding example, class Ink is the base class for classes ColorInk and BlackInk. The JVM throws a ClassCastException in the previous case because the code in bold tries to explicitly cast an object of ColorInk to BlackInk.

Note that this line of code avoided the compilation error because the variable inks defines an ArrayList of type Ink, which can store objects of type Ink and all its subclasses. The code then correctly adds the permitted objects: one each of BlackInk and ColorInk. If the code had defined an ArrayList of type BlackInk or ColorInk, the code would have failed the compilation, as follows:

Here’s the compilation error thrown by the previously modified piece of code:

Invalid.java:6: inconvertible types
found   : ColorInk
required: BlackInk
        Ink ink = (BlackInk)inks.get(0);
                                    ^

You can use the instanceof operator to verify whether an object can be cast to another class before casting it. Assuming that the definition of classes Ink, ColorInk, and BlackInk are the same as defined in the previous example, the following lines of code will avoid the ClassCastException:

In the previous example, the condition (inks.get(0)instanceofBlackInk) evaluates to false, so the then part of the if statement doesn’t execute.

In the following Twist in the Tale exercise, I’ll introduce an interface used in the casting example in listing 7.2 (answer in the appendix).

Twist in the Tale 7.4

Let’s introduce an interface used in listing 7.2 and see how it behaves. Following is the modified code. Examine the code and select the correct options:

class Ink{}
interface Printable {}
class ColorInk extends Ink implements Printable {}
class BlackInk extends Ink{}

class TwistInTaleCasting {
    public static void main(String args[]) {
        Printable printable = null;
        BlackInk blackInk = new BlackInk();
        printable = (Printable)blackInk;
    }
}

  1. printable = (Printable)blackInk will throw compilation error
  2. printable = (Printable)blackInk will throw runtime exception
  3. printable = (Printable)blackInk will throw checked exception
  4. The following line of code will fail to compile:
    printable = blackInk;

7.5.3. IllegalArgumentException

As the name of this exception suggests, IllegalArgumentException is thrown to specify that a method has passed illegal or inappropriate arguments. Its class hierarchy is shown in figure 7.19.

Figure 7.19. Class hierarchy of IllegalArgumentException

Even though it’s a runtime exception, programmers usually use this exception to validate the arguments that are passed to a method. The exception constructor is passed a descriptive message, specifying the exception details. Examine the following code:

public void login(String username, String pwd, int maxLoginAttempt) {
    if (username == null || username.length() < 6)
        throw new IllegalArgumentException
                   ("Login:username can't be shorter than 6 chars");
    if (pwd == null || pwd.length() < 8)
        throw new IllegalArgumentException
                   ("Login: pwd cannot be shorter than 8 chars");
    if (maxLoginAttempt < 0)
        throw new IllegalArgumentException
                   ("Login: Invalid loginattempt val");

    //.. rest of the method code
}

The previous method validates the various method parameters passed to it and throws an appropriate IllegalArgumentException if they don’t meet the requirements of the method. Each object of the IllegalArgumentException is passed a different String message that briefly describes it.

7.5.4. NullPointerException

The NullPointerException, shown in figure 7.20, is the quintessential exception.

Figure 7.20. Class hierarchy of NullPointerException

I imagine that almost all Java programmers have had a taste of this exception, but let’s look at an explanation for it.

This exception is thrown by the JVM if you try to access a non-static method or a variable through a null value. The exam can have interesting code combinations to test you on whether a particular piece of code will throw a NullPointerException. The key is to ensure that the reference variable has been assigned a non-null value. In particular, I’ll address the following cases:

  • Accessing members of a reference variable that is explicitly assigned a null value
  • Using an uninitialized local variable, which may seem to throw a NullPointerException
  • Attempting to access nonexistent array positions
  • Using members of an array element that are assigned a null value

Let’s get started with the first case, in which a variable is explicitly assigned a null value:

The preceding code tries to access the method add on the variable list, which has been assigned a null value. It throws an exception, as follows:

Exception in thread "main" java.lang.NullPointerException
    at ThrowNullPointerException.main(ThrowNullPointerException.java:5)

By default, the static and instance variables of a class are assigned a null value. In the previous example, the static variable list is assigned an explicit null value. To help you clarify the code and avoid any possible doubt, list is assigned an explicit null value. When the method main tries to execute the method add on the variable list, it calls a method on a null value. This call causes the JVM to throw a Null-Pointer-Exception (which is a RuntimeException). If you define the variable list as an instance variable and don’t assign an explicit value to it, you’ll get the same result (NullPointerException being thrown at runtime). Because the static method main can’t access the instance variable list, you’ll need to create an object of the class ThrowNullPointerException to access it:

You can prevent a NullPointerException from being thrown by checking whether an object is null before trying to access its member:

What happens if you modify the previous code as follows? Will it still throw a NullPointerException?

Interestingly, the previous code fails to compile. list is defined as a local variable inside the method main, and by default local variables aren’t assigned a value—not even a null value. If you attempt to use an uninitialized local variable, your code will fail to compile. Watch out for similar questions in the exam.

Another set of conditions when code may throw the NullPointerException involves the use of arrays:

In the preceding code, the static variable oldLaptops is assigned a null value by default. Its array elements are neither initialized nor assigned a value. The code that tries to access the array’s second element throws a NullPointerException.

In the following code, two array elements of the variable newLaptops are initialized and assigned a default value of null. If you call the method toString on the second element of the variable newLaptops, it results in a NullPointerException being thrown:

If you modify the code at as follows, it won’t throw an exception—it’ll print the value null. This is because the object-based System.out.println() overload calls the object-based String.valueOf() overload, which itself checks whether the object to “print” is null, in which case it will output null without calling any toString() method:

Exam Tip

In the exam, watch out for code that tries to use an uninitialized local variable. Because such variables aren’t initialized with even a null value, you can’t print their value using the System.out.println method. Such code won’t compile.

Let’s modify the previous code that uses the variable oldLaptops and check your understanding of NullPointerExceptions. Here’s another Twist in the Tale hands-on exercise for you (answers in the appendix).

Twist in the Tale 7.5

Let’s check your understanding of the NullPointerException. Here’s a code snippet. Examine the code and select the correct answers.

class TwistInTaleNullPointerException {
    public static void main(String[] args) {
        String[][] oldLaptops =

             { {"Dell", "Toshiba", "Vaio"}, null,
{"IBM"}, new String[10] };
        System.out.println(oldLaptops[0][0]);          // line 1
        System.out.println(oldLaptops[1]);             // line 2
        System.out.println(oldLaptops[3][6]);          // line 3
        System.out.println(oldLaptops[3][0].length()); // line 4
        System.out.println(oldLaptops);                // line 5
    }
}

  1. Code on line 1 will throw NullPointerException
  2. Code on lines 1 and 3 will throw NullPointerException
  3. Only code on line 4 will throw NullPointerException
  4. Code on lines 3 and 5 will throw NullPointerException

7.5.5. ArithmeticException

When the JVM encounters an exceptional mathematical condition, like dividing an integer by zero, it throws ArithmeticException (the class hierarchy shown in figure 7.21). Note that division by 0 is not the same as division by 0.0. In this section, we’ll cover the results of division of integers and decimals by 0 and 0.0.

Figure 7.21. Class hierarchy of ArithmeticException

The following summarizes the cause of an ArithmeticException:

  • A division will be performed as an integer division as long as only integers are involved. As soon as there’s a floating-point number, then everything is computed in floating-point arithmetic (true for all arithmetic operations, by the way).
  • An integer division by zero throws an ArithmeticException.
  • A floating-point division by zero won’t throw any exception but rather will return ±Infinity or NaN, depending on the first operand.
Division of an integer value by 0

Although it might seem simple to spot an occurrence of this exception, assumptions can be wrong. Let’s start with a simple and explicit example (which is easy to spot):

On execution, the previous code will throw an ArithmeticException with a similar message:

Exception in thread "main" java.lang.ArithmeticException: / by zero
    at ThrowArithmeticEx.main(ThrowArithmeticEx.java:3)

Here’s an example of comparatively complex code that you might see on the exam. Do you think it will throw an ArithmeticException? Also, do you think that the answer seems obvious like in the preceding code?

class ThrowArithmeticEx {
    public static void main(String args[]) {
        int a = 10;
        int y = a++;
        int z = y--;

        int x1 = a - 2*y - z;
        int x2 = a - 11;
        int x = x1/ x2;

        System.out.println(x);
    }
}

The preceding code throws ArithmeticException for the operation x1/x2 because the value of x2 is 0. With the initialization of the variable y, the value of variable a is incremented by 1, from 10 to 11 (due to the post-fix increment operator). The variable x2 is initialized with a value that’s equal to 11 and less than a, which is 0.

Exam Tip

In the exam, watch out for division with integers. If the divisor is 0, the integer value that’s being divided doesn’t matter. Such an operation will throw an ArithmeticException.

What do you think would be the answer if you divide 0 by 0? What do you think is the output of the following code: 1, 0, or ArithmeticException?

class ThrowArithmeticEx {
    public static void main(String args[]) {
        int x = (int)(7.3/10.6);
        int y = (int)(100.76/123.87);

        int z = x/y;

        System.out.println(x);
    }
}

Division of an integer number by 0 will result in an ArithmeticException. So the preceding code will also throw an ArithmeticException.

Exam Tip

Division of a negative or positive integer value by 0 will result in an ArithmeticException.

Here’s an explicit example of dividing 0 by 0:

Exam Tip

Division of 0 by 0 results in an ArithmeticException.

Division of a decimal value by 0

If you divide a positive decimal number by 0, the answer is Infinity:

If you divide a negative decimal number by 0, the answer is -Infinity:

Exam Tip

If you divide a positive decimal value by 0, the result is Infinity. If you divide a negative decimal value by 0, the result is -Infinity.

Here’s an interesting question: what do you think is the result of division of 0.0 by 0? Here’s a quick code snippet:

Exam Tip

Division of 0.0 by 0 results in NaN (Not a Number).

Any mathematical operation with a NaN results in NaN.

Division of integers or decimals by 0.0

Dividing by 0 and dividing by 0.0 don’t give you the same results. Let’s revisit the previous examples, starting with the modified version of the first example in this section:

The preceding code doesn’t throw an ArithmeticException. It outputs Infinity.

Exam Tip

When a positive integer or decimal value is divided by 0.0, the result is Infinity.

Here’s another modified example:

class DivideByZeroPointZero {
    public static void main(String args[]) {
        int a = 10;
        int y = a++;
        int z = y--;

        int x1 = a - 2*y - z;
        int x2 = a - 11;
        double x3 = x2;

        double x = x1/ x3;

        System.out.println(x);
        System.out.println(x1);
        System.out.println(x3);
    }
}

Here’s the output of the preceding code:

-Infinity
-17
0.0

The preceding code doesn’t throw an ArithmeticException. The variable x1 is assigned a negative integer value, that is, -17. The variable x2 is assigned the value 0. When the variable x3 of type double is initialized with the value of x2, it’s promoted to a double value, assigning 0.0 to x3. When a negative integer value is divided by 0.0, the result is –Infinity.

Exam Tip

When a negative integer or decimal value is divided by 0.0, the result is –Infinity.

7.5.6. NumberFormatException

What happens if you try to convert “87” and “9m#” to numeric values? The former value is OK, but you can’t convert the latter value to a numeric value unless it’s an encoded value, straight from a James Bond movie, that can be converted to anything.

As shown in figure 7.22, NumberFormat-Exception is a runtime exception. It’s thrown to indicate that the application tried to convert a string (with an inappropriate format) to one of the numeric types.

Figure 7.22. Class hierarchy of NumberFormatException

Multiple classes in the Java API define parsing methods. One of the most frequently used methods is parseInt from the class Integer. It’s used to parse a String argument as a signed (negative or positive) decimal integer. Here are some examples:

Starting in Java 7, you can use underscores (_) in numeric literal values. But you can’t use them in String values passed to the method parseInt. The letters ABCD aren’t used in the decimal number system, but they can be used in the hexadecimal number system, so you can convert the hexadecimal literal value "12ABCD" to the decimal number system by specifying the base of the number system as 16:

Note that the argument 16 is passed to the method parseInt, not to the method println. The following will not compile:

You may throw NumberFormatException from your own method to indicate that there’s an issue with the conversion of a String value to a specified numeric format (decimal, octal, hexadecimal, binary), and you can add a customized exception message. One of the most common candidates for this exception is methods that are used to convert a command-line argument (accepted as a String value) to a numeric value. Please note that all command-line arguments are accepted in a String array as String values.

The following is an example of code that throws a NumberFormatException programmatically-:

The conversion of the hexadecimal literal 16b to the decimal number system is successful. But the conversion of the hexadecimal literal 65v to the decimal number system fails, and the previous code will give the following output:

363
Exception in thread "main" java.lang.NumberFormatException: 65v cannot be converted to hexadecimal number
    at ThrowNumberFormatException.convertToNum(ThrowNumberFormatException.java:8)
    at ThrowNumberFormatException.main(ThrowNumberFormatException.java:14)

Now let’s take a look at some of the common errors that are covered on this exam.

7.5.7. ExceptionInInitializerError

The ExceptionInInitializerError error is typically thrown by the JVM when a static initializer in your code throws any type of RuntimeException. Figure 7.23 shows the class hierarchy of ExceptionInInitializer-Error.

Figure 7.23. Class hierarchy of xceptionInInitializerError

A static initializer block is defined using the keyword static, followed by curly braces, in a class. This block is defined within a class but not within a method. It’s usually used to execute code when a class loads for the first time. Runtime exceptions arising from any of the following will throw this error:

  • Execution of an anonymous static block
  • Initialization of a static variable
  • Execution of a static method (called from either of the previous two items)

The static initializer block of the class defined in the following example will throw a NumberFormatException, and when the JVM tries to load this class, it’ll throw an ExceptionInInitializerError:

public class DemoExceptionInInitializerError {
    static {
        int num = Integer.parseInt("sd", 16);
    }
}

Following is the error message when the JVM tries to load the class DemoExceptionInInitializerError:

java.lang.ExceptionInInitializerError
Caused by: java.lang.NumberFormatException: For input string: "sd"
    at java.lang.NumberFormatException.forInputString(NumberFormatException.java:48)
    at java.lang.Integer.parseInt(Integer.java:447)
    at DemoExceptionInInitializerError.<clinit>(DemoExceptionInInitializerError.java:3)
Exam Tip

Beware of code that seems to be simple in the OCA Java SE 8 Programmer I exam. The class DemoExceptionInInitializerError (mentioned previously) seems deceptively simple, but it’s a good candidate for an exam question. As you know, this class throws the error ExceptionInInitializerError when the JVM tries to load it.

In the following example, initialization of a static variable results in a NullPointer-Exception being thrown. When this class is loaded by the JVM, it throws an ExceptionInInitializerError:

public class DemoExceptionInInitializerError1 {
    static String name = null;
    static int nameLength = name.length();
}

The error message when the JVM tries to load the DemoException-InInitializer-Error1 class is as follows:

java.lang.ExceptionInInitializerError
Caused by: java.lang.NullPointerException
    at DemoExceptionInInitializerError1.<clinit>(DemoExceptionInInitializerError1.java:3)
Exception in thread "main"

Now let’s move on to the exception thrown by a static method, which may be called by the static initializer block or to initialize a static variable. Examine the following code, in which MyException is a user-defined RuntimeException:

This is the error thrown by the class DemoExceptionInInitializerError2:

java.lang.ExceptionInInitializerError
Caused by: MyException
    at DemoExceptionInInitializerError2.getName(DemoExceptionInInitializerError2.java:4)
    at DemoExceptionInInitializerError2.<clinit>(DemoExceptionInInitializerError2.java:2)

Did you notice that the error ExceptionInInitializerError can be caused only by a runtime exception? This happens for valid reasons, of course.

If a static initializer block throws an error, it doesn’t recover from it to come back to the code to throw an ExceptionInInitializerError. This error can’t be thrown if a static initializer block throws an object of a checked exception because the Java compiler is intelligent enough to determine this condition and doesn’t allow you to throw an unhandled checked exception from a static initialization block.

Exam Tip

ExceptionInInitializerError can be caused by an object of RuntimeException only. It can’t occur as the result of an error or checked exception thrown by the static initialization block.

7.5.8. StackOverflowError

The StackOverflowError error extends Virtual-MachineError (as shown in figure 7.24). As its name suggests, you should leave it to be managed by the JVM.

Figure 7.24. Class hierarchy of StackOverflowError

This error is thrown by the JVM when a Java program calls itself so many times that the memory stack allocated to execute the Java program “overflows” (overflows means that the stack exceeds a certain size). Examine the following code, in which a method calls itself recursively without an exit condition-:

The following error is thrown by the previous code:

Exception in thread "main" java.lang.StackOverflowError
    at DemoStackOverflowError.recursion(DemoStackOverflowError.java:3)

7.5.9. NoClassDefFoundError

What would happen if you failed to set your classpath and, as a result, the JVM was unable to load the class that you wanted to access or execute? Or what would happen if you tried to run your application before compiling it (and so no .class file would be found for the class you were trying to use)? In both these conditions, the JVM would throw a NoClassDefFoundError (the class hierarchy shown in figure 7.25).

Figure 7.25. Class hierarchy of NoClassDefFoundError

This is what the Java API documentation says about this error:

Thrown if the Java Virtual Machine or a ClassLoader instance tries to load in the definition of a class (as part of a normal method call or as part of creating a new instance using the new expression) and no definition of the class could be found.[1]

1

The NoClassDefFoundError documentation can be found in the Javadoc: http://docs.oracle.com/javase/8/docs/api/java/lang/NoClassDefFoundError.html.

Because this particular error isn’t a coding issue, I don’t have a coding example for you. As you can see from the error hierarchy diagram in figure 7.25, this is a linkage error arising from a missing class file definition at runtime. Like every system error, this error shouldn’t be handled by the code and should be left to be handled exclusively by the JVM.

Note

Don’t confuse the exception thrown by Class.forName(), used to load the class, and NoClassDefFoundError, thrown by the JVM. Class.forName() throws ClassNotFoundException.

7.5.10. OutOfMemoryError

What happens if you create and use a lot of objects in your application—for example, if you load a large chunk of persistent data to be processed by your application? In such a case, the JVM may run out of memory on the heap, and the garbage collector may not be able to free more memory for the JVM. In this case, the JVM is unable to create any more objects on the heap. An OutOfMemoryError will be thrown (the class hierarchy shown in figure 7.26).

Figure 7.26. Class hierarchy of OutOfMemoryError

You’ll always work with a finite heap size, no matter what platform you work on, so you can’t create and use an unlimited number of objects in your application. To get around this error, you need to either limit the number of resources or objects that your application creates or increase the heap size on the platform you’re working with.

A number of tools are available (which are beyond the scope of this book) that can help you monitor the number of objects created in your application.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.119.160.181