In this section, we’ll take a look at common exception classes and categories of excep- tions. 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 com- mon errors and exceptions. Although the exam specifically lists four runtime excep- tions, you might see the other common exception and error classes on the exam.
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.
Table 7.2 Common errors and exceptions
Runtime exceptions Errors
ArrayIndexOutOfBoundsException ExceptionInInitializerError IndexOutOfBoundsException StackOverflowError
ClassCastException NoClassDefFoundError IllegalArgumentException OutOfMemoryError ArithmeticException
NullPointerException NumberFormatException
[8.5] “Recognize common exception classes (such as NullPointerException, ArithmeticException, ArrayIndexOutOfBoundsException,
ClassCastException)”
504 CHAPTER 7 Exception handling
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. IndexOutOf- BoundsException is subclassed by 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 ille- gal 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:
System.out.println(season[5]);
System.out.println(season[-9]);
The following lines of code will throw an IndexOutOfBoundsException:
System.out.println(exams.get(-1));
System.out.println(exams.get(4));
Why do you think the JVM has taken the responsibility on itself to throw this excep- tion? One of the main reasons is that this exception isn’t known until runtime and
java.lang.Throwable
java.lang.Exception
java.lang.RuntimeException
java.lang.IndexOutOfBoundsException
java.lang.ArrayIndexOutOfBoundsException Figure 7.17 Class hierarchy of ArrayIndexOutOfBoundsException
Can’t access position
>= array length Can’t access array at negative position
Can’t access list at negative position Can’t access list at position >= its size
505 Common exception classes and categories
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, ArrayIndexOutOf- BoundsException 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.
Examine the code in the next listing, where the line of code that throws the Class- CastException is shown in bold.
import java.util.ArrayList;
public class ListAccess {
public static void main(String args[]) { ArrayList<Ink> inks = new ArrayList<Ink>();
inks.add(new ColorInk());
inks.add(new BlackInk());
Ink ink = (BlackInk)inks.get(0);
} }
class Ink{}
class ColorInk extends Ink{}
class BlackInk extends Ink{}
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
Listing 7.2 An example of code that throws ClassCastException
java.lang.Throwable
java.lang.Exception
java.lang.RuntimeException
java.lang.ClassCastException
Figure 7.18 Class hierarchy of ClassCastException
Throws
ClassCastException
506 CHAPTER 7 Exception handling
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 sub- classes. 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:
import java.util.ArrayList;
public class Invalid {
public static void main(String args[]) {
ArrayList<ColorInk> inks = new ArrayList<ColorInk>();
inks.add(new ColorInk());
Ink ink = (BlackInk)inks.get(0);
} }
class Ink{}
class ColorInk extends Ink{}
class BlackInk extends Ink{}
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:
import java.util.ArrayList;
public class AvoidClassCastException { public static void main(String args[]) { ArrayList<Ink> inks = new ArrayList<Ink>();
inks.add(new ColorInk());
inks.add(new BlackInk());
if (inks.get(0) instanceof BlackInk) {
BlackInk ink = (BlackInk)inks.get(0);
} } }
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).
Compilation issues
No
ClassCastException
507 Common exception classes and categories
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;
} }
a printable=(Printable)blackInk will throw compilation error
b printable=(Printable)blackInk will throw runtime exception
c printable=(Printable)blackInk will throw checked exception
d The following line of code will fail to compile:
printable = blackInk;
7.5.3 IllegalArgumentException
As the name of this exception suggests, Illegal- ArgumentException is thrown to specify that a method has passed illegal or inappropriate argu- ments. Its class hierarchy is shown in figure 7.19.
Even though it’s a runtime exception, pro- grammers 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. Exam- ine 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");
Twist in the Tale 7.4
java.lang.Throwable
java.lang.Exception
java.lang.RuntimeException
java.lang.IllegalArgumentException
Figure 7.19 Class hierarchy of IllegalArgumentException
508 CHAPTER 7 Exception handling //.. 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.
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 NullPointer- Exception
■ 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:
import java.util.ArrayList;
class ThrowNullPointerException {
static ArrayList<String> list = null;
public static void main(String[] args) { list.add("1");
} }
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) java.lang.Throwable
java.lang.Exception
java.lang.RuntimeException
java.lang.NullPointerException
Figure 7.20 Class hierarchy of NullPointerException
list is null
Attempt to call method add on list throws NullPointerException
509 Common exception classes and categories
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- PointerException (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:
import java.util.ArrayList;
class ThrowNullPointerException {
ArrayList<String> list;
public static void main(String[] args) {
ThrowNullPointerException obj = new ThrowNullPointerException();
obj.list.add("1");
} }
You can prevent a NullPointerException from being thrown by checking whether an object is null before trying to access its member:
import java.util.ArrayList;
class ThrowNullPointerException { static ArrayList<String> list;
public static void main(String[] args) { if (list!=null)
list.add("1");
} }
What happens if you modify the previous code as follows? Will it still throw a Null- PointerException?
import java.util.ArrayList;
class ThrowNullPointerException {
public static void main(String[] args) { ArrayList<String> list;
if (list!=null) list.add("1");
} }
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.
list is implicitly assigned a null value
Attempt to call method add on list throws NullPointerException
Ascertain that list is not null
Fails to compile
510 CHAPTER 7 Exception handling
Another set of conditions when code may throw the NullPointerException involves the use of arrays:
class ThrowAnotherNullPointerException { static String[] oldLaptops;
public static void main(String[] args) { System.out.println(oldLaptops[1]);
} }
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:
class ThrowNullPointerException {
public static void main(String[] args) { String[] newLaptops = new String[2];
System.out.println(newLaptops[1].toString());
} }
If you modify the code at B 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:
System.out.println(newLaptops[1]);
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).
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 =
Twist in the Tale 7.5
Throws
NullPointerException
Throws
NullPointerException
b
No RuntimeException; prints “null”
511 Common exception classes and categories
{ {"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 }
}
a Code on line 1 will throw NullPointerException
b Code on lines 1 and 3 will throw NullPointerException
c Only code on line 4 will throw NullPointerException
d Code on lines 3 and 5 will throw NullPointerException
7.5.5 ArithmeticException
When the JVM encounters an exceptional mathe- matical 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 deci- mals by 0 and 0.0.
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.
DIVISIONOFANINTEGERVALUEBY 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):
class ThrowArithmeticEx {
public static void main(String args[]) { System.out.println(77/0);
} }
java.lang.Throwable
java.lang.Exception
java.lang.RuntimeException
java.lang.AritmeticException
Figure 7.21 Class hierarchy of ArithmeticException
Division by 0
512 CHAPTER 7 Exception handling
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 vari- able 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 pre- ceding code will also throw an ArithmeticException.
EXAM TIP Division of a negative or positive integer value by 0 will result in an ArithmeticException.
513 Common exception classes and categories
Here’s an explicit example of dividing 0 by 0:
System.out.println(0/0);
EXAM TIP Division of 0 by 0 results in an ArithmeticException.
DIVISIONOFADECIMALVALUEBY 0
If you divide a positive decimal number by 0, the answer is Infinity:
class DivideDecimalNumberByZero {
public static void main(String args[]) { System.out.println(77.0/0);
} }
If you divide a negative decimal number by 0, the answer is -Infinity:
class DivideNegativeDecimalNumberByZero { public static void main(String args[]) { System.out.println(-77.0/0);
} }
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:
System.out.println(0.0/0);
EXAM TIP Division of 0.0 by 0 results in NaN (Not a Number).
Any mathematical operation with a NaN results in NaN. DIVISIONOFINTEGERSORDECIMALSBY 0.0
Dividing by 0 and dividing by 0.0 don’t give you the same results. Let’s revisit the pre- vious examples, starting with the modified version of the first example in this section:
class DivideIntegerByZeroPointZero {
public static void main(String args[]) { System.out.println(77/0.0);
System.out.println(77.0/0.0);
} }
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.
Throws ArithmeticException
Outputs Infinity
Outputs -Infinity
Outputs NaN
Outputs Infinity
514 CHAPTER 7 Exception handling
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.
Multiple classes in the Java API define pars- ing methods. One of the most frequently used
java.lang.Throwable
java.lang.Exception
java.lang.RuntimeException
java.lang.IllegalArgumentException
java.lang.NumberFormatException
Figure 7.22 Class hierarchy of NumberFormatException