CS 1301 * Ch 6, Handout 1

advertisement
CS 1302 – Ch 12 – Exception Handling
These notes will cover Sections 12.1-12.7
A good reference on exceptions: http://mindprod.com/jgloss/exception.html
Sections 12.1-12.2 – Exception Handling Overview
1. What is a run-time error? When you code tries to do something illegal, or just can’t do a run-time error
occurs, an exception is thrown, and your code stops.
2. Example 1: ArithmeticException – Divide by zero and others.
Code
int x=20, y=0;
int z=x/y;
Output
Exception in thread "main" java.lang.ArithmeticException: / by zero
3. Example 2: ArrayIndexOutOfBoundsException – Attempting to access an array element that doesn’t
exist
Code
int[] vals = new int[3];
vals[50]=100;
Output
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 50
4. Example 3: IndexOutOfBoundsException – An index of some sort is out of range.
Code
ArrayList<Integer> vals = new ArrayList<>();
int x = vals.get(50);
Output
Exception in thread "main" java.lang.IndexOutOfBoundsException: Index:
50, Size: 0
1
5. Example 4: StringIndexOutOfBoundsException – An index in a string is out of range.
Code
String dog = "Snoopy";
Character c = dog.charAt(33);
Output
Exception in thread "main" java.lang.StringIndexOutOfBoundsException:
String index out of range: 33
6. Example 5: NullPointerException – Attempting to use null when an object was excepted.
Code
A a = null;
int x = 1;
if( x > 5 )
a = new A();
a.m();
class A {
public void m() {}
}
Output
Exception in thread "main" java.lang.NullPointerException
7. Example 6: ClassCastException – Trying to cast an object to something it isn’t
Code
Dog dog = new Dog();
WolfDog wolfDog = (WolfDog)dog;
class Dog {}
class WolfDog extends Dog {}
Output
Exception in thread "main" java.lang.ClassCastException: errors1.Dog
cannot be cast to errors1.WolfDog
2
8. The exception hierarchy is shown below:
9. Handling Errors – For robust software systems we must try to handle run-time errors so that the
program does not stop unexpectedly. In general, there are two techniques:
1. Anticipate and trap sources of error before they result in a run-time error.
2. Detecting and handling run-time errors after they occur.
3
10. Anticipating Errors – Some errors we may be able to anticipate. For instance, suppose we have a
method to divide two numbers. If the denominator is 0, then a run-time error will result and the
program will halt. Thus, it is easy to trap that source of error with an if statement:
class A2 {
int z=0;
public void divide1( int x, int y ) {
if( y != 0 )
z = x/y;
else
System.out.println( "y can't be 0." );
}
}
However, this solution is poor because: (a) displaying a message to the console is practically useless in
a gui application, (b) the calling method is not informed of the error. It has no way of knowing weather
the calculation was successful or not. Similarly, the method below is not very good either. A better
solution would be to inform the calling method with an indicator of whether the method was
successful:
public boolean divide3( int x, int y ) {
if( y != 0 ) {
z = x/y;
return true;
}
else
return false;
}
11. It is hard to anticipate every single situation that could cause a run-time error. For example, the
method below could fail because: (a) an array index reference that is too big (or small), (b) the value of
the denominator could be zero, (c) the vals array might not have been created.
public void divide4( int x, int y ) {
z = vals[x]/vals[y];
}
4
12. Detecting Run-Time Errors – Hopefully it is clear, that even if we had an abundance of time, it would
be nearly impossible to anticipate every possibly source of error. Thus, run-time errors are going to
occur. In Java, we can use a try/catch block to detect run-time errors immediately after they occur and
allow us to provide code to keep the code running, inform the user, send an error report, correct the
error, etc.
try {
// code that might fail
}
catch( Exception e ) {
// code to recover from failure
}
The idea is to surround a method call that may fail (in general, any piece of code that may fail) with a
try/catch block. Thus, the JVM tries to execute the method and if it fails it catches the exception that is
thrown (running the code in that block), instead of terminating the program.
13. Example – the divide4 method below can fail in multiple ways as stated above. For the hard-coded
main, we see that the method will fail due to an index out of bounds.
5
The path of execution for a successful call to divide4 is shown below.
14. Example – Consider the (pseudo) code below and assume that statement2 is subject to run-time
failure.
Code
statement1;
try {
statement2;
statement3;
}
catch( Exception e
) {
statement4;
}
statement5;
Statements that execute when:
a. statement2 fails:
1, 4, 5
b. statement2 succeeds: 1, 2, 3, 5
6
15. RuntimeException – The java.lang package defines the Exception class of which RuntimeException is a
subclass. Two of its important methods are: toString and printStackTrace.
a. toString – The toString method gives the actual class of the Exception and sometimes some
additional information. For example:
java.lang.ArrayIndexOutOfBoundsException: 4
where the “4” is the index that was out of bounds.
b. printStackTrace –The printStackTrace is much more useful because it displays the stack frames that
exist when the error occurs. Thus, in the example below we can see that the error occurred on line
92 which was called from line 27.
java.lang.ArrayIndexOutOfBoundsException: 4
at ch14_exceptions.A2.divide4(FirstTryCatch.java:92)
at ch14_exceptions.FirstTryCatch.main(FirstTryCatch.java:27)
16. Creating and throw an Exception – Another approach to error detection is to create and throw our
own exceptions. In the example below, the method detects an error, creates, and throws an exception:
class A {
public void m1( int x, int y ) {
if( y == 0 )
throw new ArithmeticException("y can't be 0.");
int z = x/y;
}
}
Which can be caught with this code:
try {
A a = new A();
a.m1(4,0);
}
catch( ArithmenticException e ) {
System.out.println( e );
}
Output: java.lang.ArithmeticException: y can't be 0.
7
Sections 12.3 – Exception Types
1. Exception Hierarchy – The Exception hierarchy defined in the API is:
a. The Exception class describes errors caused by your program and external circumstances. These
errors can be caught and handled by your program.
b. RuntimeException is caused by programming errors, such as bad casting, accessing an out-ofbounds array, and numeric errors.
c. System errors are thrown by JVM and represented in the Error class. The Error class describes
internal system errors. Such errors rarely occur. If one does, there is little you can do beyond
notifying the user and trying to terminate the program gracefully.
2. Unchecked Exceptions – RuntimeException, Error and their subclasses are known as unchecked
exceptions. This means that these types of errors can and will occur, but you do not have to catch
them, e.g try/catch is not required. All other exceptions are known as checked exceptions, meaning
that the compiler forces the programmer to check and deal with the exceptions. For instance, when
you attempt to read from a text file, it is possible to throw an IOException which must be caught.
8
Sections 12.4 – More on Exception Handling
1. Declaring an Exception – A method that throws an exception must either catch it, or, as in the example
below, declare the exception which in turn requires the calling method to catch it.
declare exception
method1() {
method2() throws Exception {
try {
invoke method2;
}
catch (Exception ex) {
Process exception;
}
catch exception
if (an error occurs) {
throw new Exception();
}
throw exception
}
}
2. Declaring a Checked Exception – Every method must state the types of checked exceptions it might
throw. This is known as declaring exceptions.
public void myMethod() throws IOException
public void myMethod() throws IOException, OtherException
3. Catching Multiple Exceptions – In general, you can catch multiple types of exceptions by supplying
multiple catch blocks. However, only one catch block will activate, the first one that matches (we’ll see
more on this later). However, a catch block for a subclass Exception must occur before a catch block for
a superclass Exception. Thus, exceptions are caught from most specific to most general.
try {
statements; // Statements that may throw exceptions
}
catch (Exception1 exVar1) {
handler for exception1;
}
catch (Exception2 exVar2) {
handler for exception2;
}
...
catch (ExceptionN exVar3) {
handler for exceptionN;
}
9
4. Example – Consider the divide method below which can fail in multiple ways.
class A3
{
int[] vals;
public A3(int[] vals) {
this.vals = vals;
}
public int divide( int x, int y ) {
if( x==3 )
throw new Exception("General Exception");
return vals[x]/vals[y];
}
}
Next, consider this main. As we see, the first catch block will be activated.
public static void main(String[] args) {
A3 a = new A3( new int[] {2,4,0,6} );
try {
(a) a.divide(1,2); (b) a.divide(4,2); (c) a.divide(3,1);
}
catch ( IndexOutOfBoundsException e ) {
System.out.println( "Caught IndexOutOfBoundsException: " + e );
}
catch ( ArithmeticException e ) {
System.out.println( "Caught ArithmeticException: " + e );
}
catch ( RuntimeException e ) {
System.out.println( "Caught RuntimeException: " + e );
}
catch ( Exception e ) {
System.out.println( "Caught Exception: " + e );
}
}
Output:
(a) a.divide(1,2); - Caught ArithmeticException
(b) a.divide(4,2); - Caught IndexOutOfBoundsException
(c) a.divide(3,1); - Caught Exception
5. Example – Modify the try block above with:
A3 a = new A3( null ); // Null pointer exception
try {
a.divide(1,2);
}
In this case a RuntimeException is caught, but the actual exception is NullPointerException.
10
Sections 12.5 – The finally Clause
1. try/catch/finally – You can add a finally block to a try/catch to make it try/catch/finally. The finally
block of code is always executed.
try{
Statements
}
catch (Exception e {
statements
}
finally {
statements
}
2. Example
11
Output:
ÏÏ1
ÏÏ2
ÏÏ4
ÏÏ5
ÏÏ
ÏÏ1
ÏÏ3
ÏÏ4
ÏÏ5
3. Notice that “4” and “5” are printed no matter what the case. So, how is finally adding anything? We’ll
see in an example in Section 12.7 where we rethrow an excetion.
Sections 12.6 – When to Use Exceptions
1. When to throw an Exception: Suppose an exception can occur in a method you have written. If you
want the exception to be processed by its caller, you should create an exception object and throw it. If
you can handle the exception in the method where it occurs, there is no need to throw it. When should
you use the try-catch block in the code? You should use it to deal with unexpected error conditions. Do
not use it to deal with simple, expected situations.
2. Example: This code:
try {
System.out.println(refVar.toString());
}
catch (NullPointerException ex) {
System.out.println("refVar is null");
}
Is better replaced with this code:
if (refVar != null)
System.out.println(refVar.toString());
else
System.out.println("refVar is null");
12
Sections 12.7 – Rethrowing Exceptions
1. Example – Modify the example from Section 12.5 to re-throw the exception once it is caught in the
method. Then, modify main to use a try/catch. In this case a different path is followed when an error
occurs, “5” is not printed. If the throw was replaced by a return, the flow would be the same.
Output:
ÏÏ1
3
4
6
13
2. Example – Illustrates a return statement, and finally.
public class RethrowReturn {
public static void main(String[] args) {
try {
A a = new A();
int z = a.divide(2, 3);
//
//
// prints: 1,3,4
int z = a.divide(3, 2); // prints: 5,2,3,4
int z = a.divide(20, 2); // prints: 3
if( z > 4 )
return;
System.out.println( "1" );
}
catch ( Exception e ) {
System.out.println( "2" );
}
Finally {
System.out.println( "3" );
}
System.out.println( "4" );
}
}
class A
{
public int divide( int x, int y ) throws Exception {
if( x==3 ) {
System.out.println( "5" );
throw new Exception("3's a funny number");
}
return x/y;
}
}
14
3. Example – These are two methods from a real system. The first method is called to open a connection
to a server and establish input and output communication (socket) channels.
final public void openConnection() throws IOException {
// Do not do anything if the connection is already open
if(isConnected())
return;
//Create the sockets and the data streams
try {
clientSocket= new Socket(host, port);
output = new ObjectOutputStream(clientSocket.getOutputStream());
input = new ObjectInputStream(clientSocket.getInputStream());
}
catch (IOException ex) {
// All three of the above must be closed when there is a failure
// to create any of them
try {
closeAll();
}
catch (Exception exc) { }
throw ex; // Rethrow the exception.
}
clientReader = new Thread(this); //Create the data reader thread
readyToStop = false;
clientReader.start(); //Start the thread
}
private void closeAll() throws IOException {
try {
//Close the socket
if (clientSocket != null)
clientSocket.close();
//Close the output stream
if (output != null)
output.close();
//Close the input stream
if (input != null)
input.close();
}
catch (Exception exc) {
throw exc;
finally {
// Set the streams and the sockets to NULL no matter what
// Doing so allows, but does not require, any finalizers
// of these objects to reclaim system resources if and
// when they are garbage collected.
output = null;
input = null;
clientSocket = null;
}
}
15
4. Example – In the examples below, suppose A, B, C, D, E are blocks of code and that A might throw an
exception.
Code
try{
A
B
Scenario
A doesn’t throw exception
A throws exception
A & C throw exceptions
Blocks Executed
A, B, D, E
A, C, D, E
A, C, D
Scenario
A doesn’t throw exception
A throws exception
Blocks Executed
A, B, D, E
A, C, D
}
catch(Exception e) }
C
}
Finally{
D
}
E
Code
try{
A
B
}
catch(Exception e) }
C
throw e // or return
}
Finally{
D
}
E
16
Download