Lecture 07: Exception
Handling
SE116: Introduction to Programming II
Previously on SE116
● We have covered the use of run-time polymorphism, abstract classses, and interfaces.
● Polymorphism refers to binding of the corresponding memory addresses of the same named methods to the code where they are called.
● There are two kinds of polymorphism:
● Compile-time polymorphism (also referred as early binding or static binding)
● In use for ‘overloaded methods’
● The compiler is able to separate the overloaded methods since their signatures are different.
● Run-time polymorphism (also referred as late binding or dynamic binding)
● In use for ‘overridden methods’
● If the programming languge supports the run-tiime polymorphism, the compiler ignores binding of an overridden method called via a superclass reference.
● Run-time polymorphism provides the programming language with binding an overridden method on a subclass object where the method is called via a
superclass reference.
● The keyword ‘abstract’ in Java is used for a method and/or a class.
● An abstract method has no body definition. An abstract method can only be a member of an abstract class.
● An abstract class contains constructor at least. An abstract class can contain data members and/or defined methods and/or abstract methods.
● If a class is not abstract, then it is a concrete class.
● An abstract class can not be used to instantiate an object. An abstract class can be used as a superclass in an inheritance hiearchy and can be used to define a variable
that would refer to a concrete subclass object in that hiearchy.
● The keyword ‘interface’ in Java is used to create data types diferent than classes.
● An interface is used to declare abstract method(s).
● An interface is not a class. Therefore, an interface can not be used to instantiate an object. A class can realize an interface by implementing the body definitions of the
abstract methods of that interface.
● If a concrete class ‘implements’ an interface, the class must override all the abstract methods of the interface.
● An interface can extend another interface. A class can implement multiple interfaces.
● An interface can also contain variables specifed as ‘public final static’.
● Checkpoint: Using interface vs using abstract class?
Previously on SE116
● What are the main features of an Object Oriented Programming Language?
○ An object oriented programming language should provide the following features (at least):
■ encapsulation,
■ inheritance,
■ polymorphism (run-time polymorphism is meant here).
● So, we are done with the main features. From now on, let’s dissect another feature
supported by Java programming language: ‘exception handling’.
Introduction
● Definition: An exception is an event, which occurs during the execution of a
program, that disrupts the normal flow of the program's instructions.
● Compilation errors can be detected by the compiler at compilation time.
● Run-time (execution) errors happen because of unexpected turn of events.
● It is not always possible to foresee these runtime errors, but we at least have
an idea about where they might appear.
● However, how can we handle them as the program is in execution?
● Remember the call stack.
Introduction
● The entry to every application is the main function.
● This function calls other functions, and other functions call others.
● There can only be a single function executing in a single thread.
● Every time a function is called, it is placed in a “call stack.”
● Stacks are a basic data structure that you will learn next year. For now, you
should know that they are simply a list.
○ We push a value (in this case the function) to the stack; that is, we add it to the list.
○ We pop a value from the stack; that is we get the value AND remove it from the list.
○ Stacks are first in last out; if we push A, B, C and D, and then when we pop we get D, C, B,
and A.
○ Very much like function calls; main calls A, A calls B, B calls C, C calls D; when D returns, it
must return to C, C to B, B to A, and A to main.
Introduction
● When something unexpected happens in a function, we know how we got to that
function thanks to the call stack; and we also have an option to let the calling function
handle the exception.
○ What do I mean by that?
○ Let’s say that your average method finds the average value in a list of integer values:
float average(int[] values) { … }
○ What if the list is empty? What will the method return? Zero? This assumes that there are n number of
zeros in the list.
○ You are working with positive integers, so return -1 so that the method that calls average should know
there is a problem with the list when it checks the return value and finds out that it is less than zero.
○ Instead we can throw an exception and let the calling function handle the empty array problem.
Maybe it will re-read the file for the values, maybe it will ask the user to enter the values again. We
don’t care: we want our list to have at least 1 value.
○ That is what I mean; we can pass the exception to the calling function…
Exception Objects
● This is a great opportunity to introduce objects that are not real-life objects.
● Once an exception is thrown, the runtime creates an object and hands it off to
the runtime system.
● This exception object contains information about the error, the type of
exception and the state of the program when it happened.
● This is very useful debugging information.
Exception Objects
● Once the exception is thrown, the runtime system attempts to find something
to handle it.
● It looks at the call stack to find a place that can handle it:
Catch or Specify
● We must honor the Catch or Specify Requirement.
● This means that code that might throw certain exceptions must be enclosed
by either of the following:
○ A try statement that catches the exception. The try must provide a handler for the exception.
○ A method that specifies that it can throw the exception. The method must provide a throws
clause that lists the exception.
● Code that fails to honor the Catch or Specify Requirement will not compile.
● Not all exceptions are subject to the Catch or Specify Requirement.
● To understand why, we need to look at the three basic categories of
exceptions, only one of which is subject to the Requirement.
Kinds of Exceptions
● The first kind of exception is the checked exception.
● These are exceptional conditions that a well-written application should anticipate
and recover from.
○ For example, suppose an application prompts a user for an input file name, then opens the file by
passing the name to the constructor for java.io.FileReader.
○ Normally, the user provides the name of an existing, readable file, so the construction of the
FileReader object succeeds, and the execution of the application proceeds normally.
○ But sometimes the user supplies the name of a nonexistent file, and the constructor throws
java.io.FileNotFoundException.
○ A well-written program will catch this exception and notify the user of the mistake, possibly prompting
for a corrected file name.
● Checked exceptions are subject to the Catch or Specify Requirement.
● All exceptions are checked exceptions, except for those indicated by Error,
RuntimeException, and their subclasses.
Kinds of Exceptions
● The second kind of exception is the error.
● These are exceptional conditions that are external to the application, and that
the application usually cannot anticipate or recover from.
○ For example, suppose that an application successfully opens a file for input, but is unable to
read the file because of a hardware or system malfunction.
○ The unsuccessful read will throw java.io.IOError.
○ An application might choose to catch this exception, in order to notify the user of the problem
— but it also might make sense for the program to print a stack trace and exit.
● Errors are not subject to the Catch or Specify Requirement.
● Errors are those exceptions indicated by Error and its subclasses.
Kinds of Exceptions
● The third kind of exception is the runtime exception.
● These are exceptional conditions that are internal to the application, and that the
application usually cannot anticipate or recover from.
● These usually indicate programming bugs, such as logic errors or improper use of an
API.
○ For example, consider the application described previously that passes a file name to the constructor for
FileReader.
○ If a logic error causes a null to be passed to the constructor, the constructor will throw
NullPointerException.
○ The application can catch this exception, but it probably makes more sense to eliminate the bug that
caused the exception to occur.
● Runtime exceptions are not subject to the Catch or Specify Requirement.
● Runtime exceptions are those indicated by RuntimeException and its subclasses.
● Errors and runtime exceptions are collectively known as unchecked exceptions.
Kinds of Exceptions
Catching and Handling Exceptions
● Very simple: try-catch-finally
● We try (or attempt) that might cause an exception in a try block.
● If there is an exception, we catch it, simply by listing possible exception types
in a series of catch blocks.
● If there is no exception, then none of the catch blocks are executed.
● The finally block always executes, even when there is no exception.
○ It is a good place to close the connections for example, because this block executes even
when there is an exception.
● Let’s take a look at an example.
1. public void writeList() {
2. PrintWriter out = null;
3.
4. try {
5. System.out.println("Entering try statement");
6.
7. out = new PrintWriter(new FileWriter("OutFile.txt"));
8. for (int i = 0; i < SIZE; i++) {
9. out.println("Value at: " + i + " = " + list.get(i));
10. }
11. } catch (IndexOutOfBoundsException e) {
12. System.err.println("Caught IndexOutOfBoundsException: "
13. + e.getMessage());
14.
15. } catch (IOException e) {
16. System.err.println("Caught IOException: " + e.getMessage());
17.
18. } finally {
19. if (out != null) {
20. System.out.println("Closing PrintWriter");
21. out.close();
22. }
23. else {
24. System.out.println("PrintWriter not open");
25. }
26. }
27. }
Catching More Than One Type of Exception with One Exception Handler
● A single catch block can handle more than one type of exception.
● This feature can reduce code duplication and lessen the temptation to catch
an overly broad exception.
● In the catch clause, specify the types of exceptions that block can handle, and
separate each exception type with a vertical bar (|):
1. catch (IOException|SQLException ex) {
2. /* handle exception */
3. }
Specifying the Exceptions Thrown by a Method
● It's appropriate for code to catch exceptions that can occur within it.
● In other cases, however, it's better to let a method further up the call stack
handle the exception.
○ For example, if you were providing the average method we have discussed earlier as part of
a package of classes, you probably couldn't anticipate the needs of all the users of your
package.
○ In this case, it's better to not catch the exception and to allow a method further up the call
stack to handle it.
● We do this by adding a throws clause to the method declaration.
○ The throws clause comprises the throws keyword followed by a comma-separated list of all
the exceptions thrown by that method.
○ The clause goes after the method name and argument list and before the brace that defines
the scope of the method
Specifying the Exceptions Thrown by a Method
1. public float average(int[] myArray) throws Exception, RuntimeException
{
2. if(myArray.length == 0) {
3. throw new Exception("Array has no elements");
4. }
5. /* ... */
6. }
Picking up Exceptions
● Consult the documentation.
● https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Except
ion.html
● There are so many exception types here.
● Let’s check IOException.
● Signals that an I/O exception of some sort has occurred. This class is the
general class of exceptions produced by failed or interrupted I/O operations.
● Remember the “Catch or Specify” requirement.
Create your own Exception
● Just extend one of the exceptions.
● Let’s work on the average method.
● We can create an exception called EmptyArrayException that extends
RuntimeException.
1. class EmptyArrayException extends RuntimeException {
2. public EmptyArrayException() {
3. super("Array has no elements");
4. }
5. }
6.
7. class MyAverage {
8. public static float average(int[] myArray)
9. throws EmptyArrayException {
10.
11. if (myArray.length == 0) {
12. throw new EmptyArrayException();
13. }
14.
15. float sum = 0;
16. for(int i=0;i<myArray.length;i++) sum += myArray[i];
17. return sum / (float)myArray.length;
18. }
19. }
20. Notice the call stack!
21. public class MyDriver { $ javac MyDriver.java
22. public static void main(String[] args){ $ java MyDriver
23. int[] m = {}; Exception in thread "main" EmptyArrayException: Array has no
24. System.out.println(MyAverage.average(m)); elements
25. } at MyAverage.average(MyDriver.java:12)
26. } at MyDriver.main(MyDriver.java:24)
Create your own Exception
● You should write your own exception classes if you answer yes to any of the
following questions; otherwise, you can probably use someone else's.
○ Do you need an exception type that isn't represented by those in the Java platform?
○ Would it help users if they could differentiate your exceptions from those thrown by classes
written by other vendors?
○ Does your code throw more than one related exception?
○ If you use someone else's exceptions, will users have access to those exceptions? A similar
question is, should your package be independent and self-contained?
Chained Exceptions
● An application often responds to an exception by throwing another exception.
● In effect, the first exception causes the second exception.
● It can be very helpful to know when one exception causes another.
● Chained Exceptions help the programmer do this.
Assertions
● When implementing and debugging a class, it’s sometimes useful to state
conditions that should be true at a particular point in a method.
● Assertions help ensure a program’s validity by catching potential bugs and
identifying possible logic errors during development.
● Preconditions and postconditions are two types of assertions.
● Java includes two versions of the assert statement for validating assertions
programmatically.
● assert evaluates a boolean expression and, if false, throws an
AssertionError (a subclass of Error).
Assertions
● You use assertions primarily for debugging and identifying logic errors in an
application.
● You must explicitly enable assertions when executing a program
○ They reduce performance,
○ They are unnecessary for the program’s user.
● To enable assertions, use the java command’s -ea command-line option.
Finally
● Let’s work with a resource, so that we can release it in the finally block.
● A resource is an object that must be closed after the program is finished with
it.
● In the example that follows, the file is closed even when there is an I/O
exception.
1. import java.io.PrintWriter;
2. import java.io.FileWriter;
3. import java.io.IOException;
4.
5. public class MyResource {
6.
7. public static void main(String[] args) {
8. PrintWriter out = null;
9. try {
10. out = new PrintWriter(new FileWriter("OutFile.txt"));
11. out.println("Hello");
12. }
13. catch (IOException e) {
14. System.out.println(e);
15. }
16. finally {
17. if(out != null) {
18. out.close();
19. }
20. }
21. }
22. }
Try-with-resources
● The try-with-resources statement is a try statement that declares one or
more resources.
● Typically we place the resource release in a finally block to ensure that a
resource is released, regardless of whether there were exceptions when the
resource was used in the corresponding try block.
● The try-with-resources statement ensures that each resource is closed at
the end of the statement.
● Any object that implements java.lang.AutoCloseable, which includes all
objects which implement java.io.Closeable, can be used as a resource.
● Let’s rework the example in finally.
1. import java.io.PrintWriter;
2. import java.io.FileWriter;
3. import java.io.IOException;
4.
5. public class MyResource {
6.
7. public static void main(String[] args) {
8.
9. try (PrintWriter out = new PrintWriter(new FileWriter("OutFile.txt"))) {
10. out.println("Hello");
11. }
12. catch (IOException e) {
13. System.out.println(e);
14. }
15. }
16. }
Try-with-resources
● With try-with-resources, we add a parenthesis right after the try clause which
initializes a resource that implements the AutoCloseable interface.
References
● Oracle Java Documentation, Exceptions:
https://docs.oracle.com/javase/tutorial/essential/exceptions/index.html
● Course slides of the textbook ‘Java How to Program, 10/e (Early Objects),
Global Edition, Paul Deitel and Harvey Deitel, Pearson’
● Slides by Kaya Oğuz and Senem Kumova Metin