Java Exceptions Are Particularly Evil

From BillTrost in DoesXpWorkForJava.

Sorry I couldn't resist this WikiName.

What conventions or patterns are there for handling existing Java Exceptions and when/when not to create new Exception classes? Stuff that could really help to improve on RonJeffries' report that experienced Smalltalkers tell him that refactoring is 2-4 times slower in JavaLanguage?


I'm a CeePlusPlus programmer and so cannot compare Java to SmalltalkLanguage. But I can compare it to C++. There are relatively few things not to like about Java exceptions and a couple of things to like.

I think that CheckedExceptionsAreOfDubiousValue: they introduce a dependency between a method and potentially all of its direct or indirect callers. The type information conveyed is not of much practical value (it seems like it ought to be but in practice it's rarely used) for purposes of coding although it does add valuable documentation to the method. Generalizing the type of exception in the "throws" clause can go a long way toward mitigating the problem. For instance, a method that does a lot of file manipulation can put "IOException" in its "throws" clause. This will cover: EOFException, FileNotFoundException, InterruptedIOException, and ZipException as well as many others. Having a single base exception type for each package can also help with this problem. The dependencies are still there but they are more general and therefore more stable. See ConvertExceptions for more information.

The other big problem with JavaLanguage exceptions isn't with the exception mechanism itself but with the mechanism that calls "finalize". In CeePlusPlus the destructor for a local object will be called as soon as that object goes out of scope. This allows the use of an idiom called ResourceAllocationIsInitialization which uses destructors as a mechanism for releasing resources such as memory, file handles, database connections, etc. when a method exits by any means either normal or exceptional. This idiom cannot be used in Java because the timing of "finalize" calls is non-deterministic. (See FinalizationProblem.) In fact, it is not guaranteed that they will ever be called (I do not use them for this reason). ResourceAllocationIsInitialization is a fine idiom in C++ and I use it a lot. But it does take some time and effort to set up and is pretty opaque to the uninitiated. JavaLanguage supports a "finally" clause which is much easier to use on an ad hoc basis than ResourceAllocationIsInitialization but which is less powerful and ultimately more error-prone because it must always be set up manually.

I disagree on finalize being a big problem (or being a problem at all). It might look like a problem if one thinks about it like CeePlusPlus destructors. Finalize is not a destructor. In my opinion it sometimes can be a debugging aid, but it seldom is even that. So the occasions when finalize should be used are very rare. I have not seen one yet. In JavaLanguage, if you need to explicitly destroy an object, you need to explicitly label the object as such. This is done by writing API documentation and implementing a DestroyMethod. -- AnttiBrax?

The issue is primarily on how to release single threaded resources, for example, files, sockets, and user-specific database connections. In C++, this can be done automatically in many cases without the developer writing a method and calling it at the correct time. Failure to release resources correctly, particularly after encountering an error condition. I really don't want to get into a LanguagePissingMatch, I am just trying to describe the differences in the languages. Each reader can determine how significant these differences are on his own.

As for when to create new exception types: I recommend throwing instances of Exception until something in your code makes you want to do something more complex. At that point I'd move toward having an exception type per package and more specific subtypes based on those. -- PhilGoodwin

I strongly recommend not to do this. Throwing a plain Exception covers also runtime exceptions. If you feel like you need to throw (or catch) a plain Exception it is most likely that your are not following a good exception policy. A good example of an exception policy is at http://www.octopull.demon.co.uk/java/ExceptionalJava.html. -- AnttiBrax?


What irritates me is the apparent lack of thought put into the hierarchy of exceptions in JavaLanguage. Many operations seem to throw several exceptions with no common parent. This leads to silly code which violates OnceAndOnlyOnce, for example:

  try
  {
ret = Class.forName(className).newInstance();
  }
  catch(ClassNotFoundException e)
  {
log.println("couldn't load class '" + className + "'");
  }
  catch(IllegalAccessException e)
  {
log.println("couldn't load class '" + className + "'");
  }
  catch(InstantiationException e)
  {
log.println("couldn't load class '" + className + "'");
  }
If they insist on this kind of Exception return, then Java should include some sort of Exception case structure which allows you to group exceptions together and handle them with the same code.

Or have I missed something? -- FrankCarver

All exceptions have Throwable as the common parent, and most have Exception. You can catch Exception. -- DaveHarris

I could, but doesn't that defeat the point of having different exception classes in the first place? I'm always reluctant to casually catch Exception or Throwable. What if another type of exception is thrown (in the case of Throwable, it could even be a RuntimeException). I don't want to catch that with my simple error log case. So I might then end up with something like:

As noted below, RuntimeException is in fact a subclass of Exception, so even catching Exception is a bad idea in that light.

  try {
    ret = Class.forName(className).newInstance();
  }
  catch(Exception e) {
    if (e instanceof ClassNotFoundException ||
        e instanceof IllegalAccessException ||
        e instanceof InstantiationException)
    {
      log.println("couldn't load class '" + className + "'");
    } else {
      throw e;
    }
  }
which is arguably worse than my first example.

Worse in some ways (you're losing automatic checking of which CheckedExceptions you're handling), better in others (the logging code isn't repeated). One solution you can use in your own exception hierarchies is to use TagInterfaces to create groupings of exceptions that would otherwise be unrelated. Then you can write a single catch clause that catches the tag and handles all the exceptions uniformly. -- pg

Another problem with the above is that it doesn't actually handle anything. The compiler will insist that something above it handle all Exception-s now, since you threw a variable of type Exception (namely, e). Hardly the desired effect.

An extension to DaveHarris' comment above, it's OK to catch Exception/Throwable, if you have no intelligent processing anyway. The Exception subclasses are added as a "why" to refine your exception processing. In the example above, is the processing going to be different if you know that one of these is thrown? Maybe if you're writing a Class loader, but in general from an Application standpoint you can't continue in any case.

I disagree (with the lack of thought idea). One reason for a class throwing many different exceptions with no "parent" exception grouping them is that they all should be handled differently. To use the example from above, ClassNotFoundException should be handled differently than IllegalAccessException. If I were debugging the above example and one of those exceptions occurred, I would want to know which one it was, instead of just seeing "couldn't load class..." I already know that it couldn't load the class, but to fix it, I need to know why it didn't load. That is the point of having the different exceptions in the first place. -- JoshuaBaran


Why not something like?

try {
ret = Class.forName(className).newInstance();
} catch (ClassNotFoundException e1) {
throw e1;
} catch ( Exception e2 ) {
System.out.println( e2.getMessage() );
}


This is somewhat at right angles to what you're asking but ... I recently arrived at the following idiom if I want to perform some side effect when an exception occurs, without actually catching the exception and rethrowing it (which would confuse the stack trace, unless you put in extra work).

How would re-throwing the same exception object confuse the stack trace? The stack trace is filled in when and where the Throwable object is constructed, not when and where the object is thrown. (Unless things have changed, try creating an exception object somewhere and throw it from somewhere else.)

    boolean doneOkay = false;
    try {
        doSomething();
        doneOkay = true;
    }
    finally {
        if (! doneOkay) {
            logger.warn("Something went wrong");
            // or, in EJB land, say: context.setRollbackOnly();
        }
    }
Is this useful enough to put with the other JavaIdioms? Does anyone have any problems with this idiom?

I'd say at least that it smells a bit of status codes and intermixed work and error handling. I'm not sure that there's a cleaner way to do it, though. -- DavisHerring?

As for the title of this page, I'm inclined to agree. In the DylanLanguage, f'rinstance, you can catch several unrelated exceptions like this:

    block
      doSomething();
    exception (type-union(<some-error>,
                          <some-other-error>,
                          <yet-another-error>))
      log-warn("Something went wrong");
    end;
Of course, Dylan has its own problems :-)

-- HughGreene


I think the Class.forName() exceptions are particularly evil. The rest of the exception hierarchy isn't so bad (take IOException or SQLException for example). See NestedException for a good pattern for doing exceptions in JavaLanguage without creating unnecessary dependencies. (The most hassle-free way to do it is to have one exception per module, and wrap any other exceptions that occur in the module code within the module's exception.)

