Effective
Code should be reused rather than copied.
The dependencies between components should be kept to a minimum.
Errors should be detected as soon as possible after they are made,ideally at compile time.
本篇摘自 Joshua Bloch 《Effective Java》 一书。
Chapter-02 Creating and Destroying Objects
ITEM 1: Consider static factory methods instead of constructors
1 | Date d = Date.from(instant); |
ITEM 2: Consider a builder when faced with many constructor parameters
1 | NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8).calories(100).sodium(35).carbohydrate(27).build(); |
ITEM 3: Enforce the Singleton property with a private constructor or an enum type
1 | // Singleton with public final field |
ITEM 4: Enforce Noninstantiability with a private constructor
1 | // Noninstantiable utility class |
ITEM 5: Prefer dependency injection to hardwiring resources
1 | // Dependency injection provides flexibility and testability |
ITEM 6: Avoid creating unnecessary objects
1 | String s = new String("bikini"); // DON'T DO THIS! |
1 | // Hideously slow! Can you spot the object creation? |
This program gets the right answer, but it is much slower than it
should be, due to a one-character typographical error. The
variable sum
is declared as a Long
instead of a long
, which means that
the program constructs about 2^31 unnecessary Long
instances
(roughly one for each time the long i
is added to the Long sum
).
Changing the declaration of sum
from Long
to long
reduces the
runtime from 6.3 seconds to 0.59 seconds on my machine. The
lesson is clear: prefer primitives to boxed primitives, and
watch out for unintentional autoboxing.
ITEM 7: Eliminate obsolete(过时的,废弃的) object references
/* to let gc do its work */
1 | //stack pop |
ITEM 8: Avoid finalizers an cleaners
Finalizers are unpredictable, often dangerous, and generally unnecessary.
Their use can cause erratic behavior, poor performance, and portability problems. Finalizers have a few valid uses, which we’ll cover later in this item, but as a rule, you should avoid them.
1 | public class AutoCloseableTest implements AutoCloseable { |
ITEM 9: Prefer try-
with-resources to try-finally
1 | // try-finally - No longer the best way to close resources! |
Chapter-03 Methods Common to All Objects
ITEM 10: Obey the general contract when overriding equals
The equals
method implements an emphasized textequivalence relation. It has
these properties:
-
Reflexive(自反性): For any non-null reference value
x, x.equals(x)
must
returntrue
. -
Symmetric(对称性): For any non-null reference
valuesx
andy, x.equals(y)
must returntrue
if and only
ify.equals(x)
returnstrue
. -
Transitive(传递性): For any non-null reference values
x, y, z
,
ifx.equals(y)
returnstrue
andy.equals(z)
returnstrue
,
thenx.equals(z)
must returntrue
. -
Consistent(一致性): For any non-null reference values
x
andy
, multiple
invocations ofx.equals(y)
must consistently returntrue
or
consistently returnfalse
, provided no information used
inequals
comparisons is modified. -
For any non-null reference value
x, x.equals(null)
must
returnfalse
.
ITEM 11: Always override hashCode
when override equals
ITEM 12: Always override toString
ITEM 13: Override clone
judiciously(明智)
ITEM 14: Consider implementing comparable
Chapter-04 Classes and Interfaces
ITEM 15: Minimize the accessibility of classes and members
private protected public
ITEM 16: In public classes,use accessor methods, not public fields
ITEM 17: minimize mutability
To make a class immutable, follow these five rules:
-
Don’t provide methods that modify the object’s state (known as mutators).
-
Ensure that the class can’t be extended.
-
Make all fields final.
-
Make all fields private.
-
Ensure exclusive access to any mutable components.
Immutable objects are inherently thread-safe; they require no synchronization.
They cannot be corrupted by multiple threads accessing them concurrently.
This is far and away the easiest approach to achieve thread safety.
Since no thread can ever observe any effect of another thread on an immutable
object, immutable objects can be shared freely.
Not only can you share immutable objects, but they can share their internals.
1 | // Immutable class with static factories instead of constructors |
ITEM 18: Favor composition over inheritance
Inheritance is a powerful way to achieve code reuse, but it is not always the best tool for the job. Used inappropriately, it leads to fragile(脆弱) software.
Unlike method invocation, inheritance violates(违反) encapsulation(封装).
ITEM 19: Design and document for inheritance or else prohibit(禁止) it
ITEM 20: Prefer interfaces to abstract classes
Interfaces allow for the construction of nonhierarchical type frameworks.
ITEM 21: Design interfaces for posterity(后代)
ITEM 22: Use interfaces only to define types
ITEM 23: Prefer class hierarchies to tagged classes
ITEM 24: Favor static member classes over nonstatic
ITEM 25: Limit source files to a aingle top-level class
Chapter-05 Generics
ITEM 26: Don’t use raw types
Term | Example |
---|---|
Parameterized type | List<String> |
Actual type parameter | String |
Generic type | List<E> |
Formal type parameter | E |
Unbounded wildcard type | List<?> |
Raw type | List |
Bounded type parameter | <E extends Number> |
Recursive type bound | <T extends Comparable<T>> |
Bounded wildcard type | List<? extends Number> |
Generic method | static <E> List<E> asList(E[] a) |
Type token | String.class |
ITEM 27: Eliminate unchecked warnings
ITEM 28: Prefer Lists to Arrays
ITEM 29: Favor generic types
1 | public class Stack<E> { |
ITEM 30: Favor generic methods
1 | // Generic method |
1 | public interface Comparable<T> { |
The type parameter T
defines the type to which elements of the
type implementing Comparable<T>
can be compared.
1 | // Using a recursive type bound to express mutual comparability |
The type bound <E extends Comparable<E>>
may be read as “any
type E
that can be compared to itself,” which corresponds more or
less precisely to the notion of mutual comparability.
ITEM 31: Use bounded wildcards to increase API flexibility
ITEM 32: Combine generics and varargs judiciously
The purpose of varargs is to allow clients to pass
a variable number of arguments to a method.
1 | // Mixing generics and varargs can violate type safety! |
In fact, the Java libraries export several such methods, including Arrays.asList(T... a), Collections.addAll(Collection<? super T> c, T... elements),
and EnumSet.of(E first, E... rest)
.
1 | // UNSAFE - Exposes a reference to its generic parameter array! |
1 | // Safe method with a generic varargs parameter |
ITEM 33: Consider typesafe heterogeneous(异构) containers
1 | // Typesafe heterogeneous container pattern - API |
Chapter-06 Enums and Annotations
ITEM 34: Use enums instead of int constants
1 | // The int enum pattern - severely deficient! |
ITEM 35: Use instance fields instead of ordinals
Many enums are naturally associated with a single int
value.
All enums have an ordinal
method, which returns the numerical position of each enum constant in its type.
1 | public enum Ensemble { |
ITEM 36: Use EnumSet instead of bit fields
1 | // Bit field enumeration constants - OBSOLETE(已过时)! |
1 | // EnumSet - a modern replacement for bit fields |
ITEM 37: Use EnumMap instead of ordinal indexing
1 | class Plant { |
ITEM 38: Emulate extensible enums with interfaces
1 | // Emulated extensible enum using an interface |
ITEM 39: Prefer annotations to naming patterns
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
ITEM 40: Consistently use the override
annotation
ITEM 41: Use marker interfaces to define types
Chapter 07. Lambdas and Streams ???
ITEM 42: Prefer lambdas to anonymous classes
1 | // Anonymous class instance as a function object -obsolete! |
ITEM 43: Prefer method references to lambdas
All five kinds of method
references are summarized in the table below:
Method Ref Type | Example | Lambda Equivalent |
---|---|---|
Static | Integer::parseInt | str -> Integer.parseInt(str) |
Bound | Instant.now()::isAfter | Instant then = Instant.now(); t -> isAfter(); |
Unbound | String::toLowerCase | str -> str.toLowerCase() |
Class Constructor | TreeMap<K, V>::new | () -> new TreeMap<K, V> |
Array Constructor | int[]::new | len -> new int[len] |
1 | //Inappropriate use of string concatenation - Performs poorly! |
ITEM 64: Refer to objects by their interfaces
Item 51 says that you should use interfaces rather than classes as parameter types. More generally, you should favor the use of interfaces over classes to refer to objects. If appropriate interface types exist, then parameters, return values, variables, and fields should all be declared using interface types.
1 | // Good - uses interface as type |
ITEM 65: Prefer interfaces to reflection
ITEM 66: Use native methods judiciously
The Java Native Interface (JNI) allows Java programs to call native methods, which are methods written in native programming languages such as C or C++.
Historically, native methods have had three main uses.
They provide access to platform-specific facilities such as registries.
They provide access to existing libraries of native code, including legacy libraries that provide access to legacy data.
Finally, native methods are used to write performance-critical parts of applications in native languages for improved performance.
It is rarely advisable to use native methods for improved performance.
ITEM 67: Optimize judiciously
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.
ITEM 68: Adhere(采取) to generally accepted naming conventions(公约)
Chapter 10. Exceptions
ITEM 69: Use exceptions only for exceptional conditions
ITEM 70: Use checked exceptions for recoverable conditions and runtime exceptions for programming errors
Use checked exceptions for conditions from which the caller can reasonably be expected to recover.
Use runtime exceptions to indicate programming errors.
Therefore, all of the unchecked throwables you implement should subclass RuntimeException
(directly or indirectly).
ITEM 71: Avoid unnecessary use of checked exceptions
ITEM 72: Favor the use of standard exceptions
The most commonly reused exception type is IllegalArgumentException
.
This is generally the exception to throw when the caller passes in an argument whose value is inappropriate.
Another commonly reused exception is IllegalStateException
.
This is generally the exception to throw if the invocation is illegal because of the state of the receiving object.
NullPointerException
, UnsupportedOperationException
.
Do not reuse Exception
, RuntimeException
, Throwable
, or Error
directly.
ITEM 73: Throw exceptions appropriate to the abstraction
higher layers should catch lower-level exceptions and, in their place, throw exceptions that can be explained in terms of the higher-level abstraction.
ITEM 74: Document all exceptions thrown by each method
ITEM 75: Include failure-capture information in detail messages
To capture a failure, the detail message of an exception should contain the values of all parameters and fields that contributed to the exception.
ITEM 76: Strive for failure atomicity
ITEM 77: Don’t ignore exceptions
Chapter 11. Concurrency
1 | // Broken! - How long would you expect this program to run? |
You might expect this program to run for about a second, after which the main thread sets stopRequested
to true
, causing the background thread’s loop to terminate.
However, the program never terminates: the background thread loops forever!
The problem is that in the absence of synchronization, there is no guarantee as to when, if ever, the background thread will see the change in the value of stopRequested
made by the main thread.
In the absence of synchronization, it’s quite acceptable for the virtual machine to transform this code:
1 | while (!stopRequested) |
into this code:
1 | if (!stopRequested) |
This optimization is known as hoisting, and it is precisely what the OpenJDK Server VM does.
The result is a liveness failure: the program fails to make progress.
One way to fix the problem is to synchronize access to the stopRequested
field.
ITEM 79: Avoid excessive synchronization
ITEM 80: Prefer executors,tasks, and streams to threads
ITEM 81: Prefer concurrency utilities to wait
and notify
ITEM 82: Document thread safety
ITEM 83: Use lazy initialization judicious
ITEM 84: Don’t depend on the thread scheduler
Chapter 12. Serialization
ITEM 85: Prefer alternatives to java serialization
ITEM 86: Implement Serializable
with great caution
ITEM 87: Consider using a custom serialized form
ITEM 88: Write readObject
methods defensively
ITEM 89: For instance control, prefer enum types to readResolve
ITEM 90: Consider serialization proxies instead of serialized instances
参考
- [1] Effective Java / Joshua Bloch