A successfully compiled program can still fail when it is run. Hopefully, such failures are rare occurrences.
The term exception is shorthand for the phrase "exceptional event." An exception is an event, which occurs during the execution of a program, that disrupts the normal flow of the program's instructions.
The client programmer can cause errors to occur by violating preconditions or making logic errors:
ListsomeIntegers = new ArrayList<Integer>(); int x = someIntegers.get(0);
Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 0, Size: 0 at java.util.ArrayList.rangeCheck(ArrayList.java:571) at java.util.ArrayList.get(ArrayList.java:349) at RangeEx.main(RangeEx.java:14)
The client programmer can cause errors to occur by violating preconditions or making logic errors:
String s = "hello"; String t = s.substring(6);
Exception in thread "main" java.lang.StringIndexOutOfBoundsException: String index out of range: -1 at java.lang.String.substring(String.java:1949) at java.lang.String.substring(String.java:1916) at StringEx.main(StringEx.java:9)
The client programmer can cause errors to occur by violating preconditions or making logic errors:
List<Integer> someIntegers = new Array<Integer>(); int sum = 0; for (Integer i : someIntegers) { sum += i; } int average = sum / someIntegers.size();
Exception in thread "main" java.lang.ArithmeticException: / by zero at ArithmeticEx.main(ArithmeticEx.java:15)
The client programmer can cause errors by failing to validate user input:
Enter a number: abc
Exception in thread "main" java.lang.NumberFormatException: For input string: "abc" at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65) at java.lang.Integer.parseInt(Integer.java:481) at java.lang.Integer.parseInt(Integer.java:514) at ParseEx.main(ParseEx.java:10)
The runtime environment that the program executes in can cause errors to occur.
String filename = "unreadable.txt"; Scanner fileInput = new Scanner(new File(filename));
Exception in thread "main" java.io.FileNotFoundException: unreadable.txt (Permission denied) at java.io.FileInputStream.open(Native Method) at java.io.FileInputStream.(FileInputStream.java:137) at java.util.Scanner. (Scanner.java:653) at FileEx.main(FileEx.java:9)
Similarly, if you try to write a file to a full hard drive
an IOException
will occur.
ListsomeIntegers = new ArrayList<Integer>(); int x = someIntegers.get(0);
What happens when RangeEx.main
invokes ArrayList.get
?
RangeEx.main
invokes ArrayList.get
ArrayList.get
invokes ArrayList.rangeCheck
ArrayList.rangeCheck
checks if the passed
index is valid
In this example, the index is not valid, so
an exception object is created inside
ArrayList.rangeCheck
.
We say that the method throws
an exception.
From the Java Tutorials: What Is an Exception?
The runtime system searches the call stack for a method that contains a block of code that can handle the exception. This block of code is called an exception handler.
The search begins with the method in which the error occurred and proceeds through the call stack in the reverse order in which the methods were called. When an appropriate handler is found, the runtime system passes the exception to the handler.
If no handler is found, the program terminates with an error message.
Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 0, Size: 0 at java.util.ArrayList.rangeCheck(ArrayList.java:571) at java.util.ArrayList.get(ArrayList.java:349) at RangeEx.main(RangeEx.java:14)
When the virtual machine detects an invalid operation, it throws an exception. Throwing an exception involves creating an exception object that contains information about the error. The exception object is then handed to the Java runtime system.
When the runtime system receives an exception object, it searches for a block of code called an exception handler that can handle the exception. If such a block of code is found, the exception handler is said to catch the exception.
Thus far, you have not written any exception handlers. If the Java runtime system cannot find an exception handler, the program terminates in an uncontrolled fashion (it crashes).
In Java, you use a try
block to
contain code that might throw an exception.
Immediately after the try
block
you use one or more catch
blocks to handle any exceptions thrown
by the try
block.
int num = 0; try { num = Integer.parseInt(someString); } catch (NumberFormatException ex) { output.println("not a number"); }
The Integer.parseInt
contract
tells you what kinds of exceptions it might
throw.
output.println("Enter a fraction");
String str = input.nextLine();
int slash = str.indexOf("/");
String left = str.substring(0, slash);
String right = str.substring(slash + 1);
int leftInt = Integer.parseInt(left);
int rightInt = Integer.parseInt(right);
int answer = leftInt / rightInt;
output.println("Quotient = " + answer);
All lines in red might cause an exception to be thrown.
try { output.println("Enter a fraction"); String str = input.nextLine(); int slash = str.indexOf("/"); String left = str.substring(0, slash); String right = str.substring(slash + 1); int leftInt = Integer.parseInt(left); int rightInt = Integer.parseInt(right); int answer = leftInt / rightInt; output.println("Quotient = " + answer); } catch (IndexOutOfBoundsException e) { output.println("No slash in input!"); }
try { output.println("Enter a fraction"); String str = input.nextLine(); int slash = str.indexOf("/"); String left = str.substring(0, slash); String right = str.substring(slash + 1); int leftInt = Integer.parseInt(left); int rightInt = Integer.parseInt(right); int answer = leftInt / rightInt; output.println("Quotient = " + answer); } catch (IndexOutOfBoundsException e) { output.println("No slash in input!"); } catch (NumberFormatException e) { output.println("Non-integer operands!"); }
try { output.println("Enter a fraction"); String str = input.nextLine(); int slash = str.indexOf("/"); String left = str.substring(0, slash); String right = str.substring(slash + 1); int leftInt = Integer.parseInt(left); int rightInt = Integer.parseInt(right); int answer = leftInt / rightInt; output.println("Quotient = " + answer); } catch (IndexOutOfBoundsException e) { output.println("No slash in input!"); } catch (NumberFormatException e) { output.println("Non-integer operands!"); } catch (ArithmeticException e) { output.println("Cannot divide by zero!"); } output.println("Done!");
Exceptions are exceptional events that disrupt the normal flow of a program.
An exception is thrown when a method hands an exception object to the runtime system; the exception object contains information about the error that caused the exception to be thrown.
The exception can be caught by an exception handler. An exception handler is a block of code that receives the exception object after it is thrown.
If there is no appropriate handler for the exception that was thrown, the program will stop execution in a largely uncontrolled fashion (it will crash).
Java uses objects to represent exceptions. Like all other Java
objects, the hierarchy is rooted at Object
:
Throwable
Throwable
is the superclass for all exception and error objects
in Java. Only objects that are substitutable for Throwable
may be thrown or caught.
Throwable
defines all of the methods for exceptions
and errors. In fact, if you look at the APIs for its subclasses,
you will find no new methods for the subclasses.
Throwable
defines several methods to access the information
about the error or exception. There are two you should know about for
CSE1020:
String getMessage()
Returns the detail message string of this throwable.
void printStackTrace()
Prints this throwable and all of the methods that were invoked
to cause the throw.
try { // throws IndexOutOfBoundsException "hello".substring(6); } catch (IndexOutOfBoundsException ex) { System.out.println(ex.getMessage()); }
The above code fragment prints:
String index out of range: -1
try { // throws IndexOutOfBoundsException "hello".substring(6); } catch (IndexOutOfBoundsException ex) { ex.printStackTrace(); }
The above code fragment prints:
java.lang.StringIndexOutOfBoundsException: String index out of range: -1 at java.lang.String.substring(String.java:1938) at java.lang.String.substring(String.java:1905) at Ex.main(Ex.java:7)
Error
An
Error
represents an exceptional condition that is
external to the application, and that the application usually cannot
anticipate or recover from. An example would be hardware failure
(hard drive failure, out of memory, etc.).
There usually isn't anything the programmer can do if an
Error
is thrown; thus, it usually makes no sense to
attempt to catch Error
s and the compiler does not
force the programmer to catch them.
Exception
An
Exception
object represents an unusual event
that is internal to the application (ie. something is wrong
with the Java code). There are two kinds of exceptions: checked
and unchecked.
All exceptions, except subclasses of Error
and RuntimeException
, are checked exceptions.
A checked exception is one that every well-written
program should anticipate and handle. The compiler forces
the programmer to write an exception handler for such exceptions
(or the programmer must indicate that their code throws
an exception). If the programmer does not provide a handler,
the compiler will issue a compilation error.
You have already seen one common example of a checked exception.
Whenever you previously created a Scanner
for a
File
object you
had to alter the header of your main
method:
public static void main(String[] args) throws IOException { // ... Scanner fileInput = new Scanner( new File(someFileName) ); }
If you look at the Scanner
API, you will see that
its constructor advertises that it throws a
FileNotFoundException
, which is a checked exception.
If someFileName
is the name of a non-existant file,
the constructor call will throw an exception that the programmer
must handle.
RuntimeException
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.
—The Java Tutorials: Exceptions
Runtime exceptions help the programmer find programming errors in their code by telling the programmer where the exception has occurred and the sequence of methods that caused the exception to occur.
RuntimeException
and all of its subclasses are
unchecked exceptions.
An unchecked exception is an exception that the compiler does not force the programmer to handle. Forcing the programmer to catch every possible runtime exception even for perfectly written code would be very tedious and would reduce the clarity of the program. Because unchecked exceptions are caused by programming errors, they can be eliminated by careful design, implementation, and debugging.
You have probably already encountered several different kinds of runtime exceptions:
output.println("Enter a string : "); String userInput = input.next(); // throws StringIndexOutOfBoundsException // if s.length() < 5 String s = userInput.substring(5); // throws NumberFormatException // if userInput is not an integer Integer i = Integer.parseInt(userInput); // throws ArithmeticException // if i == 0 int quotient = 5 / i;
try
The programmer must write a handler whenever a method
that throws a checked exception is invoked. The first step
in creating an exception handler is to place the code
that can cause an exception in a try
block:
public static void main(String[] args) { // ... try { Scanner fileInput = new Scanner( new File(someFileName) ); // read the file here } }
catch
The second step is to place the code that handles the exception
in a catch
block. The catch
block goes
directly after the try
block; no code can go between
the two blocks:
public static void main(String[] args) { // ... try { Scanner fileInput = new Scanner( new File(someFileName) ); // read the file here } catch (FileNotFoundException ex) { } }
The catch
block says what type of exception it
can handle, and it provides a name for the exception object.
In this example, the catch
block says that it can
handle exceptions of type FileNotFoundException
.
catch
The catch
block contains code that is only
run if the exception handler is invoked. In our example,
we might print a message to the user that the file
was not found:
public static void main(String[] args)
{
// ...
try
{
Scanner fileInput = new Scanner(
new File(someFileName)
);
// read the file here
}
catch (FileNotFoundException ex)
{
output.printf("File named %s was not found.",
someFileName);
}
}
Substitutability comes into play when you write an
exception handler. The catch
block:
catch (FileNotFoundException ex) { }
says that it can handle an exception of type
FileNotFoundException
or any exception that is
substitutable for FileNotFoundException
.
FileNotFoundException
is a subclass of
IOException
. The exception handler could have
been written as:
catch (IOException ex) { }
This catch
block will handle an exception of type
IOException
or any exception that is
substitutable for IOException
. This means that
it can handle FileNotFoundException
because
FileNotFoundException
is substitutable for
IOException
; unfortunately,
it also handles a lot
of other exceptions as well.
The most general catch
block that you can write is:
catch (Throwable ex) { }
which will catch any Error
or Exception
.
If you do this, you will have a hard time deciding what to do about
the exceptional event because all you know is that a
Throwable
event has occurred; it would be far more
useful if you knew exactly what kind of exception has occurred.
Finish reading Chapter 11 if you haven't done so already.