Catching an exception | CodeGuru

Catching an exception

Bruce Eckel’s Thinking in Java Contents | Prev | Next If a method throws an exception, it must assume that exception is caught and dealt with. One of the advantages of Java exception handling is that it allows you to concentrate on the problem you’re trying to solve in one place, and then deal with […]

Written By
CodeGuru Staff
CodeGuru Staff
Mar 1, 2001
8 minute read
CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More

If


a method throws an exception, it must assume that exception is caught and dealt


with. One of the advantages of Java exception handling is that it allows you to


concentrate on the problem you’re trying to solve in one place, and then


deal with the errors from that code in another place.

To


see how an exception is caught, you must first understand the concept of a

guarded
region
,
which is a section of code that might produce exceptions, and is followed by
the code to handle those exceptions.

The
try block

If


you’re inside a method and you throw an exception (or another method 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


leave a method, you can set up a special block within that method to capture


the exception. This is called the


try
block

because you “try” your various method calls there. The try block is


an ordinary scope, preceded by the keyword


try

:

try {
  // Code that might generate exceptions
}

If


you were checking for errors carefully in a programming language that


didn’t support exception handling, you’d have to surround every


method call with setup and error testing code, even if you call the same method


several times. With exception handling, you put everything in a try block and


capture all the exceptions in one place. This means your code is a lot easier


to write and easier to read because the goal of the code is not confused with


the error checking.


Exception
handlers

Of


course, the thrown exception must end up someplace. This “place” is


the


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 exceptions
} catch(Type1 id1) {
  // Handle exceptions of Type1
} catch(Type2 id2) {
  // Handle exceptions of Type2
} catch(Type3 id3) {
  // Handle exceptions of Type3
}
 
// etc... 

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

,


and so on) can be used inside the handler, just like a method argument.


Sometimes you never use the identifier because the type of the exception gives


you enough information to deal with the exception, but the identifier must


still be there.

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 handlers stops


once the catch clause is finished.) Only the matching catch clause executes;


it’s not like a


switch

statement in which you need a


break

after each


case

to prevent the remaining ones from executing.

Note


that, within the try block, a number of different method calls might generate


the same exception, but you need only one handler.


Termination
vs. resumption

There


are two basic models in exception-handling theory. In


termination

(which is what Java and C++ support), you assume the error is so critical


there’s no way to get back to where the exception occurred. Whoever threw


the exception decided that there was no way to salvage the situation, and they


don’t


want

to come back.

The


alternative is called


resumption

.


It means that the exception handler is expected to do something to rectify the


situation, and then the faulting method is retried, presuming success the


second time. If you want resumption, it means you still hope to continue


execution after the exception is handled. In this case, your exception is more


like a method call – which is how you should set up situations in Java in


which you want resumption-like behavior. (That is, don’t throw an


exception; call a method that fixes the problem.) Alternatively, place your


try

block inside a


while

loop that keeps reentering the


try

block until the result is satisfactory.

Historically,


programmers using operating systems that supported resumptive exception


handling eventually ended up using termination-like code and skipping


resumption. So although resumption sounds attractive at first, it seems it


isn’t quite so useful in practice. The dominant reason is probably the

coupling
that
results: your handler must often be aware of where the exception is thrown from
and contain non-generic code specific to the throwing location. This makes the
code difficult to write and maintain, especially for large systems where the
exception can be generated from many points.

Advertisement

The
exception specification

In


Java, you’re required to inform the client programmer, who calls your


method, of the exceptions that might be thrown from your method. This is


civilized because the caller can know exactly what code to write to catch all


potential exceptions. Of course, if source code is available, the client


programmer could hunt through and look for


throw

statements, but often a library doesn’t come with sources. To prevent


this from being a problem, Java provides syntax (and


forces

you


to use that syntax) to allow you to politely tell the client programmer what


exceptions this method throws, so the 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 types. So your method


definition might look like this:

