840 likes | 989 Views
Java 212: Exceptions Handling. Chapter Objectives. Learn what an exception is See how a try / catch block is used to handle exceptions Become aware of the hierarchy of exception classes Learn about checked and unchecked exceptions. Chapter Objectives (continued).
E N D
Java 212: Exceptions Handling
Chapter Objectives • Learn what an exception is • See how a try/catch block is used to handle exceptions • Become aware of the hierarchy of exception classes • Learn about checked and unchecked exceptions
Chapter Objectives (continued) • Learn how to handle exceptions within a program • Discover how to throw and rethrow an exception • Learn how to handle events in a program • A good reference: Sun’s Java Tutorial, Exceptions Chapter
Intro A major factor in good programming is having your code elegantly handle errors as they arise. Until this point, whenever errors have been generated, we have “handled” the error by simply allowing our programs to crash. Obviously this will not do in the real world. Though you won’t always be able to anticipate exact errors, you will frequently be able to anticipate certain categories of errors. For example, if you ask a user for some input, you might anticipate getting back a data type different from the type you require. Eg: You ask the user for their age and they enter a data-type other than int (e.g. “fifteen”) In this lecture we will talk about anticipating different types of errors (aka exceptions) and the appropriate ways of handlingthose exceptions.
Intro contd Being an object-oriented language, Java handles errors by generating special exception objectswhich are automatically instantiated for us behind the scenes when an error occurs. Java has a special construct called a ‘try’ block that we will now use whenever we workwith code that has the potential of generating particular types of errors (exceptions). Within a try/catch block, you have the option of “handling” the exception – but you are not required to. You can simply ignore the exception object and allow some other part of the program to “catch” it. Typically, this “other part of the program” refers to whichever statement invoked the method in which the error was generated. If no statement in the entire program catches the exception, the program ends abruptly and displays an exception message to the console.
* “Handling” an Exception - Overview KEY POINT: When an exception is generated, some code, somewhere must “handle” it. If the exception is never handled, the program will crash. Ideally, all of our code will be ready to handle all possible exceptions. If we wish to handle an exception, the error (exception) generating code is enclosed in something called a try block. We can handle the exception by following the try block with one or more catch blocks. If the code that generates the exception is not equipped to handle it (e.g. the code is not enclosed inside a ‘try/catch’ block), the “outer” level (i.e. whichever method called the method with the offending statement) is given the opportunity to handle the exception. If that outer method doesn’t handle it, then the next outer method will be given the opportunity. If Java makes it all the way back to main() and still never handles the exception, the program aborts. Sometimes we want to delegate the handling of an exception to an outer-level. In that case, we can include a ‘throws’ statement. More on this a little later.
Overview contd • Exceptions tend to be about problems that come up at run-time as opposed to compile-time. • The compiler will require that you deal with (“handle”) the exception. • Note that the compiler doesn’t care how you take care of (handle) the exception, it only cares that you are aware of the possibility that an exception can occur and intend todeal with it appropriately. • You’ll spend much more time handling exceptions than you will creating them. • Most types of exceptions have already been thought of by the people who created Java. These pre-existing exception classes are automagically instantiated when that type of exception occurs. • When you invoke a method that can generate an exception (e.g. a method to open a file), it is up to you to have code that can handle the FileNotFound exception that might occur. • An exception is always thrown back to the caller (the method that invoked whichever method generated the exception).
Example: Handling an Exception try { FileWriter fout = new FileWriter("testFile.txt"); fout.write("test file output"); fout.close(); } catch (IOException e) { System.out.println("Error opening file"); //may have other code here... } If you look at the API for the FileWriter class, you will see that the constructor method that we invoke above has the potential to generate an IOException. Because this exception can be generated, the compiler requires us to place the method call (i.e. the constructor) inside a try block.
Another example API for the FileReader constructor try { FileReader fin = new FileReader("inputFIle.txt"); //code that reads in and works with input } catch (FileNotFoundException e) { System.out.println(“Problem opening the requested file"); //might have code here to try and fix the problem... } This tells us that we must write code that can catch this exception. So…
The Point of all this: “Robust” Programming One of the most important objectives in programming is to write “fault-tolerant” or “robust” code. That is, we want to write programs in which problems may arise, but the program continues to run. Good exception handling is one of the more effective methods of making your code “fault-tolerant”, or, “robust”.
Separate Error Handling Logic from Program Logic Assuming that potential errors will not happen all that frequently, you don’t want your code to spend inordinate amounts of time doing error-checking operations. For example, it is bad form to spend time and space encasing code inside while-loops that look for bad input. Instead, it is better to allow your program’s logic (main purpose) to function as it should, but to enclose the code in a block that is ready to spring into action if and when problems do occur. In addition to improving performance, it also improves the clarity of your code. Check out this example from Sun’s Java Tutorial
Exception • Definition: an occurrence of an undesirable situation that can be detected during program execution (e.g. as opposed to at compile-time) • Examples • Division by zero • Trying to open an input file that does not exist • An array index that goes out of bounds
Java’s Mechanism of Exception Handling • When an exception occurs, an object is instantiated automatically by Java • The datatype of exception that is generated depends, not surprisingly, on the reason for the exception • Java provides numerous exception classes to effectively handle certain common exceptions such as division by zero, invalid input, and file not found • Example: Division by zero is an arithmetic error and is handled by the classArithmeticException • So, if a division by zero occurs, Java will automatically generate an object of type ArithmeticException.
Java’s Mechanism of Exception Handling (continued) • Another example: When a Scanner object is used to input data into a program, any invalid input errors (e.g. a String is entered as input to the method nextInt() ) an exception of type classInputMismatchException is generated • Note that we have NOT been required to place the nextInt() method call inside a try block. Why not??? • The reason is that some types of exceptions MUST be placed inside a try block, while others are optional. More on this shortly. • The InputMismatchException that could be generated by the nextInt() method is one of those ‘optional’ exceptions. Following is an example of placing the code inside a try block:
Scanner console = new Scanner(System.in); int age; try { System.out.print("How old are you? "); age = console.nextInt(); } catch (InputMismatchException e) { System.out.println("Error reading in input."); System.out.println("Setting age to -1"); age = -1; } System.out.println("You are " + age + " years old."); This way of doing things is much preferable to simply having the program crash on you. Later we will show examples of how you might loop until a valid integer is entered.
Don’t forget scope! • Any variables/objects, etc declared inside the try block is local to that block. When outside of the try block, that is, when it is done or if inside a catch block, those items will no longer be in scope. • So, you will often need to declare things before entering your try block. • See the previous example where console and age were declared before the try block.
See: ExceptionExample1.java • If an exception occurs, the program catches and then handles it. • You can have several different catch blocks. Which block gets executed will depend on which type of exception occurred. • Remember that when an exception is generated, Java automatically instantiates an object of that Exception type. You can invoke various methods on that object in order to find out more about it. • For example, all exception objects have a toString method that you can invoke to find out a little bit about that exception object.
try/catch/finally Block • Statements that might generate an exception are placed in a tryblock • The tryblock might also contain statements that should not be executed if an exception occurs • The tryblock is followed by zero or more catchblocks • A catch block is where the “handling” takes place, and for this reason, is also known as an exception handler • A catchblock specifies the type of exception it can catch and contains an exception handler • Every catch block must specify one (and only one) exception parameter • e.g. catch (NumberFormatException e)
try/catch/finally Block (contd) • The lastcatchblock may or may not be followed by a finallyblock • Another way of stating: Every try block must be followed by at least one catch block and/or a finally block • Any code contained in a finallyblock always executes, regardless of whether an exception actually occurs! • One exception to this rule: When the program exits early from a tryblock by calling the method System.exit(); • If a tryblock has no catchblock, then it must have the finallyblock
try/catch/finally Block (continued) You can include a catch block for every possible exception that might have been generated inside the try block. Optional.
try/catch/finally Block (contd) • If no exception is thrown in a tryblock, all catchblocks associated with the tryblock are ignored and program execution resumes after the last catchblock • If an exception is thrown in a tryblock, the remaining statements in the try block are ignored • - The program searches the catchblocks in the order in which they appear after the tryblock, and looks for an appropriate exception handler by examining the parameter for each catch block in turn
try/catch/finally Block (contd) • If the type of exception matches the parameter type in one of the catchblocks, the code of that catchblock executes and the remaining catchblocks after this catchblock are ignored • If there is a finallyblock after the last catchblock, the finallyblock always executes (i.e. regardless of whether an exception occured)
* finally Block * • The most common use for a finally block is to close resources that were created inside the try block. • Recall that resources take up memory! • For example, when you open various streams (FileReader, BufferedReader, FileWriter, etc) all of them are taking up resources. • The ideal place to close those streams is NOT at the end of your try block, since if an exception is generated, control will never get as far as that point. • Instead, close the resources in the finally block. • See FileIOExceptions.java
Order of catch Blocks • The heading of a catchblock specifies the type of exception it handles • A catchblock can catch either exceptions of a specific type • e.g. catch (ArithmeticException e) • or all types of exceptions • e.g. catch (Exception e) • However, the order can matter! Can you see why? • If you catch the Exception class first (as opposed to one of its subclasses), that block will always get executed. • See next slide
Order of catch Blocks (continued) A reference variable of a superclass type can point to an object of its subclass: • If in the heading of a catchblock you declare an exception using the classException, then that catchblock will catch all types of exceptions because the classException is the superclass of all exception classes • In a sequence of catchblocks, exceptions of a subclass type should be placed before exceptions of a superclass type • In other words, if you declare the superclass type first, the catch block containing the subclass type will never be executed
Parent Exception Class • All exception classes are inherited from class Exception which in turn, inherits from class Throwable • Most of the methods we invoke using our exception objects come from the class Throwable.
Java Exception Hierarchy It is not difficult to create your own exception classes by extending the class Exception. In the real world, most programmers rarely need to do so.
Java’s Exception Class • classException • Subclass of class Throwable • Superclass of classes designed to handle exceptions • There are many different types of exceptions: • I/O exceptions (e.g. for opening / closing files) • Number format exceptions • File not found exceptions • Array index out of bounds exceptions • Exceptions categorized into separate classes and contained in various packages
Some exceptions from the Scanner class API for nextInt() method
* Checked vs Unchecked Exceptions • Exceptions are classified into two types: Checked and Unchecked. If an exception is UN-checked, you are NOT required to place it inside a try/catch block and handle it. (Though you may choose to do so). • An unchecked exception is one that you are not required to provide for. For example, the nextInt() method of the Scanner class throws un-checked exceptions which is why we haven’t had to write code to hande them. • A checked exception is one that the compiler requires you to provide for; that is, you must notify Java that you are aware that this exception might occur and that somewhere in your code, you are going to handle it. Your compiler will refuse to compile if you don’t. For example, a FileNotFoundException is a checked exception, and is generated when you write code that is supposed to open a file for reading or writing. • The FileNotFound exception can be generated in many places, such as when you attempt to open a File for reading input. This is why you had to enclose that code inside a try block.
Checked Exception: “Catch or Declare” As was just mentioned, the compiler requires you to anticipate checked exceptions. It does so by enforcing a “catch or declare requirement” for checked exceptions. Option 1: Catch the exception: So far, we have handled checked exceptions by enclosing the code inside a try block and handling the code via a catch and/or finally block(s). Option 2: Declare via a ‘throws’ clause. The throws clause is part of your method signature. This clause indicates that the code inside your method may generate an exception, but that your method does not handle that exception (or handles it incompletely). We will discuss the throws clause shortly.
class RuntimeException • Exceptions that inherit from this class are the only kinds of exceptions that do NOT have be enclosed in a try block. • In other words, these are unchecked exceptions • e.g. ArrayIndexOutOfBounds • Therefore, all other types of exceptions (that are not subclasses of RuntimeException) do need to be enclosed in try blocks. • These are called checked exceptions. • Any code that you write that invokes a method that can generate an exception must be caught. If you neglect to do this, your program won’t compile.
Summary: Checked vs Unchecked • All exceptions that are not subclasses of RuntimeException must be checked (placed inside a try block) or thrown. • Class that are NOT of type RuntimeException are “checked” exceptions • Must be placed inside a try block • Subclasses of RuntimeException are unchecked exceptions. • Do not require a try block
Mnemonic: rUNtime = UNchecked!
Review: Checked Exceptions • Definition: any exception that can be recognized by the compiler (i.e. that the compiler requires you to anticipate). • All subclasses of Exception exceptfor subclasses of RuntimeException are Checked exceptions. • Examples • IOException • FileNotFoundException • For example, see the API for the constructor of the FileReader or PrintWriter classes) • So, if you are writing a method that has code that can generate a checked exception, you must either: • Place the code inside a try block OR • Throw the exception
Unchecked Exceptions • Exceptions that can only be recognized at “run-time” (when the program is being executed). For example, code that requires user input. • These must be checked for by programmer • All subclasses of RuntimeException are unchecked exceptions • Examples • Division by zero • Array index out of bounds • Hopefully this makes sense. When the program is being compiled, it may not be possible for the compiler to know if a division by zero will occur. Therefore, the compiler does not check for this exception at compile-time. • Example: See the parseInt() method of the Integer class from the API
* The classException and the Operator instanceof • Recall that a reference of a superclass type can point to objects of its subclass • You can determine if a reference variable points to an object using the operator instanceof • You can combine catch blocks using this facility
The classException and the Operator instanceof try { System.out.print("Enter the " + "dividend: "); dividend = console.nextInt(); System.out.println(); System.out.print("Enter the " + "divisor: "); divisor = console.nextInt(); System.out.println(); quotient = dividend / divisor; System.out.println("Quotient = " + quotient); } catch (Exception eRef) { if (eRef instanceof ArithmeticException) System.out.println("Exception “ + eRef.toString()); else if (eRef instanceof InputMismatchException) System.out.println("Exception " + eRef.toString()); }
* Throwing an Exception • Recall that one option for dealing with checked exceptions is to ‘declare’ the exception. This is done via the throws caluse. • This clause indicates that your method does not handle (or incompletely handles) the exception. • So where does the handling occur? One way is to handle the exception inside the method invoked the current (exception-generating) method. • However, this outer method can ‘rethrow’ the exception to its calling method. • See DivisionExceptionExampleWithThrows.java
Rethrowing an Exception • As you know, when an exception occurs in a tryblock, control immediately passes to one of the catchblocks. • Typically, a catchblock does one of the following: • Completely handles the exception (the examples we’ve been examining up to this point) • Throws the same exception for the calling (outer) environment to handle the exception • This can be useful if you have a lot of error code grouped in one place. You can throw the exception to the outer method where this error code was placed. • Partially processes the exception; in this case, the catchblock either re-throws the same exception or throws another exception for the calling environment to handle
Throwing = “Ducking” • For now, you can think of throwing an exception as “ducking” it. That is, you are saying that the current method “ducks” and is going to let the exception fly on by. • So who will catch the exception? Answer: Whichever method invoked the method that generated the exception. • Ultimately, however, the exception must be caught. If not, the program aborts.