Why exception handling in java ?
Improved error recovery is one of the most powerful ways that you can increase the robustness of your code. Error recovery is a fundamental concern for every program you write, but it's especially important in java, where one of the primary goals is to create program components for others to use.
The goals for exception handling in java are to simplify the creation of large, reliable programs using less code than currently possible, and to do with more confidence that your application does not have an unhandled error.
What is exception handling in java ?
An exceptional condition is a problem that prevents the continuation of the current method. With an exceptional condition, you cannot continue processing because you don't have the information necessary to deal with the problem in the current context. All you can do is jump out of the current context and relegate that problem to a higher context. This is what happen when you throw an exception.
When you throw an exception, several things happens. First the exception object is created in the same way that any java object is created : on the heap, with new. Then the current path of execution is stopped and the reference for the exception object is ejected from the current context.
At this point the exception-handling mechanism takes over and begins to look for an appropriate place to continue executing the program.
This appropriate place is the exception handler, whose job is to recover from the problem so the program can either try another task or just continue.
One of the most important aspects of exceptions is that if something bad happens, they don't allow a program to continue along its ordinary path. This has been a real problem in language like C and C++; especially C, which had no way to force a program to stop going down a path if a problem occurred, So it was possible to ignore problems for a long time and get into a completely inappropriate state.
Exceptions allow you to force the program to stop and tell you what went wrong, or force the program to deal with the problem and return to a stable state.
In a nutshell we can define exception as :
Exception are exceptional or abnormal conditions of the program that break the normal execution / flow of your program.
Let's look at Exception hierarchy
Exception : It is the basic type that can be thrown from any of the standard java library class methods and runtime accidents. So java programmer usually extends Exception to make their custom exception.
Error : Represent compile-time and system errors that you don't worry about catching (except in very special cases).
RuntimeException : There is a group of exception types that are in this category. they are always thrown automatically by Java and you don't need to include them in your exception specifications. You never need to write an exception specification saying that a method might throw a RuntimeException or any type inherited from RuntimeException because they are unchecked exceptions.
public class NeverCaught{
static void display(){
throw new RuntimeException("from display");
}
static void test(){
display();
}
public static void main(String args[]){
test();
}
}
You can see that RuntimeException or anything inherited from it is a special case, since the compiler doesn't require an exception specification for these types. If a RuntimeException occurred and not caught then it's propagated to main then JVM called printStackTrace() for that exception and program exits.
Keep in mind that only exception of type RuntimeException and it's subclasses can be ignored in your coding, since the compiler carefully enforces the handling of all checked exceptions.
The RuntimeException represents a programming error, which is
1. An error you can not anticipate. For example, a null reference that is outside of your control.
2. An error you, as a programmer, should have checked for in your code.
Exception Arguments
As any object in java, we also create exceptions on the heap using new, which allocates storage and calls a constructor. There are two constructors in all standard exceptions.
1. The first is the default constructor.
2. The second takes a string arguments so that you can place information in the exception.
Example : throw new NullPointerException(" You are pointing null reference ");
This string can later be extracted using various methods of exception class.
The keyword throw produce a number of interesting results. after creating an exception object with new, we give the resulting reference to throw, The exception object returned from the method even though that exception object type isn't normally what the method is design to return.
You end up in an appropriate exception handler that might be far away - many levels on the call stack from where the exception was thrown.
Catching an Exception
To see how an exception is caught, let's first understand the concept of a guarded region.
Guarded region : This is a section of code that might produce exceptions and is followed by the code to handle those exceptions.
The try block
If you are inside a method and you throw an exception or another method that you call within this method throws an exception, that method will exit in the process of throwing.
If you don't want a throw to exit the method, you can set up a special block within that method to capture the exception. This is called the try block.
try block is a scope preceded by the keyword try :
try {
// Code that might generate exception.
}
With Exception handling you put everything that can generate exception in try block and capture all the exceptions in one place.
This means your code is much easier to write and read because the goal of the code is not mess up with error checking.
Exception handler in java
The thrown exception must end up someplace. This place is exception handler, and there's one for every exception type you want to catch. Exception handlers immediately follow the try block and are denoted by the keyword catch.
try{
// Code that might generate exception
} catch( Type1 id1 ) {
// Handle exception of Type1
} catch( Type2 id2 ) {
// Handle Exception of Type2
} catch( Type3 id3 ) {
// Handle Exception of Type3
}
Each catch clause (exception handler) is like a little method that takes one and only one argument of a particular type. The Identifier ( id1, id2, id3 ) can be used inside the handler, just like a method argument.
The handlers must appear directly after the try block. If an exception is thrown, the exception-handling mechanism goes hunting for the first handler with an argument that matches the type of the exception. Then it enters that catch clause, and the exception is considered handled. The search for the handlers stops once the catch clause is finished. Only the matching catch block executes.
Within the try block, a number of different method calls might generate same exception, but you need only one handler.
Termination Vs. Resumption
There are two basic models in exception-handling theory.
Java supports termination, in which you assume that the error is so critical that there is no way to get back to where the exception occurred.
Java supports termination, in which you assume that the error is so critical that there is no way to get back to where the exception occurred.
The alternative is called resumption. It means that the exception handler is expected to do something to rectify the situation, and then failing method is retried, presuming success the second time.
If you want resumption like behaviour in java, don't throw an exception when you encounter an error. Although resumption sounds attractive at first, it isn't quite so useful in practice.
Creating your own exceptions / Custom Exception handling in java
The Java exception hierarchy can't foresee all the exception you might want to report, so you can create your own exception to denote a special problem that your application / library might encounter.
To create your own exception class, you must inherit from an existing Exception class. The most trivial way to create a new type of exception is just to let the compiler create the default constructor for you, so it requires almost no code at all.
Creating Custom Exception
public class MyFirstCustomException extends Exception {}
The compiler creates a default constructor, which automatically calls the base-class default constructor.
Now using your own custom exception.
public class DemoExceptions {
public void printException() throws MyFirstCustomException {
System.out.println(" throwring MyFirstCustomException ");
throw new MyFistCustomException();
}
public static void main(String args[] ) {
DemoExceptions demoExceptions = new DemoExceptions();
try {
demoExceptions.printException();
} catch ( MyFirstCustomException e ) {
System.out.println(" Exception Caught!");
}
}
}
Here you can also send error output to standard error stream by writing to System.err. This is usually a better place to send error information than System.out
You can also create a exception class that has a constructor with a String argument.
public class MyException extends Exception {
public MyException () {}
public MyException ( String msg ) {
super(msg);
}
}
In your custom exception there are two constructor that define the way MyException object is created. In the second constructor, the base-class constructor with a String argument is explicitly invoked by using the super keyword.
Using custom exception with argument.
public class DemoException2 {
public void printException() throws MyException {
System.out.println(" Throwing my exception ");
throw new MyException ();
}
public void printExceptionWitchArg() throws MyException {
System.out.println( " Throwing my excpeton with argument ");
throw new MyException (" Exception Occurred in printExceptionWitchArg ");
}
public static void main(String args[] ){
DemoException2 demoException2 = new DemoException2();
try{
demoException2.printException();
} catch(MyException e){
e.printStackTrace();
}
try{
demoException2.printExceptionWitchArg();
} catch(MyException e){
e.printStackTrace();
}
}
}
In the handler / catch block one of the throwable method printStackTrace() is called. This produce information about sequence of methods that where called to get to the point where the exception happened.
Here the information is send to the System.out and automatically captured and displayed in the output console.
The Exception specification
In java, you are encouraged to inform the client programmer, who calls your method, of the exceptions that might be thrown from your method. This is required, because the caller can know exactly what code to write to catch all potential exceptions.
To prevent this from being a problem, Java provide syntax (and forces you to use that syntax ) to allow you to politely tell the client programmer what exceptions this method throws, so client programmer can handle them. This is the exception specification and it's part of the method declaration, appearing after the argument list.
The exception specification uses an additional keyword, throws, followed by a list of all the potential exception type.
So your method definition might look like this:
public void printName() throws Type1, Type2, Type3 {
// code here
}
However, if you say
public void printName() {
// code here
}
It means that no exceptions are thrown from the method except for the RuntimeException, which can be thrown anywhere without exception specifications.
If the code within your method causes exceptions, but your method doesn't handle them, the compiler will detect this and tell you that you must either handle the exception or indicate with an exception specification that it may be thrown from your method. By enforcing exception specification Java guarantees that a certain level of exception correctness can be ensured at compile time.
Checked Exception : Exceptions that are checked and enforced at compile time are called checked exceptions.
Catching any exception
It is possible to create a handler that catches any type of exceptions. You do this by catching the base-class exception type Exception (there are other types of base exceptions, but Exception is the base that's pertinent to virtually all programming activities)
catch (Exception e) {
System.out.println( " Caught an exception ");
}
This will catch any exception, so if you use it you have to put it at the end of your list of handlers/catch block otherwise your code will not compile.
Since the Exception class is the base of all the exception classes that are important to programmer, you don't get much specific information about the exception, but you can call the methods that come from it's base type Throwable :
String getMessage()
String getLocalizedMessage()
Get the detail message, or a message adjusted for the particular locale.
String toString()
Returns a short description of the Throwable, including the detail message if there is one.
void printStackTrace()
void printStackTrace(PrintStream)
void printStackTrace(PrintWriter)
Prints the Throwable and the Throwable's call stack trace. The call stack shows the sequence of method calls that brought you to the point at which the exceptions was thrown. The first print the standard error, the second and third print to a stream of your choice.
Throwable fillInStackrace()
Records information within the Throwable object about the current state of the stack frames. Useful when an application is re-throwing an error or exception
Example to use of basic Exception methods.
public class DemoException3 {
public static void main(String args[]){
try{
throw new Exception(" Exception ");
} catch(Exception e){
System.out.println(e.getMessage());
System.out.println(e.getLocalizedMessage());
System.out.println(e) // internally call to toString() method
e.printStackTrace();
e.printStackTrace(System.out);
}
}
}
The Stack Trace
The information provided by printStackTrace() can also be accessed directly using getStackTrace(). This method returns an array of stack trace elements, each representing one stack frame. Element zero is the top of the stack, and is the last method invocation in the sequence. The last element of the array and the bottom of the stack is the first method invocation in the sequence.
Example :
public class DemoException4 {
static void demo() {
try{
throw new Exception ();
} catch(Exception e){
for(StackTraceElement ele : e.getStackTrace()){
System.out.println(ele.getMethodName());
}
}
}
static void test(){
demo();
}
static void temp(){
test();
}
public static void main(String args[]){
demo();
System.out.println("--------------");
test();
System.out.println("--------------");
temp();
}
}
output :
demo
main
------------
demo
test
main
------------
demo
test
temp
main
There we have just print the method name, but you can also print the entire StackTraceElement, which contains additional information.
Re-throwing an exception
Sometimes you want to re-throw the exception that you just caught, particularly when you use Exception to catch any exception. since you already have the reference to the current exception, you can simply re-throw that reference.
catch(Exception e){
System.out.println(" an exception is going to be re-thrown ");
throw e;
}
Re-throwing an exception causes it to go to the exception handlers in the next higher context. Any further catch clause for the same try block are still ignored.
In addition, everything about the exception object is preserved, so the handler at the higher context that catches the specific exception type can extract all the information from that object.
If you simply re-throw the current exception, and the information that you print about that exception in printStackTrace() will pertain to the exception's origin, not the place where you re-throw it.
If you want to install new stack trace information, you can do by calling fillInStackTrace(), which return a Throwable object that it creates by stuffing the current stack information into the old exception object.
public class DemoException5{
public static void test() throws Exception{
System.out.println(" Originating exception in test ");
throw new Exception (" throw from test () ");
}
public static void demo() throws Exception {
try{
test();
} catch(Exception e){
System.out.println("Inside demo () printing stack trace ");
e.printStackTrace(System.out);
throw e;
}
}
public static void temp() throws Exception {
try{
demo();
}catch(Exception e){
System.out.println("Inside temp () printing stack trace ");
System.out.println("Inside temp () printing stack trace ");
e.printStackTrace(System.out);
throw (Exception) e.fillInStackTrace();
}
}
public static void main(String args[]){
try{
temp();
}catch(Exception e){
System.out.println("main : printStackTrace ");
e.printStackTrace(System.out);
}
try{
test();
}catch(Exception e){
System.out.println("main : printStackTrace ");
e.printStackTrace(System.out);
}
}
}
Output :
Originating exception in test
Inside demo () printing stack trace
java.lang.Exception: throw from test ()
at blog.DemoException5.test(DemoException5.java:7)
at blog.DemoException5.demo(DemoException5.java:13)
at blog.DemoException5.temp(DemoException5.java:23)
at blog.DemoException5.main(DemoException5.java:33)
Inside temp () printing stack trace
java.lang.Exception: throw from test ()
at blog.DemoException5.test(DemoException5.java:7)
at blog.DemoException5.demo(DemoException5.java:13)
at blog.DemoException5.temp(DemoException5.java:23)
at blog.DemoException5.main(DemoException5.java:33)
main : printStackTrace
java.lang.Exception: throw from test ()
at blog.DemoException5.temp(DemoException5.java:27)
at blog.DemoException5.main(DemoException5.java:33)
***********
Originating exception in test
main : printStackTrace
java.lang.Exception: throw from test ()
at blog.DemoException5.test(DemoException5.java:7)
at blog.DemoException5.main(DemoException5.java:40)
The line where fillInStackTrace() is called becomes the new point of origin of the exception.
It is also possible to re-throw a different exception from the one you caught. If you do this, you get the similar effect as when you use fillInStackTrace() but the information about the original site of the exception is lost when you re-throw exception using the new throw
public class OneException extends Exception {
public OneException(String msg) {
super(msg);
}
}
public class TwoException extends Exception {
public TwoException( String msg){
super(msg);
}
}
public class ReThrowException{
public static void display() throws OneException {
System.out.println(" Originating exception in display ");
throw new OneException("thrown from display");
}
public static void main (String args[]){
try{
try{
display();
}catch(OneException e){
System.out.println("Caught in inner try");
e.printStackTrace(System.out);
throw new TwoException (" from inner try ");
}
} catch(TwoException e){
System.out.println("Caught in outer try");
e.printStackTrace(System.out);
}
}
}
Output :
Originating exception in display
Caught in inner try
blog.OneException: thrown from display
at blog.ReThrowException.display(ReThrowException.java:20)
at blog.ReThrowException.main(ReThrowException.java:26)
Caught in outer try
blog.TwoException: from inner try
at blog.ReThrowException.main(ReThrowException.java:30)
The final exception knows only that it came from the inner try block and not from display().
Exception Chaining
Often you want to catch one exception and throw another, but still keep the information about the originating exception- this is called exception chaining.
Prior to JDK 1.4, programmer had to write their own code to preserve the original exception information, but now all Throwable subclasses have the option to take a cause object in their constructor. The cause is intended to be the originating exception, and by passing it in you maintain the stack trace back to its origin, even though you're creating and throwing a new exception.
Often you want to catch one exception and throw another, but still keep the information about the originating exception- this is called exception chaining.
Prior to JDK 1.4, programmer had to write their own code to preserve the original exception information, but now all Throwable subclasses have the option to take a cause object in their constructor. The cause is intended to be the originating exception, and by passing it in you maintain the stack trace back to its origin, even though you're creating and throwing a new exception.
The only Throwable subclasses that provide the cause argument in the constructor are the three fundamental exception classes Error (used by the JVM to report system error ), Exception , and RuntimeException.
If you want to chain any other exception types, you do it through the initCause() method rather than the constructor.
Performing cleanup with finally
There is often some piece of code that you want to execute whether or not an exception is thrown within a try block. This usually pertains to some operation other than memory recovery (since that's taken care of by the garbage collector). To achieve this effect, you use a finally clause at the end of all the exception handlers.
try{
// the guarded region
// Might throw A, B, or C
} catch( A a){
// handler for situation A
} catch( B b){
// handler for situation B
} catch( C c){
// handler for situation C
} finally {
// Activity that has to be happen every time.
}
To demonstrate that the finally clause always run, try this program.
// The finally clause is always executed.
public class FinallyAlwaysWork{
static int count = 0;
public static void main( String args[]){
while( true ){
try{
if( count++ == 0 ){
throw new Exception();
}
System.out.println("No Exception");
} catch ( Exception e ){
System.out.println("Exception");
} finally {
System.out.println("In finaly clause");
if(count == 2 ){
break; // out of the loop
}
}
}
}
}
Output :
From the output you can see the finally clause is executed whether or not an exception is thrown.
What is finally for ?
In a language without garbage collection without automatic destructor calls, finally is important because it allow the programmer to guarantee the release of memory regardless of what happens in try block. But Java has garbage collection, so releasing memory is virtually never a problem.
So when do you need to use finally in Java ?
The finally clause is necessary when you need to set something other then memory back to its original state. This is some kind of cleanup like an open file or network connection.
public class Switch {
private boolean state = false;
public boolean read () {
return state;
}
pubic void on() {
state = true;
System.out.println(this);
}
public void off() {
state = false;
System.out.println(this);
}
public String toString(){
return state ? "On" : "Off";
}
}
public class OnOffException1 extends Exception {}
public class OnOffException2 extends Exception {}
public class OnOffSwitch {
private static Switch sw = new Switch();
public static void display() throw OnOffException1, OnOffException2 {
}
public static void main(String args[]){
try {
sw.on();
display(); // can throw exception
sw.off();
} catch(OnOffException1 e){
System.out.println("OnOffException1");
sw.off();
}catch (OnOffException2 e){
System.out.println("OnOffException2");
sw.off();
}
}
}
Output :
on
off
The goal here is to make sure that the switch is off when main() is completed, so sw.off() is placed at the end of the try block and at the end of each exception handler. But it's possible that an exception might be thrown that isn't caught here, so sw.off() would be missed. However, with finally you can place the cleanup code from a try block in just one place.
Finally Guarantees cleanup.
public class WithFinally {
Static Switch sw = new Switch();
try{
sw.on();
OnOffSwitch.display(); // Code that can throw exception....
} catch(OnOffException1 e){
System.out.println("OnOffException1");
} catch ( OnOffException2 e){
System.out.println("OnOffException2");
} finally {
sw.off();
}
}
Output :
On
Off
Here the sw.off() has been moved to just one place, where it's guaranteed to run no matter what happens.
Even in cases in which the exception is not caught in the current set of catch finally will be executed before the exception-handling mechanism continues it's search for a handler at the next higher level.
public class AlwaysFinally {
public static void main( String args[]){
System.out.println("Entering into fist try block ");
try{
System.out.println("Entering into second try block ");
try{
throw new Exception();
} finally {
System.out.println("finally in second try blcok");
}
} catch(Exception e){
System.out.println("Caught Exception in first try block ");
} finally {
System.out.println("Finally in the fist try block");
}
}
}
output :
The finally statement will also be executed in situation in which break and continue statement are involved. Note that along with the labeled break and labeled continue, finally eliminates the need for a goto statement in Java.
Using finally during return
Because a finally clause is always executed, it's possible to return from multiple points within a method and still guarantee that important cleanup will be performed.
public class MultipleReturns {
public static void display(int i){
System.out.println("Initinilization of resources")
try{
System.out.println("point 1");
if(i == 1)
return;
System.out.println("point 2");
if(i == 2)
return;
System.out.println("point 2");
if(i == 3)
return;
System.out.println("End");
return;
} finally {
System.out.println("Performing clean up");
}
}
public static void main(String args[]){
for(int i = 1; i <= 4; i++){
display(i);
}
}
}
output :
So you see from the output that it doesn't matter where you return from, finally block will always be executed.
Pitfall : The lost exception
Unfortunately, there's a flow in Java's exception implementation, Although exceptions are an indication of a crisis in your program and should never be ignored, it's possible for an exception to simply be lost.
Let's see
public class VeryImportantException extends Exception {
public String toString(){
return "A very important exception! ";
}
}
public class TrivialException extends Exception {
public Strig toString(){
return "A trivial exception";
}
}
public class LostException{
public void crate() throws VeryImportantException {
throw new VeryImportantException();
}
public void dispose() throws TrivialException {
throw new TrivialException();
}
public static void main(String arge[]){
try{
LostException lostExcep = new LostException ();
try{
lostExcep.create();
} finally{
lostExcep.dispose();
}
} catch( Exception e){
System.out.println(e);
}
}
}
Output :
You can see from the output that there is no evidence of the VeryImportantException, which is simply replaced by the TrivialException in the finally clause.
This is a rather serious pitfall, since it means that an exception can be completely lost.
An even simpler way to lose an exception is just to return from inside a finally clause
public class ExceptionSilencer {
public static void main(String args[]){
try{
throw new VeryImportantException();
} finally {
// using return inside the finally block will lost any throw exception.
return;
}
}
}
If you run this program you will see that it produce no output. even though an exception is thrown.
Exception restrictions
When you override a method, you can throw only the exception that have been specified in the base-class version of the method.
This is useful restriction, since it means that code that works with the base class will automatically work with any object derived from the base class
This example demonstrates the kinds of restrictions imposed for exceptions.
Overridden method may throw only the exceptions specified in their base-class versions, or exceptions derived from the base-class exceptions.
public class Base{
public Base() throws IOException{
}
public void display() throws Exception{
}
}
public class ExceptionRestrictions extends Base{
public ExceptionRestrictions()throws IOException SQLException{
}
// child class method has to throw same exception or subclass of that base class thrown Exception.
// child class can also choose not to throw any exceptions even if base class method does.
public void display() throws Exception{
}
}
Child class method has to throw same exception or subclass of that base class thrown Exception.
Child class can also choose not to throw any exceptions even if base class method does.
The restriction on exception does not apply to constructors.
You can see that a constructor can throw anything it wants, regardless of what the base-class constructor throws.
However, since a base-class constructor must always be called, the derived-class constructor must declare any base-class constructor exceptions in its exception specification.
A derived-class constructor cannot catch exception thrown by its base-class constructor.
All these constraints produce must more robust exception handling code.
Although exception specifications are enforced by the compiler during inheritance, the exception specifications are not part of the type of a method, which comprises only the method name and argument types. Therefore, you cannot overload methods based on exception specifications.
In addition, just because an exception specification exist in a base-class version of a method does not mean that it must exist in the derived class version of the method.
This is quit different from inheritance rules, where a method in the base class must also exist in the derived class.
Put another way, the "exception specification" for a particular method may narrow during inheritance and override, but it may not widen.
Constructors role in cleanup
It's important that you always ask, "If an exception occurs, will everything be properly cleaned up ?" Most of the time you are fairly safe, but with constructors there is problem.
The Constructor puts the object into a safe starting state, but it might perform some operation- such as opening a file that does not get cleaned up until the user is finished with the object and calls a special cleanup method.
If you throw an exception from inside a constructor, these cleanup behaviours might not occur properly. This means that must be careful while you write your constructor.
You might think that finally is the solution. But it's not quite that simple, because the finally performs the cleanup code every time.
If a constructor fails partway through its execution, It might not have successfully created some part of the object that will be cleaned up in the finally clause.
public class InputFile{
private BufferedReader in;
public InputFile(String name) throw Exception{
try{
in = BufferedReader(new FileReader(name));
} catch(FileNotFoundException e){
System.out.println("Could not open "+ name);
throw e;
}catch(Exception e){
try{
in.close();
}catch(IOException e){
System.out.println(" close unsuccessfully");
}
throw e;
} finally {
// Don't close it here
}
}
public String getLine(){
String s;
try{
s = in.readLine();
} catch(IOException e){
throw new RuntimeException(" readline() failed");
}
return s;
}
public void dispose(){
try{
in.close();
System.out.println("dispose successfully")
}catch(IOException e){
throw new RuntimeException(" in close() failed");
}
}
}
In this example, the finally clause is definitely not the place to close() the file, since that would close it every time the constructor completed.
The safest way to use a class which might throw an exception during construction and which requires cleanup is to use nested try block.
public class Cleanup{
public static void main(String args[]){
try{
InputFile in = new InputFile("cleanup.java");
try{
String s;
int i = 1;
while(s = in.readLine() != null){
// perform line by line processing here.
}
} catch(Exception e){
System.out.println("Caught exception in main");
e.printStackTrace(System.out);
} finally {
in.disponse();
}
} catch(Exception e){
System.out.println("Input file constructor failed");
}
}
}
Output :
dispose successfully
The finally that performs cleanup is associated with the inner try block; this way the finally clause is not executed if constructor fails, and it is always executed if construction success.
Exception matching
When an exception is thrown, the exception-handling system look through the nearest handlers in the order they are written. When it finds a match, the exception is considered handled, and no further searching occurs.
Matching an exception doesn't require a perfect match between the exception and its handler. A derived class object will match a handler for the base class.
Example :
public class Annoyance extends Exception {}
public class Sneeze extends Annoyance {}
public class Human {
public static void main(String args[]){
// Catch the exact type
try{
throw new Sneeze();
} catch( Sneeze sn){
System.out.println("Caught Sneeze");
} catch( Annoyance an){
System.out.println("Caught Annoyance");
}
// Catch the base type
try{
throw new Sneeze();
}catch(Annoyance an){
System.out.println("Caught Annoyance");
}
}
}
Output :
Caught Sneeze
Caught Annoyance
The Sneeze exception will be caught by the first catch clause that it matches, which is the first one, of course. However, If you remove the first catch clause, leaving only the catch clause with Annoyance, the code still works because it's catching the base class of Sneeze.
Put another way, catch(Annoyance a) will catch an Annoyance or any class derived from it. This is useful because if you decide to add more derived exception to a method, then the client programer's code will not need changing as long as the client catches the base-exceptions.
If you try to mask the derived class exception by putting the base class catch clause fist like this :
try{
throw new Sneeze();
} catch(Annoyance a){
//
} catch(Sneeze s){
//
}
The compiler will give you an error message, since it sees that the Sneeze catch clause can never be reached.
Passing exceptions to the console
The easiest way to preserve the exception without writing a lot of code is to pass them out of main() to the console.
For an example, if you want to open a file for reading, you must open and close a FineInputStream, which throws exceptions.
public class MainException{
public static void main(String args[]) throws Exception{
// open a file
FileInputStream file = new FileInputStream("main.java");
// use the file
// close the file
file.close();
}
}
Note that main() is also a method that may have an exception specification, and here the type of exception is Exception, the root class of all checked exception.
By passing it out to the console, you are relieved from writing try-catch clause within the body of main().
Converting checked exceptions to unchecked exceptions
Throwing an exception from main() is convenient when you are writing simple programs for your own consumption, but is not generally useful.
The real problem is when you are writing an method body, and you call another method and realize there is exception , "You don't have idea what to do with this exception here, You don't want to swallow it or print some message only " With chained exception, a simple solution prevent itself. You simply "wrap" a checked exception inside a RuntimeException by passing it ot the RuntimeException constructor like this.
try{
// to do sometings useful.
}catch(IDontKnowWhatToDoWithThisCheckedException e){
throw new RuntimeException(e);
}
This seems to be an ideal solution if you want to turn off the checked exception- you don't swallow it, and you don't have to put it in your method's exception specification, but because of exception chaining you don't lose any information from the original exception.
This technique provides the option to ignore the exception and let it bubble up the call stack without being required to write try-catch clause and exception specifications.
Exception guidelines
Use exception to :
1. Handle problems at the appropriate level. (Avoid catching exceptions unless you know what to do with them.)
2. Fix the problem and call the method that caused the exception again.
3. Calculate some alternative result instead of what the method was supposed to produce.
4. Do whatever you can in the current context and re-throw the same exception to a higher context.
5. Do whatever you can in the current context and re-throw the different exception to a higher context.
6. Terminate the program.
7. Make your library and program safer for application robustness.
Summary
Exceptions are integral to programming with java. One of the advantages of exception handling is that it allows you to concentrate on the problem you are trying to solve in one place, and then deal with the errors from that code in another place.
An exception-handling system is a trapdoor that allows your program to abandon execution of the normal sequence of statements.
Exception represent conditions that the current method is unable to handle. The reason exception handling system were developed is because the approach of dealing with each possible error condition produced by each function call was too onerous, and programmers simply weren't doing it.
As a result, they were ignoring the errors. Observing that the issue of programmer convenience in handling errors was a prime motivation for exceptions in the first place.
One of the important guidelines in exception handling is "Don't catch an exception unless you know what to do with it." In fact, one of the important goal of exception handling is to move the error handling code away from the point where the error occur. This allows you to focus on what you want to accomplish in one section of your code, and how you are going to deal with problems in a distinct separate section of your code.
As a result, your mainline code is not cluttered with error-handling logic, ant it's much easier to understand and maintain.
Exception handling also tend to reduce the amount of error-handling code by allowing one handler to deal with many error sites.
History
Exception handling originated in systems like PL/1 and Mesa, and later appeared in CLU, Smalltalk, Modula-3, Ada, Eiffel, C++ Python, Java and post java language Ruby and C#.
The Java design is similar to C++, except in places where the java designers felt that C++ approach caused problems.
No comments:
Post a Comment