tags:

views:

372

answers:

8

I keep getting the dreaded java.something.someException errors while running my java app. and I don't seem to be getting the hang of what exceptions to handle and what not to?
When I read the api docs most of the functions throw exceptions like if I use I/O or use an Array... etc.

How to make a decision about what exceptions to catch and what not to and based on what parameters?

I am talking about checked exceptions here.

+4  A: 

A general rule of thumb is to handle those exceptions that you can do something about and don't handle those that you can't. In the cases where you don't handle an exception the caller is expected to handle them. If you're writing a framework or a library usually you'll end up wrapping low level exceptions like SQLException in a library or framework specific exception to abstract away the lower level details.

For example, if you're writing a method that writes to a file on disk then you should probably handle FileNotFoundExceptions since you can create the missing file, but if you run into problems creating the file or writing to it then you should probably let the caller handle it (after performing whatever cleanup work needs to be done).

Bryan Kyle
Just to add to that, if you are writing an app, not a library, you probably should handle ALL exceptions otherwise you app will crash. Even if it just means printing out "error..." and exiting, it's better practice than not handling the exception.
Chris Thompson
@Chris Thompson if you can't handle the exception in a meaningfull way, crashing is the right thing to do.
Jens Schauder
@Chris: Does that mean that I should try-catch every function?
Kevin Boyd
+12  A: 

Short answer

Catch exceptions that you can deal with then and there, re-throw what you can't.

Long answer

It's called exception-handling code for a reason: whenever you are tempted to write a catch block, you need to have a good reason to catch the exception in the first place. A catch block is stating your intent to catch the exception, and then do something about it. Examples of doing something about it include, but are not limited to:

  • Retrying the operation that threw the exception. This can make sense in the case of IOException's and other issues that may be temporary (i.e. a network error in the middle of trying to upload a file to a server. Maybe your code should retry the upload a few times).

  • Logging the exception. Yes, logging counts as doing something. You might also want to re-throw the original exception after logging it so that other code still has a chance to deal with the exception, but that depends on the situation.

  • Wrapping the exception in another exception that is more appropriate for your class's interface. For example, if you have a FileUploader class, you could wrap IOException's in a more generic UploadFailedException so that classes using your class don't have to have detailed knowledge of how your upload code works (the fact that it throws an IOException is technically an implementation detail).

If the code can't reasonably do anything about the problem at the point where it occurs, then you shouldn't catch it at all.

Unfortunately, such hard-and-fast rules never work 100% of the time. Sometimes, a third-party library you are using will throw checked exceptions that you really don't care about or which will never actually happen. In these cases, you can get away with using an empty catch block that doesn't run any code, but this is not a recommended way to deal with exceptions. At the very least, you should add a comment explaining why you are ignoring the exception (but as CPerkins notes in the comments, "never say never". You may want to actually log these kinds of "never-going-to-happen" exceptions, so just in case such an exception does happen, you are aware of it and can investigate further).

Still, the general rule is, if the method you are in can't do something reasonable with an exception (log it, rethrow it, retry the operation, etc.) then you shouldn't write a catch block at all. Let the calling method deal with the exception. If you are dealing with checked exceptions, add the checked exception to the throws clause of your method, which tells the compiler to pass the exception upwards to the calling method, which may be better suited to handle the error (the calling method may have more context, so it might have a better idea of how to handle the exception).

Usually, it is good to put a try...catch in your main method, which will catch any exceptions that your code couldn't deal with, and report this information to the user and exit the application gracefully.

And finally, don't forget about finally

Also keep in mind that even if you don't write a catch block, you might still need to write a finally block, if you need clean-up code to run regardless of whether the operation you are trying to perform throws an exception or not. A common example is opening up a file in the try block: you'll still want to close the file, even if an exception occurs, and even if your method isn't going to catch the exception. In fact, another common rule of thumb that you might see in tutorials and books is that try...finally blocks should be more common that try...catch blocks in your code, precisely because catch blocks should only be written when you can actually handle the exception, but finally blocks are needed whenever your code needs to clean up after itself.

Mike Spross
Good analysis, but I'd suggest you change the recommendation for third-party checked exceptions to include a logging statement, because "never" isn't always "never", if you follow me. And failures should never be truly silent.
CPerkins
@CPerkins: That's a good point actually. I edited this advice into the post.
Mike Spross
Generally, logging and rethrowing an exception is an anti-pattern ("log-and-rethrow"), because it leads to duplicate log entries and serves no real purpose (the exception will be handled up the chain anyway). It may be necessary in rare cases, but I'd generally advise against it.
sleske
@sleske: I agree that it's generally a bad idea, but as you point out, it "may" be necessary in some cases, which I tried to convey in the wording of the post. I didn't want to necessarily suggest that it should *never* be done, and as you say, a situation may come along where you find yourself needing to do it for whatever reason. It's up to the person coding it to figure that out for themselves, I can't code it for them :)
Mike Spross
+4  A: 

I highly recommend chapter 9 (Exceptions) in Joshua Bloch's Effective Java, 2nd Edition for these questions.

