Fork of https://github.com/mgp/book-notes/blob/master/effective-java-2nd-edition.markdown
by Joshua Bloch
I, Michael Parker, own this book and took these notes to further my own learning. If you enjoy these notes, please purchase the book!
- An instance-controlled class is one that uses static factories to strictly control what instances exist at any time.
- By convention, static factory methods for an interface named
Type
are put in a non-instantiable class namedTypes
. - When naming static factory methods,
getInstance
may return the same instance, whilenewInstance
should not.
- The builder pattern simulates optional named parameters in Ada and Python.
- A builder whose parameters have been set makes a fine abstract factory, assuming some generic
Builder<T>
interface.
- Adding
implements Serializable
to a singleton class is not enough, you must declare all fieldstransient
and provide areadResolve
method. - A single-element
enum
type provides the serialization for free and is the best way to implement a singleton.
- A private constructor not only supresses instantiation, but subclassing.
- Often lazy initialization only complicates the implementation and yields no noticeable performance increase.
- Prefer primitives to boxed primitives, as unintentional autoboxing can lead to creating many new instances.
- Highly optimized garbage collectors can easily outperform object pools that do not contain heavyweight objects.
- Whenever a class manages its own memory, like a stack or object pool, the programmer should be alert for memory leaks.
- Caches, listeners, and callbacks can all be sources of memory leaks, but weak references can help.
- Do not think of finalizers as Java's analogue of C++ destructors -- there's no guarantee finalizers will be called at all!
- If an uncaught exception is thrown in a finalizer, it is ignored, and the finalization abruptly terminates.
- There is a severe performance penalty for using finalizers -- object creation and deletion increases from nanoseconds to microseconds.
- Override
equals
when a class has a notion of logical equality that differs from mere object identity, and the superclass has not provided a suitable implementation. - For classes that represent a value, such as
Integer
orDate
, theequals
method should always be overridden. - There is no way to extend an instantiable class and add a value component (field) while preserving the
equals
contract. - By using composition with views to internal components, you can add value components to instantiable classes without violating the
equals
contract. - To compare
float
anddouble
values, use theFloat.compare
andDouble.compare
methods to deal withNaN
and-0.0
values. - For best performance, first compare fields that are more likely to differ, or less expensive to compare.
- To take the hash code of
float
anddouble
values, useFloat.floatToIntBits
andDouble.doubleToLongBits
, respectively. - Immutability offers the chance to cache hash codes if computing them is expensive.
- Try not to specify the behavior of your hash code method in Javadoc, as that limits your options for improving it later.
- If you specify the format in Javadoc, provide a static factory method accepting a
String
parameter so a client can convert between the two forms. - Provide programmatic information to all the information provided by
toString
, or clients may try to parse the string to retrieve it.
- Like the
equals
method, there is no way to extend and instantiable class with a new value component while preserving thecompareTo
contract. - If
compareTo
is consistent withequals
, note that sorted collections (e.g.TreeSet
,TreeMap
) use the equality test imposed bycompareTo
instead ofequals
and may break the interface (e.g.Set
,Map
) contract. - Use methods
Double.compare
andFloat.compare
instead of relational operators, which don't obey thecompareTo
contract for floating point values.
- Private and package-private members can "leak" into the exported API if the class implements Serializable.
- Even a protected member is part of the class's exported API and must be supported forever.
- With the exception of
public static final
fields to immutable objects, public classes should have no public fields.
- If a class is package-private or a private nested class, there's nothing wrong with exposing its data.
- A public class with immutable public fields is okay because it can enforce their invariants upon construction.
- For immutable classes, the Java memory model requires that all fields be
final
to ensure correct behavior when passing an instance between threads without synchronization. - Defend against "leaking" references by making defensive copies in constructors, accessors, and
readResolve
methods when needed. - Instances of an immutable class can share internal objects with one another for efficiency.
- If a client requires performing expensive multi-stage operations on your class, expose them as primitive methods, or provide a mutable companion class (like
StringBuilder
forString
).
- Inheritance violates encapsulation because the subclass depends on the implementation details of the superclass for its proper function.
- Inheritance means you inherit the scope and flaws of an API, whereas composition allows you to design a better suited one.
- If an appropriate interface exists, using composition and forwarding allows you to instrument any implementation of the interface, instead of a single implementation through inheritance.
- The class that allows subclassing must document its self-use of overridable methods.
- The only way to test if a class is suitably designed for inheritance (e.g. provides all the necessary implementation hooks through protected methods) is to actually write subclasses.
- Eliminating a class's self use of methods, typically through introducing private helper methods, can make a class safe to subclass.
- Use interfaces to allow construction of non-hierarchical type frameworks.
- Simulated multiple inheritance is where a class implementing an interface can forward invocations to an instance of a private inner class that extends the skeletal implementation of that interface and hence does the bulk of the work.
- A variant of a skeletal implementation is the simple implementation, which is a concrete class that defines the simplest possible implementation, such as
AbstractMap.SimpleEntry
.
- Don't use constant interfaces, or interfaces that define no methods but only constants, which classes implement to access the constants without fully qualifying their names.
- If the constants are static members of a class, and you really don't want to qualify their names, use the
static import
facility for brevity.
- Classes that implement concrete strategies, or simulate function pointers, should be stateless and made into singletons.
- When defining a strategy as an anonymous class that is inline in a method invocation, consider extracting the object as a
private static final
field so a new instance is not created upon every call.
- Nonstatic member classes are ideal for providing adapters, or views of an outer class as an instance of some unrelated class.
- An anonymous class have enclosing instances if and only if they occur in a non-static context.
List<E>
is read as "list of E", andList<String>
is read as "list of string", whereString
is the actual type parameter andE
is the formal type parameter.- While an instance of the raw type
List
could be designated to hold only types of a single class but opts out of type-checking,List<Object>
is typesafe because it explicitly states that it can contain objects of any type. - The unbound wildcard type, such as in
List<?>
, represents a list of some unknown type and so forbids inserting any element other thannull
, unlike the raw typeList
which is not typesafe.
- Always use the
@SuppressWarnings
annotation on the smallest scope possible, to not mask other, critical warnings.
- Arrays are covariant, so
Sub[]
is a subtype ofSuper[]
, and reified, so they retain their type-information at runtime andSuper[]
can throw anArrayStoreException
when given a different subclass; by contrast,List<E>
is invariant and erased. - Consequently, arrays provide runtime safety but not compile-time safety, while a generic type like
List<E>
provides compile-time safety but not runtime safety.
- You can exploit the type inference provided by generic methods and write generic static factory methods that make instances easier to create.
- If you have an immutable, singleton instance of a generic class that could be shared across all types, make it private and of type
Object
, and then let a generic singleton factory method cast it to the caller's desired type. - The type bound
<T extends Comparable<T>>
may be read as "for every type T that can be compared to itself" and is the definition of a mutually comparable type.
- If a parameterized type represents a
T
producer, use<? extends T>
; if a parameterized type represents aT
consumer, use<? super T>
. - Do not use wildcard types as return types, or clients will be forced to use wildcard types in their code.
- Comparables and comparators are always consumers, so you should always use
Comparator<? super T>
in preference toComparator<T>
. - If a type parameter appears only once in a method declaration, replace it with a wildcard, using a private helper method to capture the type if necessary.
- A type token is a class literal is passed among methods to communicate both compile-time and runtime type information.
- The cast method of the
Class
type is the dynamic analog of Java's cast operator, throwing aClassCastException
if the operation fails. - The "checked" collection wrappers in
java.util.Collections
use this method with type tokens to enforce, at runtime, that invalid types are not added to a collection through its raw type.
- You can add or reorder constants in an
enum
type without recompiling its clients because the constant values are not compiled into the clients as they are with the int enum pattern. - Enums are by their nature immutable, and so all their fields should be final.
- If you override the
toString
method of anenum
type, consider writing afromString
method, similar to how the staticvalueOf
method would perform if you had not overriddentoString
. - To share a constant specific method implementations between
enum
values, move each implementation into a private nestedenum
, and pass an instance of this strategy enum to the constructor of the top-levelenum
.
- An
EnumSet
is represented by one or morelong
values, and so many of its operations are effiiciently performed with bitwise arithmetic.
- When you access an array that is indexed by the ordinal value of an
enum
, it is your responsibility to use the correctint
value, as no type safety is afforded. - An
EnumMap
contains an array internally, offering the speed of an ordinal-indexed array with the type safety and richness of theMap
interface. - Instead of using an array of arrays to define a mapping from two enum values, use
EnumMap<..., EnumMap<...>>
, which is internally represented as an array of arrays.
- If two enums implement the same interface, both their classes adhere to the type
<T extends Enum<T> & InterfaceName>
. - Since implementations cannot be inherited from one
enum
type to another, the functionality must be encapsulated in a helper class or a static helper method.
- Annotations like
Retention
andTarget
for annotation type declarations are called meta-annotations. - A marker annotation is one with no parameter and simply serves to mark some class, method, or field for interpretation by some other method or program.
- There is no need to use the
@Override
annotation when a concrete class overrides an abstract method, i.e. implements it, because a differing signature will be caught by the compiler anyway. - In an abstract class or interface, annotate all methods you believe to override superclass or superinterface methods, whether concrete or abstract, to ensure that you don't accidentally introduce any new methods.
- Use a marker interface instead of an annotation if you want to write one or more methods that accept only objects that have this marking, or implement the interface.
- Use a marker interface instead of an annotation if you want to limit the use of the marker to elements of a particular interface, by having the marker interface extend that interface.
- A marker annotation, however, allows marking elements other than classes and interfaces, and allows adding more information while retaining backwards compatibility through type elements with defaults.
- Nonpublic methods should check their parameters using assertions, which are enabled with the
-enableassertions
command line flag, instead of explicitly throwing exceptions. - Skip checking a method's parameters before performing the computation if the validity check would be expensive or impractical and the validity check is performed implicitly during the computation.
- When making defensive copies of constructor parameters, check the validity of the copies instead of the originals to guard against malicious changes to the parameters by another thread.
- Do not use the
clone
method to make a defensive copy of a constructor parameter whose type is subclassable by untrusted parties. - The defensive copy can be replaced by documented transfer of ownership if copying the object would be costly and the class trusts its clients not to modify the components inappropriately.
- If you go over four parameters, try to break up the method into orthogonal methods with fewer parameters, introduce helper classes that bundle related parameters together, or use a builder pattern.
- Prefer two-element
enum
types to boolean parameters.
- Beware that selection among overloaded methods is static, while selection among overridden methods is dynamic.
- Avoid cases where the same set of parameters can be passed to different overloadings of a method by the addition of casts.
- If a method with a primitive parameter overloads a method with a generic parameter, the two can become conflated from autoboxing.
- Don't blindly retrofit every method that has a final array parameter to use varargs; use varargs only when a call operates on a variable-length sequence of values.
- Returning
null
from an array or collection-valued method complicates the caller logic, and usually the callee's. - To eliminate the overhead from creating an empty collection or array, return the immutable empty collections in
java.util.Collections
, or create a static empty array which is necessarily immutable.
- To include a multiline code example in a doc comment, use a Javadoc
{@code}
tag wrapped inside an HTML<pre>
tag. - The
{@literal}
tag is like the{@code}
tag in that it eliminates the need to escape HTML metacharacters, but doesn't render the contents in monospaced font. - No two members or constructors should have the same summary description, which is the first (sometimes incomplete) sentence of a doc comment.
- Declare a variable at the latest point possible, typically right before it is first used, and strive to provide an initializer.
- If the bound for a loop variable is expensive to compute on every iteration, store it in a loop variable that will fall out of scope with the counter variable.
- If you are writing a type that represents a group of elements, have it implement the
Iterable
interface even if it does not implementCollection
. - The enhanced
for
loop cannot be used if you need to remove elements through an iterator, reassign elements through a list iterator, or iterate over collections in parallel.
- Every programmer should be familiar with the contents of
java.lang
andjava.util
(particularly the collections framework), and to a lesser extentjava.io
andjava.util.concurrent
.
- The
BigDecimal
can contain decimal values of arbitrary size and provides eight rounding modes, which is ideal for business calculations with legally mandated rounding behavior. - If you choose to keep track of the decimal point yourself,
int
provides up to nine decimal digits, whilelong
provides up to eighteen.
- Never use
==
on two boxed primitives, because this always performs identity comparison, while the<
and>
operators compare the underlying primitive values. - When you mix primitives and boxed primitives in a single operation, the boxed primitive is auto-unboxed, which can result in
NullPointerExceptions
. - Beware of boxed primitives being implicitly unboxed and then re-boxed, which can cause performance problems.
- Instead of using a key to represent an aggregate type, write a private static member class -- even if it only has two fields.
- A consequence of strings being immutable is that the time to concatenate n strings is quadratic in n.
- An alternative to using a
StringBuilder
is to try processing the strings one at a time to avoid all concatenation.
- If you depend on any properties of an implementation not specified by its interface, such as its synchronization policy, use the class as a type and document the requirements.
- If a class is unavailable at compile time but there exists an appropriate interface, create instances reflectively and access them normally through their interface.
- If writing a package that runs against multiple versions of some other package, you can compile it against the minimal environment required to support it, and access any newer classes or methods reflectively.
- Strive for encapsulation so that a part of the system can be rewritten for performance without changing its other parts.
- You need to measure attempted optimization carefully on the Java platform, because the language does not have a strong performance model, or well-defined relative costs.
- Components of a package name should be short, generally eight or fewer characters, where abbreviations are encouraged and acronyms are acceptable.
- Names of
static final
fields whose values are immutable should be in uppercase with words separated by underscores. - Methods that convert a type have the format
toType
, while methods that return a view have the formatasType
, and methods that return a primitive representation have the formattypeValue
.
- Do not force clients to use exceptions for ordinary control flow, and instead offer methods to test whether an exception could be thrown, such as
hasNext
for classIterator
. - Use a distinguished return value, like
null
, if the object is accessed by multiple threads or if the state-testing method duplicates the work of the state-dependent method.
Item 58: Use checked exceptions for recoverable conditions and runtime exceptions for programming errors
- Use runtime exceptions to indicate programming errors, typically precondition violations.
- Don't implement any new
Error
subclasses, and don't define a throwable that does not subclassException
orRuntimeException
.
- When designing an API, only throw a checked exception if it can be prevented by a proper use of the API, and the programmer can take some useful action once thrown.
- Throw a
NullPointerException
instead of anIllegalArgumentException
if a caller passes in anull
parameter where prohibited. - Throw an
IndexOutOfBoundsException
instead of anIllegalArgumentException
if the caller passes in an invalid index for a sequence.
- Use exception translation, where low-level exceptions are caught and exceptions appropriate to the higher-level abstraction are thrown.
- If an exception does not have a chaining-aware constructor, use the
initCause
method ofThrowable
. - Try to avoid low-level exceptions by checking the higher-level method's parameters upfront.
- Document the unchecked exceptions a method can throw, thereby documenting its preconditions.
- Do not use the
throws
keyword to include unchecked exceptions in a method declaration.
- The
toString
method, or "detail message," should contain the values of all the parameters and fields contributing to the exception. - To capture this information easily, make them parameters to the constructor, and internally generate the detail message from them.
- A failed method invocation should leave the object in the state it was prior to the invocation.
- Typically you can easily achieve failure atomicity by checking the parameters' validity before the operation.
- Synchronization is not just for mutual exclusion, but ensuring that a value written by one thread is seen by another.
- Both read and write operations on mutable data must be
synchronized
or visibility is not guaranteed. - Beware that the increment and decrement operators are not atomic on volatile integers.
- Inside a
synchronized
region, do not invoke a method that is provided by the client as a function object, or can be overriden. - Reentrant locks simplify the construction of multi-threaded object oriented programs, but can allow foreign methods to access an object in an inconsistent state.
- Only make a mutable class thread-safe if intended for concurrent use and you can achieve better concurrency with internal locking; otherwise, punt locking to the client.