If an error occurs the developer cannot reasonably recover from it should be an Error
e.g. VerifyError
or NoMuchMethodError
. If a condition occurs I believe should be impossible, I use AssertionError
If an error occurs which the developer might be able to recover from, though most developers are unlikely to know how to deal the exception, use a RuntimeException
as this doesn't force the developer to write handling code.
If an error is being passed to the caller to deal with, even if most developers don't know how to recover from exceptions, and even if they did, might find it hard to recover from that exception, a checked exception can be used.
You can also create a Throwable
or a direct sub-class which is also checked, however I only use this as a simple way to print a stack trace i.e. make it clear it's not really an error. I suggest avoiding ever throwing such as Throwable as it is confusing and very unlikely to be handled correctly.
In our code base, we might say we use Exception effectively and in many cases write both the caller and callee which is be best chance to be able to pass exceptions in a useful manner. Never the less, recovering via a fallback accounts for only 19% of our catch
cases, and a "signal" accounts of 6% of cases (A "Signal" is passing a checked exception from deep in the call stack on rare occasions)
In summary we only manage to handle and recover from about 25% of exceptions/errors in the manner I believe checked exceptions are intended. I think it's a valuable 25%, but I would be happier if it was higher.

For the full post discussing the different ways we handle exceptions.
https://vanilla-java.github.io/2016/06/21/Reviewing-Exception-Handling.html