Yes, finalize() is basically useless, use finally instead. Java is not like CeePlusPlus, PerlLanguage, or PythonLanguage in this respect. (Sometimes I miss ReferenceCounting-based GarbageCollection.)

-- BrianSlesinsky

On the contrary, IOException is (IMHO) pure evil. Instead of defining an UnknownIOException class, and having such innocuous things as InputStream.read() throw only that while more specific things throw their appropriate exception and that, they made all I/O methods (and thus, virally, anything even remotely related to I/O) throw one generic exception type. Without knowing far too much (for the Java modus operandi, anyway) about the underlying implementation, you never know what to do with one aside from print its message somewhere. (Should you check to see if it's a FooIOException that you know how to deal with? Is it worth the trouble? Does this implementation throw those?) For instance, look at http://java.sun.com/j2se/1.3/docs/api/java/io/class-use/InterruptedIOException.html -- one of the platform standard exceptions, and it's not even used anywhere! Of course, it says it's thrown when an I/O operation is interrupted, but there's no way to tell, now is there? Exceptions lose their value when they become arbitrary. -- DavisHerring?

The LimboLanguage (from BellLabs) has a clever idea about reclaiming resources. Potentially cyclic types must be marked so with the keyword cyclic. Language rules prohibit the creation of cyclic structures from non-cyclic types. Thus non-cyclic types can use ReferenceCounting, assuring immediate reclamation of resources. This seems to work fine, since most (if not all) types representing external resources are not going to be cyclic. -- JonathanAmsterdam


Couldn't you handle the above exception code using an exception handler? This is overblown for the problem displayed, but as example:

  ExceptionHandler handler = new ExceptionHandler();
  try 
  { 
Class.forName();
  } catch (Exception e) 
  {
handler.catchException(e);
  }

class ExceptionHandler { public void catchException(Exception e) { if <in handled exceptions> // print out error else // throw exception. }

public void addException(Class exClass) { // Add the exception class to the list of handled exceptions } }
-- WillSargent

The problem with this is that catchException must itself declare that it throws "Exception", which causes everything which uses it to have to deal with the possibility of absolutely anything happening (as far as the compiler can tell). Eventually all the undifferentiated exceptions reach top level, where you still have to have a handler which tries to figure out what to do with an arbitrary exception. The proper thing to do here is to watch for RuntimeExceptions and propagate them (unless the code knows what to do with them), and then otherwise catch exactly what you need to (possibly with a handler method to factor out the error reporting). Of course, this is partially the fault of the weird exception hierarchy discussed below. -- DavisHerring?


If you're not doing anything particularly special in the exception handler, then catching Exception is probably the right thing to do. If it's making you uncomfortable, then you've probably got too much stuff in the try-block; the try should only contain a single conceptual operation, or (rarely) a set of operations that you want to fail as a group.

Also don't forget the ability of an exception to describe itself in the log:

  try {
ret = Class.forName(className).newInstance();
  } catch (Exception e) {
log.print("couldn't load class '" + className + "': ");
e.printStackTrace(log);
  }
This will handle any failure of the instantiation, and tell you why (what type of exception) and where it failed.

On the other hand, catching Throwable (while useful for daemon threads which need to live even if some operation, or user-registered code, fails in unforeseen ways) is potentially dangerous. In particular, you must ALWAYS pass ThreadDeath through:

  try {
someUserClass.run();
  } catch (ThreadDeath t) {
throw t;
  } catch (Throwable t) {
System.err.print("User supplied code failed:");
t.printStackTrace(System.err);
  }
-- AlexPopiel


I've found that just catching Exception always gets me in trouble. When I'm developing, I find there are broadly two sorts of exceptions:

When I'm developing, I want my code to handle the first kind with a little grace, but I often want it to fall over in a burning heap of useless junk with the second kind. But if you end every try clause with "catch (Exception e)" it treats them all the same.

Possibly try this technique:

  try {
someClass.someMethod();
  }
  catch (RuntimeException rex) {
rex.printStackTrace();
throw rex;
  }
  catch (YourException yex) {
yex.printStackTrace();
// graceful exception handling code
  }
It gives you the benefit of dealing with your "graceful" exceptions, but still goes down flaming on the RuntimeExceptions. In general, I only catch exceptions that I know will be thrown, and only if I can do something intelligent with them. Otherwise, I throw them up to the caller, since they should then be more capable than I am of deciding how to deal with a failure. -- JesseBlomberg

There seems to be no point in catching the RuntimeException at all in the example. If the only thing one is going to do is to print the stack trace then the exception should just be allowed to pass to the caller. Of course, one must take care that the object is left in a usable state if one decides not to catch a RuntimeException. -- AnttiBrax?

I like the pattern here of catch RuntimeException before catching Exception, given the unfortunate decision SunMicrosystems made for RuntimeException to extend Exception. However I think there is also an AntiPattern here, of printStackTrace() followed by throw. Instead of leaving the exception handled (as in the lower block) or unhandled (by simply throwing it), it is in a limbo "touched but not handled" state. The least bad result of this is that the exception will get logged multiple times. -- ScottVachalek


Part of the problem is that Sun just messed up on the type hierarchy. It looks like this (excuse the BigUglyAsciiGraphic?):

                                            Throwable (checked)
                                                     |
                               +---------------------+---------------------+
                               |                                           |
                      Exception (checked)                             Error (unchecked)
                               |
                +--------------+--------------+
                |                             |
  RuntimeException (unchecked)   <OtherException subclasses> (checked)
                |
  <RuntimeException subclasses> (unchecked)

So RuntimeException has kind of magic behavior. I don't know if this would be strictly considered a violation of the LiskovSubstitutionPrinciple, but in any case it's a related problem. I really think that RuntimeException isn't a specialization of Exception, and it should be checked exceptions that are the exception, not unchecked ones. If instead there were a CheckedException class, which all checked exceptions (and only checked exceptions) had to extend, then you could write:

  try {
    whatever();
  } catch (CheckedException ce) {
    handleException(ce);
  }
And all UncheckedExceptions would just fall through. As it is now, the best you can do is similar to Jesse's example:

  try {
    whatever();
  } catch (RuntimeException rte) {
    throw rte;
  } catch (Exception e) {
    handleException(e);
  }
-- TimMoore

Yeah, that's what I do. It's not incredibly elegant, but anyone with six months of JavaLanguage experience should take one look at it and know what you're doing. Luckily the reflection examples are not the norm. Usually a method only throws one class of exception so you're fine.

I think CheckedExceptions are great, when used appropriately. You have to think about them though. It would be nice if there were an UnhandledExceptionException, for when you acknowledge that an exceptional condition might occur but when it's not a requirement of your application to handle it. Such an exception would extend RuntimeException and would be caught by any top-level exception handler, where the root cause would be extracted and a the stack trace logged or presented to the user.

It would also be nice to have a similar IllegalExceptionException for dealing with exceptions that logically should never happen.

Although it doesn't cover the general case, there is ExceptionInInitializerError.

Of course anyone can create these for their own applications.

-- BenArnold

To me, that smells faintly of the whole mess with throws() specifications and std::unexpected() in CeePlusPlus...


Consider the compare method of java.util.Comparator:

  int compare(T o1, T o2) throws ClassCastException 
What do you do if you implement Comparator, and you have an SQLException whilst performing the comparison? You cannot alter the method signature of the Comparator interface. You are forced to either swallow the exception, or wrap it as a RuntimeException.

Or wrap it in your own Exception that extends ClassCastException. That give you the benefit to be able to catch exactly this Exception.

Clearly, traditional advice for using CheckedExceptions falls apart when you are required to write code to play nicely with other people's interfaces.

A useful strategy is therefore LetExceptionsPropagateOnlyAsUncheckedExceptions.

-- AndreParrie

Actually, this should be regarded as a benefit of CheckedExceptions. The signature dictates you must handle the exception (unchecked exceptions should only be used in the event of avoidable, i.e. programmer, errors or system failures). Otherwise, having one bad comparator implementation can cause an otherwise robust system to fall over (technically, it still can, but the evil would have to be intentional). But what if the compare method can't properly handle the exception? Well, that is a good indication that the method is trying to do too much. Compare is likely to be called repeatedly (i.e. NlogN times for your run-of-the-mill sort) as such should be as lightweight as possible. In this case I would see if I could redesign to do the heavy lifting in an initialisation phase and handle any exceptional conditions there. -- RichardCordova


CategoryException CategoryJava CategoryEvil


EditText of this page (last edited June 11, 2008) or FindPage with title or text search