Chain exceptions by passing the caught exception as the cause argument of the new exception.
Messages in exceptions are not chained. I have to do that myself if I want to how something went wrong.
toString() and toMessage() differ only in that toString() prepends the exception type to what it dumps out.
Most messages from Java are horribly terse, almost to the point of uselessness.
Use unchecked exceptions (i.e., subclassed from RuntimeException) when the caller did something wrong with the API; use checked exception (i.e., subclasses from Exception) when the call was made correctly, but something didn’t cooperate properly.
You have to reimplement all the constructor variations in subclasses, but can basically delegate everything to the superclass.