Errors, exceptions, and exception handling

22 Apr

Errors, exceptions, and exception handling
Nothing is worse than software that crashes. Java provides a method for handling software errors through what is known as exception handling. This section will briefly discuss exceptions and exception handling, including:
• Definition of exceptions and exception handling
• Different types of exceptions
• Exception examples: a specified exception, our very own exception

An exception, simply put, is something going wrong during the course of program execution. Exception handling is the term used to describe how the program will deal with these exceptions. When an unexpected condition occurs, an exception object is created and the method or class that experienced the error condition is said to throw the exception. The exception object can then be passed throughout the program and handled where it’s most appropriate to do so. The basic mechanism in Java for handling these exception objects is the try/catch block. Consider a simple example.

import java.io.*;
// This program will NOT compile

public class CheckedException {
public static void main(String[] args) {
String inputLines;
BufferedReader myFile = new BufferedReader(
while ((inputLines=myFile.readLine()) != null) {
System.out.println(inputLines);
}
}
}

The program example above tries to open a text file, read in the text, and print that text to the screen. The code block responsible for doing that work is enclosed in a try/catch block. That indicates that the program will try to perform the operations inside the block, and if it is unable to because of an exception, it will catch the exception and execute the code in the catch code block. If we tried to compile the above program without the try/catch statements, we would receive a compiler error.

C:\> javac CheckedException.Java


CheckedException.Java:5: Exception Java.io.FileNotFoundException must
be caught, or it must be declared in the throws clause of this method.
BufferedReader myFile = new BufferedReader(new
FileReader(args[0]));
CheckedException.Java:6: Exception Java.io.IOException must be caught,
or it must be declared in the throws clause of this method.
while ((inputLines=myFile.readLine()) != null) {
2 errors

The reason for this is that several of the methods used in the program throw exceptions, which requires provisions to check for, or catch them. The Java API shows which methods throw exceptions and precisely which exceptions they throw. In our example, close() and readLine() are methods of the BufferedReader class, which both throw an exception called IOException. IOException is a class that extends the Exception class and is superclass to a host of more specific classes such as the FileNotFoundException. The FileReader constructor throws the FileNotFoundException, which can be caught separately, or as an IOException because it’s a subclass of that exception. The following revised version compiles without errors.

import java.io.*;
// This one will compile
public class FixedCheckedException {
public static void main(String[] args) {
String inputLines;
try {
BufferedReader myFile = new BufferedReader(
new FileReader(args[0]));
while ((inputLines=myFile.readLine()) != null) {
System.out.println(inputLines);
}
} catch(IOException e) {
System.out.println(“There was an IOException”);
}
}
}

As already noted, there are numerous different types of exceptions, but since exceptions are themselves objects, they form an inheritance hierarchy. Errors are nonrecoverable conditions that should not be caught. They usually indicate serious flaws in the logic of the program. Exceptions are abnormal conditions that should be caught and dealt with. Exceptions can further be broken down into different types:

• Checked Exceptions are exceptions that are checked by the Java compiler. The compiler is looking to see that your program is either performing exception handling through a try/catch block or specifying that the method throws an exception, thereby passing any exception on and forcing other calling methods to perform exception handling. This is commonly referred to as the “catch or specify” requirement. The IOException from the example above is a checked exception. Checked exceptions are not runtime exceptions—that is, they don’t occur during program operation.

• Runtime Exceptions are exceptions that occur during program operation. Rules governing runtime exceptions and checked exceptions are different. Methods that throw runtime exceptions do not have to be enclosed in try/ catch blocks, which is to say the “check or specify” requirement does not apply to them. The rules are different because runtime exceptions can occur in very numerous and unpredictable ways. It can be too difficult for the compiler to check for them and too difficult for the programmer to “catch or specify” them all. Good examples of runtime exceptions are division by zero and ArrayIndexOutOfBoundsException. They can be caught by try/catch blocks just like any other exception, but methods at risk for throwing these exceptions don’t have to be enclosed in try/catch blocks to get the program to compile.

We’ve seen an example of a checked exception in which we caught the exception in a try/catch block. Let’s now look at the same example, but now instead of catching the exception, let’s specify it.

Listing 3-10: SpecifiedException.java

import java.io.*;
public class SpecifiedException {
public static void main(String[] args) throws IOException {
String inputLines;
BufferedReader myFile = new BufferedReader(
new FileReader(args[0]));
while ((inputLines=myFile.readLine()) != null) {
System.out.println(inputLines);
}
}
}

The program, SpecifiedException.java, will compile and run even though we are no longer catching the checked exceptions. This is because we are specifying them through the use of the throws keyword and we are meeting the “catch or specify” requirement. Since exceptions are essentially a class hierarchy descending from the Throwable class, there’s nothing preventing us from extending the Exception class ourselves and writing our own exceptions. Consider the following:

import java.io.*;
//   This program will NOT compile
public class CustomException {
public static class FileIsTooShortException extends Exception {
public FileIsTooShortException(String str) {
super(str);
}
}
public void readFile(String fileName) throws
FileIsTooShortException {
String inputLines;
int index = 0;
try {
BufferedReader myFile = new BufferedReader(
new FileReader(fileName));
while ((inputLines=myFile.readLine()) != null) {
System.out.println(inputLines);
index++;
}
} catch(IOException e) {
System.out.println(“There was an IOException”);
}
if (index < 200) {
throw new FileIsTooShortException(“That file is too small”);
}
}
public static void main(String[] args) {
CustomException dummy = new CustomException();
dummy.readFile(args[0]);
}
}
This program contains a class definition for a new exception, a
FileIsTooShortException. The definition consists of nothing more than a
constructor that calls the superclass constructor, which in this case will be the
Exception constructor.
public static class FileIsTooShortException extends Exception {
public FileIsTooShortException(String str) {
super(str);
}
}

Exactly what a FileIsTooShortException is supposed to mean isn’t clear from the constructor. It becomes clear in the method below. Here, we have defined a method, printFile(), that accepts a file name as an argument, opens the file and then prints out the lines to the screen. Since several of the methods used throw IOExceptions, there is a try/catch block for exception handling. But, we have also specified that this method throws an exception, a FileIsTooShortException. In conjunction with this, the method counts the number of lines. If the number of lines is less than 200, it uses the FileIsTooShortException constructor to create an Exception object and throws that object.

public void readFile(String fileName) throws
FileIsTooShortException {
String inputLines;
int index = 0;
try {
BufferedReader myFile = new BufferedReader(
new FileReader(fileName));
while ((inputLines=myFile.readLine()) != null) {
System.out.println(inputLines);
index++;
}
} catch(IOException e) {
System.out.println(“There was an IOException”);
}
if (index < 200) {
throw new FileIsTooShortException(“That file is too small”);
}
}
Finally, there is the main() method. This simply creates a dummy CustomException
object and invokes the printFile() method.
public static void main(String[] args) {
CustomException dummy = new CustomException();
dummy.readFile(args[0]);
}
If you try to compile this program, you will see the following:
c:\> javac CheckedException.java
CustomException.Java:28: Exception CustomException.
FileIsTooShortException must be caught, or it must be declared
in the throws clause of this method.
dummy.readFile(args[0]);
^
1 error

This is actually a good thing! It means that we have succeeded in making our own custom exception. The program as written has a custom checked exception built into it, FileIsTooShortException. The method that throws that exception is printFile(). Since it’s a checked exception, we have to either “catch or specify” the exception. To catch it, we would embed the printfile() method in a try/catch block. To specify it, we would have to declare that the method calling printFile() throws the FileIsTooShortException (thereby passing the buck). Since the program above does neither of these, we should see exactly the error we are seeing. Both fixes are illustrated below. The catch method of fixing it:

Listing 3-12: CatchCustomException.java
import java.io.*;
public class CatchCustomException {
public static class FileIsTooShortException extends Exception {
public FileIsTooShortException(String str) {
super(str);
}
}
public void readFile(String fileName) throws FileIsTooShortException {
String inputLines;
int index = 0;
try {
BufferedReader myFile = new BufferedReader(
new FileReader(fileName));
while ((inputLines=myFile.readLine()) != null) {
System.out.println(inputLines);
index++;
}
} catch(IOException e) {
System.out.println(“There was an IOException”);
}
if (index < 200) {
throw new FileIsTooShortException(“That file is too small”);
}
}
public static void main(String[] args) {
CatchCustomException dummy = new CatchCustomException();
try {
dummy.readFile(args[0]);
} catch(FileIsTooShortException e) {
System.out.println(“We are experiencing a strange new exception”);
}
}
}

The specify way of fixing this:
Listing 3-13: SpecifyCustomException.java

import java.io.*;
public class SpecifyCustomException {
public static class FileIsTooShortException extends Exception {
public FileIsTooShortException(String str) {
super(str);
}
}
public void readFile(String fileName) throws
FileIsTooShortException {
String inputLines;
int index = 0;
try {
BufferedReader myFile = new BufferedReader(
new FileReader(fileName));
while ((inputLines=myFile.readLine()) != null) {
System.out.println(inputLines);
index++;
}
} catch(IOException e) {
System.out.println(“There was an IOException”);
}
if (index < 200) {
throw new FileIsTooShortException(“That file is too small”);
}
}
public static void main(String[] args) throws
FileIsTooShortException {
SpecifyCustomException dummy = new SpecifyCustomException();
dummy.readFile(args[0]);
}
}

Before leaving this section, there is one more brief topic to address: the finally keyword. In addition to the catch portion of the try/catch block, Java provides a mechanism for cleaning up after exception handling. This is the finally keyword. Code located within the finally block gets executed no matter what happens inside the try/catch block. It’s a good place to close any open files or devices.

Listing 3-14: FinallyExample.java

import java.io.*;
public class FinallyExample {
public static void main(String[] args) {
String inputLines;
BufferedReader myFile = null;
try {
myFile = new BufferedReader(new FileReader(args[0]));
while ((inputLines=myFile.readLine()) != null) {
System.out.println(inputLines);
}
} catch(IOException e) {
System.out.println(“There was an IOException”);
} finally {
try {
myFile.close();
} catch (IOException e2) {
System.out.println(
“There was an IOException in closing the file”);
}
}
}
}

The finally block in the code above will always be executed after the first try block. So no matter  what kind of exception happens in the first try/catch block, the program will at least attempt to close the BufferedReader.

Random Posts

Comments are closed.