In the previous lesson we looked at sorting in alternative sequences.
Exceptions
Exceptions are Java's way of controlling unexpected occurrences, and which allows you can gain control over what to do in these circumstances.
In this section you will learn:
- What exceptions are and how to use them
- The different categories of exception
- How to define your own exceptions
What are exceptions?
Methods and constructors can throw an exception if something occurs which would prevent the normal execution of the program. Several Java supplied classes can throw exceptions if you attempt to do something that is not appropriate. An Exception is an object, albeit one that the Java runtime applies special handling for. There are many Java supplied exception classes and you can define your own if need be.
There are three categories of exception that can be thrown by a constructor or by a method:
- Unchecked exceptions: These refer to situations which you as the programmer could have prevented but didn't do so, such as dividing a number by zero or invoking a method on an object reference that is
null(maybe because you have not yet instantiated the object). Because this category of exception is preventable the Java compiler will not force you to handle the exception if you let it occur. It is strongly recommended that you use defensive programming techniques to prevent this type of exception occurring (see later example) - Checked exceptions: These are situations which you as the programmer cannot prevent and therefore the Java compiler will force you to make provision for. Examples would be trying to open a file that does not exist (maybe because someone deleted the file) or being unable to connect to a remote server because it is unavailable. The Java compiler forces you to use the exception handling mechanism for these situations
- Errors: These are situations which you as the programmer cannot prevent and that there would be little point in attempting to, such as an execution fault in the JRE or if your program runs out of memory
Handling unchecked exceptions
To demonstrate the category where an exception can occur but the programmer has not prevented it, enter the following statements:
int numerator = 123;
int denominator = 0;
System.out.println("answer = " + (numerator / denominator));
If you attempt to run the above you will receive an ArithmeticException and the program will stop, since division by zero is not a valid mathematical operation.
To use defensive programming, simply check that the denominator value is not zero before doing the calculation:
int numerator = 123;
int denominator = 0;
if (denominator != 0) {
System.out.println("answer = " + (numerator / denominator));
} else {
System.out.println("denominator is zero");
}
One of the most common exceptions you are likely to encounter in this category is NullPointerException.
Suppose you have the code:
ZooKeeper fred = null; String fredName = fred.getName();
You would receive a NullPointerException at runtime and the program would stop. You need to prevent the exception by ensuring that either you check for null or by instantiating fred before attempting to invoke any methods upon it.
Checked exceptions
The FileReader class enables you to read from text files, and one of its constructors allows you to pass a String argument containing the path and file name you wish to read from. See what happens if you have the following line of code somewhere: (note: to try this you need to import package java.io).
FileReader someFile = new FileReader("filename.txt");
Your class will not compile because the FileReader constructor can potentially throw the checked exception FileNotFoundException (see the Java API documentation), and you have not yet made any provision for this. You can provide for these situations by placing the statements that can throw an exception within a try…catch block, as follows:
try {
FileReader someFile = new FileReader("filename.txt");
} catch (FileNotFoundException ex) {
System.out.println(ex.getMessage());
}
Note the following:
- Inside the
tryblock you include the statement(s) which could throw a checked exception - If an exception is thrown, any statements following the one that caused the exception will be skipped and execution will pass to the catch block
- The
catchblock specifies the name of the exception class that is being handled (FileNotFoundExceptionin the above case), an object of which is assigned to a reference variable (exin the above case). The code within thecatchblock will be executed - If no exception is thrown, then the code within the
catchblock is not executed
If you have a series of statements, then it is possible that between them they may throw more than one type of exception. In this case you can either catch them individually (if you need to do something different for each exception should it occur) or catch them all inside a single catch handler.
Continuing the earlier example, after instantiating a FileReader object you invoke its read() method to return a character from the file. This method can throw a IOException:
try {
FileReader someFile = new FileReader("filename.txt");
int c = someFile.read();
} catch (FileNotFoundException ex) {
System.out.println(ex.getMessage());
} catch (IOException ex) {
System.out.println(ex.getMessage());
}
The above shows how you can have a separate catch block for each exception. If you look at the Java API for FileNotFoundException you will see that it inherits from IOException. When this is the case you need to catch the more specialised exception (which in this case is FileNotFoundException) before the more generalised exception (which in this case isIOException). The compiler will not allow you to catch IOException first since it could then never reach the FileNotFoundException block.
What you can do, however, is combine the inherited exceptions into a single catch block when you are performing the same handling code in all cases, as is the case in the above example. You could therefore change the above to the following:
try {
FileReader someFile = new FileReader("filename.txt");
int c = someFile.read();
} catch(IOException ex) {
System.out.println(ex.getMessage());
}
The above approach works because FileNotFoundException "is a" (i.e., inherits from) IOException, so it will be caught by the latter's catch clause.
With the try…catch mechanism you can optionally add a finally block containing code which will always be executed, whether an exception is thrown or not:
FileReader someFile = null;
try {
someFile = new FileReader("filename.txt");
int c = someFile.read();
} catch(IOException ex) {
System.out.println(ex.getMessage());
} finally {
if (someFile != null) {
try {
someFile.close();
} catch (IOException ex) {
System.out.println(ex.getMessage());
}
}
}
The finally block is frequently used to close opened resources, such as files and network connections. Note in the above example that the FileReader object someFile needed to be declared prior to the try block in order for it to still be in scope inside the finally block. Note also that the close() method can itself throw an exception, so that needs to be caught too.
It is important that opened resources such as files are closed when you have finished with them to ensure that the file is properly updated.
Enhancements from Java 7
From Java 7 onwards you can get the resources that require closing to be automatically taken care of, using a modification to the try...catch block. For example, the above example could instead be written as follows:
try (FileReader someFile = new FileReader("filename.txt")) {
int c = someFile.read();
} catch(IOException ex) {
System.out.println(ex.getMessage());
}
If you need multiple files to be automatically closed you can place each of them inside the same brackets of the try statement, where each is separated by a semi-colon.
Another enhancement from Java 7 is the ability to catch multiple exceptions in the same block, which is useful if you want to take the identical action in each case. Here is an example from an earlier part of this section:
try {
FileReader someFile = new FileReader("filename.txt");
int c = someFile.read();
} catch (FileNotFoundException ex) {
System.out.println(ex.getMessage());
} catch (IOException ex) {
System.out.println(ex.getMessage());
}
From Java 7 you can use a single catch block with each exception separated by a vertical bar:
try {
FileReader someFile = new FileReader("filename.txt");
int c = someFile.read();
} catch (FileNotFoundException | IOException ex) {
System.out.println(ex.getMessage());
}
In the next lesson you will learn about throwing exceptions.
Comments