Main notes taken from the book Effective Java Third Edition
Source code at https://github.com/jbloch/effective-java-3e-source-code
- One advantage of static factory methods is that, unlike constructors, they have names.
- A second advantage of static factory methods is that, unlike constructors, they are not required to create a new object each time they’re invoked.
- A third advantage of static factory methods is that, unlike constructors, they can return an object of any subtype of their return type.
- A fourth advantage of static factories is that the class of the returned object can vary from call to call as a function of the input parameters.
- A fifth advantage of static factories is that the class of the returned object need not exist when the class containing the method is written.
- The main limitation of providing only static factory methods is that classes without public or protected constructors cannot be subclassed.
- A second shortcoming of static factory methods is that they are hard for programmers to find.
- The telescoping constructor pattern works, but it is hard to write client code when there are many parameters, and harder still to read it.
- A JavaBean may be in an inconsistent state partway through its construction.
- The JavaBeans pattern precludes the possibility of making a class immutable.
- The Builder pattern simulates named optional parameters.
- The Builder pattern is well suited to class hierarchies.
- The Builder pattern is a good choice when designing classes whose constructors or static factories would have more than a handful of parameters.
- Making a class a singleton can make it difficult to test its clients.
- A single-element enum type is often the best way to implement a singleton.
- Attempting to enforce noninstantiability by making a class abstract does not work.
- A class can be made noninstantiable by including a private constructor.
- Static utility classes and singletons are inappropriate for classes whose behavior is parameterized by an underlying resource.
- A better alternative to satisfy the ability to support multiple instances of the class, each of which uses the resource desired by the client, is pass the resource into the constructor when creating a new instance.
- Autoboxing blurs but does not erase the distinction between primitive and boxed primitive types.
- Prefer primitives to boxed primitives, and watch out for unintentional autoboxing.
- Nulling out object references should be the exception rather than the norm.
- Whenever a class manages its own memory, the programmer should be alert for memory leaks.
- Another common source of memory leaks is caches.
- A third common source of memory leaks is listeners and other callbacks.
- Finalizers are unpredictable,often dangerous,and generally unnecessary.
- Cleaners are less dangerous than finalizers, but still unpredictable, slow, and generally unnecessary.
- Never do anything time-critical in a finalizer or cleaner.
- Never depend on a finalizer or cleaner to update persistent state.
- There is a severe performance penalty for using finalizers and cleaners.
- Finalizers have a serious security problem: they open your class up to finalizer attacks.
- Throwing an exception from a constructor should be sufficient to prevent an object from coming into existence; in the presence of finalizers, it is not.
- To protect nonfinal classes from finalizer attacks, write a final finalize method that does nothing.
- Have your class implement
AutoCloseable
- Once you’ve violated the
equals
contract, you simply don’t know how other objects will behave when confronted with your object. - There is no way to extend an instantiable class and add a value component while preserving the
equals
contract, unless you’re willing to forgo the benefits of object-oriented abstraction. - Do not write an equals method that depends on unreliable resources.
- When you are finished writing your equals method, ask yourself three questions: Is it symmetric? Is it transitive? Is it consistent? (create unit tests).
- Always override
hashCode
when you overrideequals
. - Don’t substitute another type for Object in the equals declaration.
- You must override
hashCode
in every class that overridesequals
. - When you fail to override
hashCode
after overridingequals
you violate the provision that equal objects must have equal hash codes. - Do not be tempted to exclude significant fields from the hash code computation to improve performance.
- Don’t provide a detailed specification for the value returned by
hashCode
, so clients can’t reasonably depend on it; this gives you the flexibility to change it.
- Providing a good
toString
implementation makes your class much more pleasant to use and makes systems using the class easier to debug. - When practical, the
toString
method should return all of the interesting information contained in the object. - Whether or not you decide to specify the format of the returned value by the
toString
, you should clearly document your intentions. - Provide programmatic access to the information contained in the value returned by
toString
.
- In practice, a class implementing
Cloneable
is expected to provide a properly functioning publicclone
method. - Immutable classes should never provide a
clone
method. - In effect, the
clone
method functions as a constructor; you must ensure that it does no harm to the original object and that it properly establishes invariants on theclone
. someArray.clone()
is the preferred idiom to duplicate an array (someArray
in this case). In fact, arrays are the sole compelling use of the clone facility.- The
Cloneable
architecture is incompatible with normal use of final fields referring to mutable objects. - Public
clone
methods should omit the throws clause. - A better approach to object copying is to provide a copy constructor or copy factory.
- Use of the relational operators
<
and>
in compareTo methods is verbose and error-prone and no longer recommended.
- Make each class or member as inaccessible as possible.
- Instance fields of public classes should rarely be public
- Classes with public mutable fields are not generally thread-safe.
- It is wrong for a class to have a
public
static
final
array field, or an accessor that returns such a field.
- If a class is accessible outside its package, provide accessor methods.
- If a class is package-private or is a private nested class, there is nothing inherently wrong with exposing its data fields.
- Immutable objects are simple.
- Immutable objects are inherently thread-safe; they require no synchronization.
- Immutable objects can be shared freely.
- Not only can you share immutable objects, but they can share their internals.
- Immutable objects make great building blocks for other objects.
- Immutable objects provide failure atomicity for free.
- The major disadvantage of immutable classes is that they require a separate object for each distinct value.
- Classes should be immutable unless there’s a very good reason to make them mutable.
- If a class cannot be made immutable, limit its mutability as much as possible.
- Declare every field private final unless there’s a good reason to do otherwise.
- Constructors should create fully initialized objects with all of their invariants established.
- Unlike method invocation, inheritance violates encapsulation.
- The class must document its self-use of overridable methods.
- A class may have to provide hooks into its internal workings in the form of judiciously chosen protected methods.
- The only way to test a class designed for inheritance is to write subclasses.
- You must test your class by writing subclasses before you release it.
- Constructors must not invoke overridable methods (directly or indirectly).
- Neither
clone
norreadObject
may invoke an overridable method, directly or indirectly. - Designing a class for inheritance requires great effort and places substantial limitations on the class.
- The best solution to this problem is to prohibit subclassing in classes that are not designed and documented to be safely subclassed.
- Existing classes can easily be retrofitted to implement a new interface.
- Interfaces are ideal for defining mixins.
- Interfaces allow for the construction of nonhierarchical type frameworks.
- Good documentation is absolutely essential in a skeletal implementation.
- It is not always possible to write a default method that maintains all invariants of every conceivable implementation.
- In the presence of default methods, existing implementations of an interface may compile without error or warning but fail at runtime.
- It is still of the utmost importance to design interfaces with great care.
- While it may be possible to correct some interface flaws after an interface is released, you cannot count on it.
- The constant interface pattern is a poor use of interfaces.
- Tagged classes are verbose, error-prone, and inefficient.
- A tagged class is just a pallid imitation of a class hierarchy.
- If you declare a member class that does not require access to an enclosing instance, always put the
static
modifier in its declaration.
- Never put multiple top-level classes or interfaces in a single source file.
- If you use raw types, you lose all the safety and expressiveness benefits of generics.
- You lose type safety if you use a raw type such as
List
, but not if you use a parameterized type such asList<Object>
. - You can’t put any element (other than
null
) into aCollection<?>
. - You must use raw types in class literals.
- This is the preferred way to use the
instanceof
operator with generic types:
if (o instanceof Set) {
// Raw type
Set<?> s = (Set<?>) o;
// Wildcard type
...
}
- Eliminate every unchecked warning that you can.
- If you can’t eliminate a warning, but you can prove that the code that provoked the warning is typesafe, then (and only then) suppress the warning with an
@SuppressWarnings("unchecked")
annotation. - Always use the
SuppressWarnings
annotation on the smallest scope possible. - Every time you use a
@SuppressWarnings("unchecked")
annotation, add a comment saying why it is safe to do so.
- The type parameter list, which declares the type parameters, goes between a method’s modifiers and its return type.
// Generic method
public static <E> Set<E> union(Set<E> s1, Set<E> s2) {
Set<E> result = new HashSet<>(s1);
result.addAll(s2);
return result;
}
- For maximum flexibility, use wildcard types on input parameters that represent producers or consumers.
- PECS stands for producer-
extends
, consumer-super
. - Do not use bounded wildcard types as return types.
- If the user of a class has to think about wildcard types, there is probably something wrong with its API.
- If a type parameter appears only once in a method declaration, replace it with a wildcard. i.e, from the following two declarations, the second, is the preferrend one:
public static <E> void swap(List<E> list, int i, int j) { // (1) not preferred, it's more complex for the API clients
list.set(i, list.set(j, list.get(i)));
}
public static void swap(List<?> list, int i, int j) { // (2) preferred, it's simpler to use by the API clients
swapHelper(list, i, j);
}
// Private helper method for wildcard capture
private static <E> void swapHelper(List<E> list, int i, int j) {
list.set(i, list.set(j, list.get(i)));
}
- It is unsafe to store a value in a generic varargs array parameter.
- The
SafeVarargs
annotation constitutes a promise by the author of a method that it is typesafe. - It is unsafe to give another method access to a generic varargs parameter array.
- Use
@SafeVarargs
on every method with a varargs parameter of a generic or parameterized type.
- To associate data with enum constants, declare instance fields and write a constructor that takes the data and stores it in the fields.
- If you override the
toString
method in anenum
type, consider writing afromString
method to translate the custom string representation back to the corresponding enum. - Switches on enums are good for augmenting enum types with constant-specific behavior.
- Use enums any time you need a set of constants whose members are known at compile time.
- It is not necessary that the set of constants in an enum type stay fixed for all time.
- Never derive a value associated with an enum from its ordinal; store it in an instance field instead.
- Just because an enumerated type will be used in sets, there is no reason to represent it with bit fields.
- It is rarely appropriate to use ordinals to index into arrays: use
EnumMap
instead.
- While you cannot write an extensible enum type, you can emulate it by writing an interface to accompany a basic enum type that implements the interface.
- There is simply no reason to use naming patterns when you can use annotations instead.
- all programmers should use the predefined annotation types that Java provides.
- Use the
Override
annotation on every method declaration that you believe to override a superclass declaration.
- Marker interfaces define a type that is implemented by instances of the marked class; marker annotations do not.
- Another advantage of marker interfaces over marker annotations is that they can be targeted more precisely.
- The chief advantage of marker annotations over marker interfaces is that they are part of the larger annotation facility.
- If you find yourself writing a marker annotation type whose target is
ElementType.TYPE
, take the time to figure out whether it really should be an annotation type or whether a marker interface would be more appropriate.
- Omit the types of all lambda parameters unless their presence makes your program clearer.
- Lambdas lack names and documentation; if a computation isn’t self-explanatory, or exceeds a few lines, don’t put it in a lambda.
- You should rarely, if ever, serialize a lambda.
- Don’t use anonymous classes for function objects unless you have to create instances of types that aren’t functional interfaces.
- Where method references are shorter and clearer, use them; where they aren’t, stick with lambdas.
- If one of the standard functional interfaces does the job, you should generally use it in preference to a purpose-built functional interface.
- Don’t be tempted to use basic functional interfaces with boxed primitives instead of primitive functional interfaces.
- You should seriously consider writing a purpose-built functional interface in preference to using a standard one if:
- It will be commonly used and could benefit from a descriptive name.
- It has a strong contract associated with it.
- It would benefit from custom default methods.
- Always annotate your functional interfaces with the
@FunctionalInterface
annotation.
- Overusing streams makes programs hard to read and maintain.
- In the absence of explicit types, careful naming of lambda parameters is essential to the readability of stream pipelines.
- Using helper methods is even more important for readability in stream pipelines than in iterative code.
- Refrain from using streams to process
char
values. - Refactor existing code to use streams and use them in new code only where it makes sense to do so.
- If you’re not sure whether a task is better served by streams or iteration, try both and see which works better.
- The
forEach
operation should be used only to report the result of a stream computation, not to perform the computation. - It is customary and wise to statically import all members of
Collectors
because it makes stream pipelines more readable.
Collection
or an appropriate subtype is generally the best return type for a public, sequence-returning method.- Do not store a large sequence in memory just to return it as a collection.
- Parallelizing a pipeline is unlikely to increase its performance if the source is from
Stream.iterate
, or the intermediate operationlimit
is used. - Do not parallelize stream pipelines indiscriminately.
- Performance gains from parallelism are best on streams over
ArrayList
,HashMap
,HashSet
, andConcurrentHashMap
instances;arrays
;int
ranges; andlong
ranges. - Not only can parallelizing a stream lead to poor performance, including liveness failures; it can lead to incorrect results and unpredictable behavior (safety failures).
- Under the right circumstances, it is possible to achieve near-linear speedup in the number of processor cores simply by adding a parallel call to a stream pipeline.
- The
Objects.requireNonNull
method, added in Java 7, is flexible and convenient, so there’s no reason to performnull
checks manually anymore.
- You must program defensively, with the assumption that clients of your class will do their best to destroy its invariants.
- Date is obsolete and should no longer be used in new code.
- It is essential to make a defensive copy of each mutable parameter to the constructor.
- Defensive copies are made before checking the validity of the parameters, and the validity check is performed on the copies rather than on the originals.
- Do not use the clone method to make a defensive copy of a parameter whose type is subclassable by untrusted parties.
- Return defensive copies of mutable internal fields.
- Choose method names carefully.
- Don’t go overboard in providing convenience methods.
- Avoid long parameter lists.
- Long sequences of identically typed parameters are especially harmful.
- For parameter types, favor interfaces over classes.
- Prefer two-element enum types to boolean parameters.
- The choice of which overloading to invoke is made at compile time.
- Selection among overloaded methods is static, while selection among overridden methods is dynamic.
- Avoid confusing uses of overloading.
- A safe, conservative policy (to fulfil previous point) is never to export two overloadings with the same number of parameters.
- You can always give methods different names instead of overloading them.
- Do not overload methods to take different functional interfaces in the same argument position.
- Never return
null
in place of an empty array or collection.
- Never return a
null
value from anOptional
-returning method: it defeats the entire purpose of the facility. - Optionals are similar in spirit to checked exceptions.
- Container types, including collections, maps, streams, arrays, and optionals should not be wrapped in optionals.
- You should declare a method to return
Optional<T>
if it might not be able to return a result and clients will have to perform special processing if no result is returned. - You should never return an optional of a boxed primitive type.
- To document your API properly, you must precede every exported class, interface, constructor, method, and field declaration with a doc comment.
- The doc comment for a method should describe succinctly the contract between the method and its client.
- Doc comments should be readable both in the source code and in the generated documentation.
- No two members or constructors in a class or interface should have the same summary description.
- When documenting a generic type or method, be sure to document all type parameters.
- When documenting an enum type, be sure to document the constants.
- When documenting an annotation type, be sure to document any members.
- Whether or not a class or static method is thread-safe, you should document its thread-safety level.
- Read the web pages generated by the Javadoc utility.
- The most powerful technique for minimizing the scope of a local variable is to declare it where it is first used.
- Nearly every local variable declaration should contain an initializer.
- Prefer for loops to while loops.
- Keep methods small and focused.
- By using a standard library, you take advantage of the knowledge of the experts who wrote it and the experience of those who used it before you.
- The random number generator of choice is now
ThreadLocalRandom
(For fork join pools and parallel streams, useSplittableRandom
). - Numerous features are added to the libraries in every major release, and it pays to keep abreast of these additions.
- Every programmer should be familiar with the basics of
java.lang
,java.util
, andjava.io
, and their subpackages. - The collections framework and the streams library should be part of every programmer’s basic toolkit, as should parts of the concurrency utilities in
java.util.concurrent
.
- The
float
anddouble
types are particularly ill-suited for monetary calculations. - Use
BigDecimal
,int
, orlong
for monetary calculations.
- Applying the
==
operator to boxed primitives is almost always wrong. - When you mix primitives and boxed primitives in an operation, the boxed primitive is auto-unboxed.
- Autoboxing reduces the verbosity, but not the danger, of using boxed primitives.
- When your program does unboxing, it can throw a
NullPointerException
.
- Strings are poor substitutes for other value types.
- Strings are poor substitutes for enum types.
- Strings are poor substitutes for aggregate types.
- Strings are poor substitutes for capabilities.
- Using the string concatenation operator repeatedly to concatenate
n
strings requires time quadratic inn
. - To achieve acceptable performance (to avoid the case mentioned before), use a
StringBuilder
in place of aString
. - Don’t use the string concatenation operator to combine more than a few strings unless performance is irrelevant.
- If appropriate interface types exist, then parameters, return values, variables, and fields should all be declared using interface types.
- If you get into the habit of using interfaces as types, your program will be much more flexible.
- It is entirely appropriate to refer to an object by a class rather than an interface if no appropriate interface exists.
- If there is no appropriate interface, just use the least specific class in the class hierarchy that provides the required functionality.
- Reflection allows one class to use another, even if the latter class did not exist when the former was compiled. This has a price:
- You lose all the benefits of compile-time type checking.
- The code required to perform reflective access is clumsy and verbose.
- Performance suffers.
- You can obtain many of the benefits of reflection while incurring few of its costs by using it only in a very limited form.
- Create instances reflectively and access them normally via their interface or superclass.
- It is rarely advisable to use native methods for improved performance.
- It is rare that you need to use them for improved performance. If you must use native methods to access low-level resources or native libraries, use as little native code as possible and test it thoroughly.
- More computing sins are committed in the name of efficiency (without necessarily achieving it) than for any other single reason,including blind stupidity. — William A. Wulf
- We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. — Donald E. Knuth
- We follow two rules in the matter of optimization: — M. A. Jackson
- Rule 1. Don’t do it.
- Rule 2 (for experts only). Don’t do it yet—that is, not until you have a perfectly clear and unoptimized solution.
- Strive to write good programs rather than fast ones.
- Strive to avoid design decisions that limit performance.
- Consider the performance consequences of your API design decisions.
- It is a very bad idea to warp an API to achieve good performance.
- Measure performance before and after each attempted optimization.
- jmh is a microbenchmarking framework that provides unparalleled visibility into the detailed performance of Java code.
- The Java platform has a well-established set of naming conventions, many of which are contained in The Java Language Specification [JLS, 6.1], summarized as follow:
- Package and module names should be hierarchical with the components separated by periods.
- Components should consist of lowercase alphabetic characters and, rarely, digits.
- The name of any package that will be used outside your organization should begin with your organization’s Internet domain name with the components reversed, for example,
edu.cmu
,com.google
,org.eff
. The standard libraries and optional packages, whose names begin withjava
andjavax
, are exceptions to this rule. - Users must not create packages or modules whose names begin with
java
orjavax
. - The remainder of a package name should consist of one or more components describing the package.
- Components should be short, generally eight or fewer characters.
- Meaningful abbreviations are encouraged, for example,
util
rather thanutilities
. - Acronyms are acceptable, for example,
awt
. - Components should generally consist of a single word or abbreviation.
- Class and interface names, including enum and annotation type names, should consist of one or more words, with the first letter of each word capitalized, for example,
List
orFutureTask
. - Abbreviations are to be avoided, except for acronyms and certain common abbreviations like
max
andmin
. - Method and field names follow the same typographical conventions as class and interface names, except that the first letter of a method or field name should be lowercase, for example,
remove
orensureCapacity
. - If an acronym occurs as the first word of a method or field name, it should be lowercase.
- The sole exception to the previous rule concerns “constant fields”, whose names should consist of one or more uppercase words separated by the underscore character, for example,
VALUES
orNEGATIVE_INFINITY
. - Local variable names have similar typographical naming conventions to member names, except that abbreviations are permitted, as are individual characters and short sequences of characters whose meaning depends on the context in which they occur, for example,
i
,denom
,houseNum
. - Input parameters should be named much more carefully than ordinary local variables, as their names are an integral part of their method’s documentation.
- Type parameter names usually consist of a single letter. Most commonly it is one of these five:
T
for an arbitrary type,E
for the element type of a collection,K
andV
for the key and value types of a map, andX
for an exception. The return type of a function is usuallyR
. A sequence of arbitrary types can beT
,U
,V
orT1
,T2
,T3
. - Instantiable classes, including enum types, are generally named with a singular noun or noun phrase, such as
Thread
,PriorityQueue
, orChessPiece
. - Non-instantiable utility classes are often named with a plural noun, such as
Collectors
orCollections
. - Interfaces are named like classes, for example,
Collection
orComparator
, or with an adjective ending inable
orible
, for example,Runnable
,Iterable
, orAccessible
. - Because annotation types have so many uses, no part of speech predominates. Nouns, verbs, prepositions, and adjectives are all common, for example,
BindingAnnotation
,Inject
,ImplementedBy
, orSingleton
. - Methods that perform some action are generally named with a verb or verb phrase (including object), for example,
append
ordrawImage
. - Methods that return a boolean value usually have names that begin with the word
is
or, less commonly,has
, followed by a noun, noun phrase, or any word or phrase that functions as an adjective, for example,isDigit
,isProbablePrime
,isEmpty
,isEnabled
, orhasSiblings
. - Methods that return a non-boolean function or attribute of the object on which they’re invoked are usually named with a noun, a noun phrase, or a verb phrase beginning with the verb
get
, for example,size
,hashCode
, orgetTime
. - Instance methods that convert the type of an object, returning an independent object of a different type, are often called
to
Type, for example,toString
ortoArray
. - Methods that return a view whose type differs from that of the receiving object are often called
as
Type, for example,asList
. - Methods that return a primitive with the same value as the object on which they’re invoked are often called type
Value
, for example,intValue
. - Common names for static factories include
from
,of
,valueOf
,instance
,getInstance
,newInstance
,get
Type, andnew
Type. - Fields of type boolean are often named like boolean accessor methods with the initial is omitted, for example,
initialized
,composite
. - Fields of other types are usually named with nouns or noun phrases, such as
height
,digits
, orbodyStyle
.
- These conventions should not be followed slavishly if long-held conventional usage dictates otherwise. Use common sense.
- Exceptions are, as their name implies, to be used only for exceptional conditions; they should never be used for ordinary control flow.
- A well-designed API must not force its clients to use exceptions for ordinary control flow.
- Java provides three kinds of throwables: checked exceptions, runtime exceptions, and errors.
- Use checked exceptions for conditions from which the caller can reasonably be expected to recover.
- Use runtime exceptions to indicate programming errors.
- All of the unchecked throwables you implement should subclass
RuntimeException
(directly or indirectly).
- When used sparingly, checked exceptions can increase the reliability of programs; when overused, they make APIs painful to use.
- If recovery may be possible and you want to force callers to handle exceptional conditions, first consider returning an optional. Only if this would provide insufficient information in the case of failure should you throw a checked exception.
- Do not reuse
Exception
,RuntimeException
,Throwable
, orError
directly. - This table summarizes the most commonly reused exceptions:
Exception | Occasion for Use |
---|---|
IllegalArgumentException |
Non-null 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 an object has been detected where it is prohibited |
UnsupportedOperationException |
Object does not support method |
- Throw
IllegalStateException
if no argument values would have worked, otherwise throwIllegalArgumentException
.
- Higher layers should catch lower-level exceptions and, in their place, throw exceptions that can be explained 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.
- Always declare checked exceptions individually, and document precisely the conditions under which each one is thrown.
- Use the Javadoc
@throws
tag to document each exception that a method can throw, but do not use the throws keyword on unchecked exceptions. - If an exception is thrown by many methods in a class for the same reason, you can document the exception in the class’s documentation comment rather than documenting it individually for each method.
- To capture a failure, the detail message of an exception should contain the values of all parameters and fields that contributed to the exception.
- Do not include passwords, encryption keys, and the like in detail messages.
- Generally speaking, a failed method invocation should leave the object in the state that it was in prior to the invocation.
- An empty catch block defeats the purpose of exceptions.
- If you choose to ignore an exception, the catch block should contain a comment explaining why it is appropriate to do so, and the variable should be named ignored.
- Synchronization is required for reliable communication between threads as well as for mutual exclusion.
- Do not use
Thread.stop
. - Synchronization is not guaranteed to work unless both read and write operations are synchronized.
- Confine mutable data to a single thread.
- When multiple threads share mutable data, each thread that reads or writes the data must perform synchronization.
- To avoid liveness and safety failures, 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.
- When you are designing a mutable class, think about whether it should do its own synchronization (this should be the case only if there is a good reason to do so, and document your decision clearly).
- Given the difficulty of using wait and notify correctly, you should use the higher-level concurrency utilities instead.
- It is impossible to exclude concurrent activity from a concurrent collection; locking it will only slow the program.
- Use
ConcurrentHashMap
in preference toCollections.synchronizedMap
. - For interval timing, always use
System.nanoTime
rather thanSystem.currentTimeMillis
. - Always use the wait loop idiom to invoke the
wait
method; never invoke it outside of a loop. - There is seldom, if ever, a reason to use
wait
andnotify
in new code.
- The presence of the synchronized modifier in a method declaration is an implementation detail, not a part of its API.
- To enable safe concurrent use, a class must clearly document what level of thread safety it supports.
- Immutable
- Unconditionally thread-safe
- Conditionally thread-safe
- Not thread-safe
- Thread-hostile
- Lock fields should always be declared final.
- Under most circumstances, normal initialization is preferable to lazy initialization.
- If you use lazy initialization to break an initialization circularity, use a synchronized accessor.
- If you need to use lazy initialization for performance on a static field, use the lazy initialization holder class idiom.
private static class FieldHolder {
static final FieldType field = computeFieldValue();
}
private static FieldType getField() {
return FieldHolder.field;
}
- If you need to use lazy initialization for performance on an instance field, use the double-check idiom.
private volatile FieldType field;
private FieldType getField() {
FieldType result = field;
if (result == null) { // First check (no locking)
synchronized (this) {
if (field == null) // Second check (with locking)
field = result = computeFieldValue();
}
}
return result;
}
- Any program that relies on the thread scheduler for correctness or performance is likely to be nonportable.
- Threads should not run if they aren’t doing useful work.
- Resist the temptation to “fix” the program by putting in calls to
Thread.yield
. Thread.yield
has no testable semantics.- Thread priorities are among the least portable features of Java.
- The best way to avoid serialization exploits is never to deserialize anything.
- There is no reason to use Java serialization in any new system you write.
- Never deserialize untrusted data.
- Prefer whitelisting to blacklisting when using an object deserialization filtering such as the
java.io.ObjectInputFilter
added in Java 9. - If you are designing a system from scratch, use a cross-platform structured-data representation such as JSON or protobuf instead of serialization.
- A major cost of implementing
Serializable
is that it decreases the flexibility to change a class’s implementation once it has been released. - A second cost of implementing
Serializable
is that it increases the likelihood of bugs and security holes. - A third cost of implementing
Serializable
is that it increases the testing burden associated with releasing a new version of a class. - Implementing
Serializable
is not a decision to be undertaken lightly. - Classes designed for inheritance should rarely implement
Serializable
, and interfaces should rarely extend it. - Inner classes should not implement `Serializable .
- Do not accept the default serialized form without first considering whether it is appropriate.
- The default serialized form is likely to be appropriate if an object’s physical representation is identical to its logical content.
- Even if you decide that the default serialized form is appropriate, you often must provide a readObject method to ensure invariants and security.
- Using the default serialized form when an object’s physical representation differs substantially from its logical data content has four disadvantages:
- It permanently ties the exported API to the current internal representation.
- It can consume excessive space.
- It can consume excessive time.
- It can cause stack overflows.
- Before deciding to make a field nontransient, convince yourself that its value is part of the logical state of the object.
- You must impose any synchronization on object serialization that you would impose on any other method that reads the entire state of the object.
- Regardless of what serialized form you choose, declare an explicit serial version UID in every serializable class you write.
- Do not change the serial version UID unless you want to break compatibility with all existing serialized instances of a class.
- When an object is deserialized, it is critical to defensively copy any field containing an object reference that a client must not possess.
- Anytime you write a
readObject
method, adopt the mind-set that you are writing a public constructor that must produce a valid instance regardless of what byte stream it is given. - Do not assume that the byte stream represents an actual serialized instance.
- Guidelines for writing a readObject method:
- For classes with object reference fields that must remain private, defensively copy each object in such a field. Mutable components of immutable classes fall into this category.
- Check any invariants and throw an
InvalidObjectException
if a check fails. The checks should follow any defensive copying. - If an entire object graph must be validated after it is deserialized, use the
ObjectInputValidation
interface. - Do not invoke any overridable methods in the class, directly or indirectly.
- If you depend on
readResolve
for instance control, all instance fields with object reference types must be declaredtransient
. - The accessibility of
readResolve
is significant. - Use enum types to enforce instance control invariants wherever possible.
- If this is not possible and you need a class to be both serializable and instance-controlled, you must provide a
readResolve
method and ensure that all of the class’s instance fields are either primitive or transient.
- Consider the serialization proxy pattern whenever you find yourself having to write a
readObject
orwriteObject
method on a class that is not extendable by its clients. This pattern is perhaps the easiest way to robustly serialize objects with nontrivial invariants.