I use this as a general rule.
- Where it makes sense, use a pre-defined Java exception. For example, if your code has some sort of I/O Error, it is fine to throw an IOException.
- Only use exception hierarchies if you need to differentiate between the two exceptions in a try/catch block. A lot of times it is perfectly fine to have a single component throw a single exception type with different messages for different errors. If the user cannot really do anything to handle the error specially, use the same generic exception class. If the user is able to handle them differently, that is when you should use a hierarchy.
- For hierarchies, do not make all exceptions from different components inherit from a base exception. There is no real reason to do this. If the consumer wants to catch anything, they can simply catch Exception.
- For package location, I put an Exception class with the code it relates to. So if I have a BusinessService in a package a.b.c, I have a a.b.c.BusinessException to go with it. I’m not a fan of putting all exceptions into an exceptions package. It just makes it hard to find.