Effective Java – Part 5

Chapter 8. Exceptions

Item 39:Use exceptions only for exceptional conditions
A well-designed API must not force its client to use exceptions for ordinary control flow. Provide state-testing method or distinguished return value for state-dependent methods. A state-testing method is mildly preferable to a distinguished return value

Item 40:Use checked exceptions for recoverable conditions and run-time exceptions for programming errors
Use checked exceptions for conditions from which the caller can reasonably be expected to recover.Use run-time exceptions to indicate programming errors. There is a strong convention that errors are reserved for use by the JVM to indicate resource deficiencies, invariant failures, or other conditions that make it impossible to continue execution. It’s best not to implement any new Error subclasses. Checked exceptions should provide methods to furnish information that could help callers to recover.

Item 41:Avoid unnecessary use of checked exceptions
Overuse of checked exceptions can make an API far less pleasant to use.One technique for turning a checked exception into an unchecked exception is to break the method that throws the exception into two methods, the first of which returns a boolean indicating whether the exception would be thrown.

Item 42:Favor the use of standard exceptions
Exceptions are no exception to the general rule that code reuse is good. Reuse must be based on semantics, not just on name.

Most commonly used exceptions are:
IllegalArgumentException – Parameter value is inappropriate.
IllegalStateException – Object state is inappropriate for method invocation.
NullPointerException – Parameter value is null where prohibited
IndexOutOfBoundsException – Index parameter value is out of range
ConcurrentModificationException – Concurrent modification of object has been detected where prohibited.
UnSupportedOperationException – Object does not support method

Item 43: Throw exceptions appropriate to the abstraction
Higher layers should catch lower-level exceptions and, in their place, throw exceptions that are explainable in terms of the higher-level abstraction. While exception translation is superior to mindless propagation of exceptions from lower layers, it should not be overused.

Item 44:Document all exceptions thrown by each method
Always declare checked exceptions individually, and document precisely the conditions under which each one is thrown using the Javadoc @throws tag.
Use the Javadoc @throws tag to document each unchecked exception that a method can throw, but do not use the throws keyword to include unchecked exceptions in the method declaration. If an exception is thrown by many methods in a class for the same reason, it is acceptable to document the exception in the class’s documentation comment rather than documenting it individually for each method.

Item 45:Include failure-capture information in detail messages
To capture the failure, the string representation of an exception should contain the values of all parameters and fields that “contributed to the exception”.
One way to ensure that exceptions contain adequate failure-capture information in their string representations is to require this information in their constructors in lieu of a string detail message.

Item 46:Strive for </vetbfailure atomicity
Generally speaking, a failed method invocation should leave the object in the state that it was in prior to the invocation. A method with this property is said to be failure atomic.

Item 47:Don’t ignore exceptions
An empty catch block defeats the purpose of exceptions, which is to force you to handle exceptional conditions. At the very least, the catch block should contain a comment explaining why it is appropriate to ignore the exception.

Chapter 9. Threads

Item 48: Synchronize access to shared mutable data
The synchronized keyword ensures that only a single thread will execute a statement or block at a time.Not only does synchronization prevent a thread from observing an object in an inconsistent state, but it also ensures that objects progress from consistent state to consistent state by an orderly sequence of state transitions that appear to execute sequentially. Every thread entering a synchronized method or block sees the effects of all previous state transitions controlled by the same lock.

Synchronization is required for reliable communication between threads as well as for mutual exclusion.The double-check idiom for lazy initialization is broken!The initialize-on-demand holder class idiom is appropriate for use when a static field is expensive to initialize and may not be needed, but will be used intensively if it is needed.

Whenever multiple threads share mutable data, each thread that reads or writes the data must obtain a lock.


The use of the volatile modifier constitutes a viable alternative to ordinary synchronization under certain circumstances, but this is an advanced technique

Item 49: Avoid excessive synchronization
To avoid the risk of deadlock, never cede control to the client within a synchronized method or block.As a rule, you should do as little work as possible inside synchronized regions. If you’re writing a class that will be used heavily in circumstances requiring synchronization and also in circumstances where synchronization is not required, a reasonable approach is to provide both synchronized (thread-safe) and unsynchronized (thread-compatible) variants.

Item 50: Never invoke wait outside a loop
Always use the wait loop idiom to invoke the wait method.If all of the threads vying for special status are logically equivalent, then all you have to do is carefully use notify instead of notifyall.If, however, only some of the waiting threads are eligible for special status at any given time, then you must use a pattern known as Specific Notification [Cargill96, Lea99].

Item 51: Don’t depend on the thread scheduler
Any program that relies on the thread scheduler for its correctness or performance is likely to be nonportable. The best way to write a robust, responsive, portable multithreaded application is to ensure that there are few runnable threads at any given time. The main technique for keeping the number of runnable threads down is to have each thread do a small amount of work and then wait for some condition using Object.wait or for some time to elapse using Thread.sleep. Threads should not busy-wait. Resist the temptation to “fix” the program by putting in calls to Thread.yield The only use that most programmers will ever have for Thread.yield is to artificially increase the concurrency of a program during testing.

Thread priorities are among the least portable features of the Java platform.

Item 52: Document thread safety The presence of the synchronized modifier in a method declaration is an implementation detail, not a part of the exported API.Its presence does not reliably indicate that a method is thread safe; it is subject to change from release to release.
To enable safe multithreaded use, a class must clearly document in prose the level of thread safety that it supports. Different level of thread safety – immutable, thread-safe, conditionally thread-safe, thread-compatible, thread-hostile.
Using internal objects for locking is particularly suited to classes designed for inheritance.

Item 53: Avoid thread groups
Thread groups don’t provide much in the way of useful functionality, and much of the functionality they do provide is flawed.
The ThreadGroup.uncaughtException method is automatically invoked when a thread in the group throws an uncaught exception.You may occasionally wish to override this implementation, for example, to direct the stack trace to an application-specific log.

Leave a Comment