void
f() throws tooBig, tooSmall, divZero { //...

If


you say

void
f() { // ...

it


means that no exceptions are thrown from the method. (


Except

for


the exceptions of type


RuntimeException

,


which can reasonably be thrown anywhere – this will be described later.)

You


can’t lie about an exception specification – if your method causes


exceptions and 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 specifications from top to bottom, Java guarantees that exception


correctness can be ensured


at
compile time

.

[42]

There


is one place you can lie: you can claim to throw an exception that you


don’t. The compiler takes your word for it and forces the users of your


method to treat it as if it really does throw that exception. This has the


beneficial effect of being a placeholder for that exception, so you can


actually start throwing the exception later without requiring changes to


existing code.


Catching
any exception

It


is possible to create a handler that catches any type of exception. 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’ll want to put it at the


end

of your list of handlers to avoid pre-empting any exception handlers that might


otherwise follow it.

Since


the


Exception

class is the base of all the exception classes that are important to the


programmer, you don’t get much specific information about the exception,


but you can call the methods that come from


its

base type

Throwable: String
getMessage( )

Gets
the detail message.

String
toString( )

Returns
a short description of the Throwable, including the detail message if there is
one.

void
printStackTrace( )

void
printStackTrace(PrintStream)

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
exception was thrown.

The


first version prints to standard error, the second prints to a stream of your


choice. If you’re working under Windows, you can’t redirect


standard error so you might want to use the second version and send the results


to


System.out

;


that way the output can be redirected any way you want.

In


addition, you get some other methods from


Throwable

’s


base type


Object

(everybody’s base type). The one that might come in handy for exceptions


is

getClass( ),
which returns an object representing the class of this object. You can in turn
query this
Class
object for its name with
getName( )
or
toString( ).
You can also do more sophisticated things with
Class
objects that aren’t necessary in exception handling.
Class
objects will be studied later in the book.

Here’s


an example that shows the use of the


Exception

methods: (See page


97

if you have trouble executing this program.)

//: ExceptionMethods.java
// Demonstrating the Exception Methods
package c09;
 
public class ExceptionMethods {
  public static void main(String[] args) {
    try {
      throw new Exception("Here's my Exception");
    } catch(Exception e) {
      System.out.println("Caught Exception");
      System.out.println(
        "e.getMessage(): " + e.getMessage());
      System.out.println(
        "e.toString(): " + e.toString());
      System.out.println("e.printStackTrace():");
      e.printStackTrace();
    }
  }
} ///:~ 

The


output for this program is:

Caught Exception
e.getMessage(): Here's my Exception
e.toString(): java.lang.Exception: Here's my Exception
e.printStackTrace():
java.lang.Exception: Here's my Exception
        at ExceptionMethods.main 

You


can see that the methods provide successively more information – each is


effectively a superset of the previous one.


Advertisement

Rethrowing
an exception

Sometimes


you’ll want to rethrow the exception that you just caught, particularly


when you use


Exception

to catch any exception. Since you already have the handle to the current


exception, you can simply re-throw that handle:

catch(Exception e) {
  System.out.println("An exception was thrown");
  throw e;
}

Rethrowing


an exception causes the exception to go to the exception handlers in the


next-higher context. Any further


catch

clauses 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, 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 so by calling
fillInStackTrace( ),
which returns an exception object that it creates by stuffing the current stack
information into the old exception object. Here’s what it looks like:
//: Rethrowing.java
// Demonstrating fillInStackTrace()
 
public class Rethrowing {
  public static void f() throws Exception {
    System.out.println(
      "originating the exception in f()");
    throw new Exception("thrown from f()");
  }
  public static void g() throws Throwable {
    try {
      f();
    } catch(Exception e) {
      System.out.println(
        "Inside g(), e.printStackTrace()");
      e.printStackTrace();
      throw e; // 17
      // throw e.fillInStackTrace(); // 18
    }
  }
  public static void
  main(String[] args) throws Throwable {
    try {
      g();
    } catch(Exception e) {
      System.out.println(
        "Caught in main, e.printStackTrace()");
      e.printStackTrace();
    }
  }
} ///:~ 

The


important line numbers are marked inside of comments. With line 17 un-commented


(as shown), the output is:

originating the exception in f()
Inside g(), e.printStackTrace()
java.lang.Exception: thrown from f()
        at Rethrowing.f(Rethrowing.java:8)
        at Rethrowing.g(Rethrowing.java:12)
        at Rethrowing.main(Rethrowing.java:24)
Caught in main, e.printStackTrace()
java.lang.Exception: thrown from f()
        at Rethrowing.f(Rethrowing.java:8)
        at Rethrowing.g(Rethrowing.java:12)
        at Rethrowing.main(Rethrowing.java:24)

So


the exception stack trace always remembers its true point of origin, no matter


how many times it gets rethrown.

With


line 17 commented and line 18 un-commented,


fillInStackTrace( )

is used instead, and the result is:

originating the exception in f()
Inside g(), e.printStackTrace()
java.lang.Exception: thrown from f()
        at Rethrowing.f(Rethrowing.java:8)
        at Rethrowing.g(Rethrowing.java:12)
        at Rethrowing.main(Rethrowing.java:24)
Caught in main, e.printStackTrace()
java.lang.Exception: thrown from f()
        at Rethrowing.g(Rethrowing.java:18)
        at Rethrowing.main(Rethrowing.java:24)

Because


of


fillInStackTrace( )

,


line 18 becomes the new

point
of origin of the exception.

The


class


Throwable

must appear in the exception specification for


g( )

and


main( )

because


fillInStackTrace( )

produces a handle to a


Throwable

object. Since

Throwable
is a base class of
Exception,
it’s possible to get an object that’s a
Throwable
but
not
an
Exception,
so the handler for
Exception
in
main( )
might
miss it. To make sure everything is in order, the compiler forces an exception
specification for
Throwable.
For example, the exception in the following program is
not
caught in
main( ):
//: ThrowOut.java
public class ThrowOut {
  public static void
  main(String[] args) throws Throwable {
    try {
      throw new Throwable();
    } catch(Exception e) {
      System.out.println("Caught in main()");
    }
  }
} ///:~ 

It’s


also possible to rethrow a different exception from the one you caught. If you


do this, you get a similar effect as when you use


fillInStackTrace( )

:


the information about the original site of the exception is lost, and what


you’re left with is the information pertaining to the new


throw

:

//: RethrowNew.java
// Rethrow a different object from the one that
// was caught
 
public class RethrowNew {
  public static void f() throws Exception {
    System.out.println(
      "originating the exception in f()");
    throw new Exception("thrown from f()");
  }
  public static void main(String[] args) {
    try {
      f();
    } catch(Exception e) {
      System.out.println(
        "Caught in main, e.printStackTrace()");
      e.printStackTrace();
      throw new NullPointerException("from main");
    }
  }
} ///:~ 

The


output is:

originating the exception in f()
Caught in main, e.printStackTrace()
java.lang.Exception: thrown from f()
        at RethrowNew.f(RethrowNew.java:8)
        at RethrowNew.main(RethrowNew.java:13)
java.lang.NullPointerException: from main
        at RethrowNew.main(RethrowNew.java:18)

The


final exception knows only that it came from


main( )

,


and not from


f( )

.


Note that


Throwable

isn’t necessary in any of the exception specifications.

You


never have to worry about cleaning up the previous exception, or any exceptions


for that matter. They’re all heap-based objects created with


new

,


so the garbage collector automatically cleans them all up.



[42]

This is a significant improvement over C++ exception handling, which


doesn’t catch violations of exception specifications until run time, when


it’s not very useful.

Contents

|

Prev

|

Next
CodeGuru Logo

CodeGuru covers topics related to Microsoft-related software development, mobile development, database management, and web application programming. In addition to tutorials and how-tos that teach programmers how to code in Microsoft-related languages and frameworks like C# and .Net, we also publish articles on software development tools, the latest in developer news, and advice for project managers. Cloud services such as Microsoft Azure and database options including SQL Server and MSSQL are also frequently covered.

Property of TechnologyAdvice. © 2026 TechnologyAdvice. All Rights Reserved

Advertiser Disclosure: Some of the products that appear on this site are from companies from which TechnologyAdvice receives compensation. This compensation may impact how and where products appear on this site including, for example, the order in which they appear. TechnologyAdvice does not include all companies or all types of products available in the marketplace.