Jim Ferrans
+2  A: 

Having coded in Java for a few years, I agree that it is easy to get annoyed by writing endless try-catch blocks.

A good way of coding fast is to catch specific exceptions in as low level as you can, and catch the base Exception at the outermost level (in main()) just so that you can write a generic error message instead of letting the program crash.

This lets you have a running code pretty fast, and then you can take your time to add specific exceptions in various layers with their handling logic.

Joy Dutta
+2  A: 

Catch checked Exception, do not catch RuntimeException. Try to catch specific Exception, try not to catch by generic java.lang.Exception.

fastcodejava
+1 for the simplicity!
Kevin Boyd
You may want to do } catch (RuntimeException e) { new RuntimeException("more information for the maintainer", e); }This could be to capture important things like record id in the database, or similar in the stack trace to help a maintainer fix an issue without having to reproduce the whole scenario.
Thorbjørn Ravn Andersen
While it's not mandatory to catch RuntimeExceptions, it makes sense sometimes. For instance, if you have a lengthy calculation which might divide by zero, throwing ArithmeticException, you'll probably want to catch that one and display some error message instead of crashing.
Joonas Pulakka
Only at the topmost level.
Thorbjørn Ravn Andersen
Re: divide by zero, if you know a calculation may divide by zero, then just check for zero before attempting the division! Better to prevent the exception from happening in the first place, than to catch it after the fact.
Kevin Panko
A: 

As a rule of thumb I catch exceptions where I will be able to do something with then, or where I want the exception to stop moving up. For example, if I am processing a list of items, and I want the next item to be processed even if the others fail, then I will put a try...catch block around that item processing and only in there. I usually put a try...catch(Throwable) in the main method of my program so I can log errors like a class not found or similar stuff.

I don't put a try...catch block in a method if I will not know what to do with that exception. If you do, you will just bloat your code with lots of exception handling code.

Ravi Wallau
+1  A: 

Module boundaries

I catch exceptions for cleaner module boundaries, too. For example if there is a SQLException thrown that I can't handle I'll catch it nevertheless and throw my own descriptive exception instead (putting the SQLException as cause). This way the caller doesn't have to know that I'm using a database to fulfill his request. He just gets an error "Cannot handle this specific use case". If I decide to fulfill his request without database access, I don't have to change my API.

tangens
To add to this -- when you do this, your wrapped exception may "hide" the underlying exception from the API, but if the calling code does not know what the wrapped object is, it may get a ClassNotFoundException. This recently happened to me while using RMI over the network, and my client code did not have a definition for the wrapped exception object.
Kevin Panko
+2  A: 

These are my personal findings:

  • You need a try {} catch (Throwable o) {...} in your main routine so any unexpected exception can be caught, logged and the user told.
  • Checked exceptions are checked because you need as a programmer to make a decision what to do when they happen. Remember, one decision might be just to say "ok, time to crash".
  • If you end up with a fatal situation with a checked exception where all you can do is crash, then "throw new RuntimeException("reason", checkedException);" so the handler above have something to log. Include value of important local variables - remember some day you will have to debug a situation where the only thing you have is the stack trace.
  • Never, ever catch an exception and just ignore it. If you have to then you must document why you are allowed to break the rule, and do it right there, in the catch block.

And a hint that will help you some day: When you crash, provide a simple means to let the user seeing the message email the stack trace to you.


EDIT: Do not be afraid to create new exceptions if the ones available does not completely cover what you need. This allows you to get better naming in stack traces and your error handling code can differentiate between different cases easily. You may want to base all your own exceptions on a common base class ("OurDomainException") so you can use the base class in catch clauses to see if what type it is.

Thorbjørn Ravn Andersen
I think exceptions and crashes should not be mixed too much. Crashes are fatal, while exceptions might be handled at a higher level. "Ok, Time to crash" is rather "I can't do what I'm supposed to do, so I signal that to my caller". The caller can then decide if there's another way to handle the error condition (e.g. retry, log, show error message, disable menu items, add task to a queue for retry later, ...). No need to call that "crash". Unless there's the classic catch(Exception e){ System.exit(); }
Olaf
+1 for adding domain specific exceptions
Olaf
I do not agree. Calling System.exit(1) does not allow final-blocks to run. Throwing an exception that will bubble up, will.
Thorbjørn Ravn Andersen
It may well be possible for a low level routine to determine that this is an uncorrectable problem. Hence the "time to crash" wording. The outermost exception handler may then allow the user to do stuff, but basically the execution is aborted. How would you handle a "eh, database went away" situtation inside your library routines?
Thorbjørn Ravn Andersen
In a "time to crash" situation, the right thing to do is throw an `Error`, not a `RuntimeException`. E.g. `OutOfMemoryError`. `Error` and `Exception` both inherit from `Throwable`, but `Error`s, like `RuntimeException`s, do not have to be declared with the `throws` keyword.
Kevin Panko
@Kevin, I don't agree. I think Errors should be reserved for the JVM.
Thorbjørn Ravn Andersen