Effective Java

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Singleton with public final field
public class Elvis {
public static final Elvis INSTANCE = new Elvis();
private Elvis() { ... }
public void leaveTheBuilding() { ... }
}

// Singleton with static factory
public class Elvis {
private static final Elvis INSTANCE = new Elvis();
private Elvis() { ... }
public static Elvis getInstance() { return INSTANCE; }
public void leaveTheBuilding() { ... }
}

// Enum singleton - the preferred approach
public enum Elvis {
INSTANCE;
public void leaveTheBuilding() { ... }
}

ITEM 4: Enforce Noninstantiability with a private constructor

1
2
3
4
5
6
7
8
// Noninstantiable utility class
public class UtilityClass {
// Suppress default constructor for noninstantiability
private UtilityClass() {
throw new AssertionError();
}
...// Remainder omitted
}

ITEM 5: Prefer dependency injection to hardwiring resources

1
2
3
4
5
6
7
8
9
// Dependency injection provides flexibility and testability
public class SpellChecker {
private final Lexicon dictionary;
public SpellChecker(Lexicon dictionary) {
this.dictionary = Objects.requireNonNull(dictionary);
}
public boolean isValid(String word) { ... }
public List<String> suggestions(String typo) { ... }
}

ITEM 6: Avoid creating unnecessary objects

1
String s = new String("bikini"); // DON'T DO THIS!
1
2
3
4
5
6
7
// Hideously slow! Can you spot the object creation?
private static long sum() {
Long sum = 0L; // here: Should be Long or long?
for (long i = 0; i <= Integer.MAX_VALUE; i++)
sum += i;
return sum;
}

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
2
3
4
5
6
7
8
//stack pop
public Object pop() {
if (size == 0)
throw new EmptyStackException();
Object result = elements[--size];
elements[size] = null; // Eliminate obsolete reference /* to let gc do its work */
return result;
}

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class AutoCloseableTest implements AutoCloseable {

public void hello() {
System.out.println("hello");
}

@Override
public void close() throws Exception {
System.out.println("AutoCloseableTest#close.");
}

public static void main(String[] args) {
try (AutoCloseableTest ac = new AutoCloseableTest()) {
ac.hello();
} catch (Exception e) {
}
}

}

ITEM 9: Prefer try-with-resources to try-finally

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// try-finally - No longer the best way to close resources!
static String firstLineOfFile(String path) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(path));
try {
return br.readLine();
} finally {
br.close();
}
}

// try-with-resources - the the best way to close resources!
static String firstLineOfFile(String path) throws IOException {
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
return br.readLine();
}
}

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
    return true.

  • Symmetric(对称性): For any non-null reference
    values x and y, x.equals(y) must return true if and only
    if y.equals(x) returns true.

  • Transitive(传递性): For any non-null reference values x, y, z,
    if x.equals(y) returns true and y.equals(z) returns true,
    then x.equals(z) must return true.

  • Consistent(一致性): For any non-null reference values x and y, multiple
    invocations of x.equals(y) must consistently return true or
    consistently return false, provided no information used
    in equals comparisons is modified.

  • For any non-null reference value x, x.equals(null) must
    return false.

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:

  1. Don’t provide methods that modify the object’s state (known as mutators).

  2. Ensure that the class can’t be extended.

  3. Make all fields final.

  4. Make all fields private.

  5. 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Immutable class with static factories instead of constructors
public class Complex {
private final double re;
private final double im;

private Complex(double re, double im) {
this.re = re;
this.im = im;
}

public static Complex valueOf(double re, double im) {
return new Complex(re, im);
}
... // Remainder unchanged
}

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

TermExample
Parameterized typeList<String>
Actual type parameterString
Generic typeList<E>
Formal type parameterE
Unbounded wildcard typeList<?>
Raw typeList
Bounded type parameter<E extends Number>
Recursive type bound<T extends Comparable<T>>
Bounded wildcard typeList<? extends Number>
Generic methodstatic <E> List<E> asList(E[] a)
Type tokenString.class

ITEM 27: Eliminate unchecked warnings

ITEM 28: Prefer Lists to Arrays

ITEM 29: Favor generic types

1
2
3
public class Stack<E> {
...
}

ITEM 30: Favor generic methods

1
2
3
4
5
6
// 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;
}
1
2
3
public interface Comparable<T> {
int compareTo(T o);
}

The type parameter T defines the type to which elements of the
type implementing Comparable<T>can be compared.

1
2
// Using a recursive type bound to express mutual comparability
public static <E extends Comparable<E>> E max(Collection<E> c);

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
2
3
4
5
6
7
// Mixing generics and varargs can violate type safety!
static void dangerous(List<String>... stringLists) {
List<Integer> intList = List.of(42);
Object[] objects = stringLists;
objects[0] = intList; // Heap pollution
String s = stringLists[0].get(0); // ClassCastException
}

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
2
3
4
// UNSAFE - Exposes a reference to its generic parameter array!
static <T> T[] toArray(T... args) {
return args;
}
1
2
3
4
5
6
7
8
// Safe method with a generic varargs parameter
@SafeVarargs
static <T> List<T> flatten(List<? extends T>... lists) {
List<T> result = new ArrayList<>();
for (List<? extends T> list : lists)
result.addAll(list);
return result;
}

ITEM 33: Consider typesafe heterogeneous(异构) containers

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// Typesafe heterogeneous container pattern - API
public class Favorites {
public <T> void putFavorite(Class<T> type, T instance);
public <T> T getFavorite(Class<T> type);
}


// Typesafe heterogeneous container pattern - client
public static void main(String[] args) {
Favorites f = new Favorites();
f.putFavorite(String.class, "Java");
f.putFavorite(Integer.class, 0xcafebabe);
f.putFavorite(Class.class, Favorites.class);
String favoriteString = f.getFavorite(String.class);
int favoriteInteger = f.getFavorite(Integer.class);
Class<?> favoriteClass = f.getFavorite(Class.class);
System.out.printf("%s %x %s%n", favoriteString, favoriteInteger, favoriteClass.getName());
}


// Typesafe heterogeneous container pattern -implementation

public class Favorites {
private Map<Class<?>, Object> favorites = new HashMap<>();

public <T> void putFavorite(Class<T> type, T instance) {
favorites.put(Objects.requireNonNull(type), instance);
}

public <T> T getFavorite(Class<T> type) {
return type.cast(favorites.get(type));
}
}


Chapter-06 Enums and Annotations

ITEM 34: Use enums instead of int constants

1
2
3
4
5
6
7
8
9
10
11
// The int enum pattern - severely deficient!
public static final int APPLE_FUJI = 0;
public static final int APPLE_PIPPIN = 1;
public static final int APPLE_GRANNY_SMITH = 2;
public static final int ORANGE_NAVEL = 0;
public static final int ORANGE_TEMPLE = 1;
public static final int ORANGE_BLOOD = 2;

public enum Apple { FUJI, PIPPIN, GRANNY_SMITH }
public enum Orange { NAVEL, TEMPLE, BLOOD }

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
2
3
4
5
6
7
8
9
public enum Ensemble {
SOLO(1), DUET(2), TRIO(3), QUARTET(4), QUINTET(5),
SEXTET(6), SEPTET(7), OCTET(8), DOUBLE_QUARTET(8),
NONET(9), DECTET(10), TRIPLE_QUARTET(12);

private final int numberOfMusicians;
Ensemble(int size) { this.numberOfMusicians = size; }
public int numberOfMusicians() { return numberOfMusicians; }
}

ITEM 36: Use EnumSet instead of bit fields

1
2
3
4
5
6
7
8
9
10
11
12
// Bit field enumeration constants - OBSOLETE(已过时)!
public class Text {
public static final int STYLE_BOLD = 1 << 0; // 1
public static final int STYLE_ITALIC = 1 << 1; // 2
public static final int STYLE_UNDERLINE = 1 << 2; // 4
public static final int STYLE_STRIKETHROUGH = 1 << 3; // 8

// Parameter is bitwise OR of zero or more STYLE_ constants
public void applyStyles(int styles) { ... }
}

text.applyStyles(STYLE_BOLD | STYLE_ITALIC);
1
2
3
4
5
6
7
8
9
// EnumSet - a modern replacement for bit fields
public class Text {
public enum Style { BOLD, ITALIC, UNDERLINE,STRIKETHROUGH }

// Any Set could be passed in, but EnumSet is clearly best
public void applyStyles(Set<Style> styles) { ... }
}

text.applyStyles(EnumSet.of(Style.BOLD, Style.ITALIC));

ITEM 37: Use EnumMap instead of ordinal indexing

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
class Plant {
enum LifeCycle {
ANNUAL, PERENNIAL, BIENNIAL
}

final String name;
final LifeCycle lifeCycle;

Plant(String name, LifeCycle lifeCycle) {
this.name = name;
this.lifeCycle = lifeCycle;
}

@Override
public String toString() {
return name;
}
}


// Using ordinal() to index into an array - DON'T DO THIS!
Set<Plant>[] plantsByLifeCycle =(Set<Plant>[]) new Set[Plant.LifeCycle.values().length];
for(int i = 0;i<plantsByLifeCycle.length;i++)
plantsByLifeCycle[i]=new HashSet<>();

for(Plant p:garden)
plantsByLifeCycle[p.lifeCycle.ordinal()].add(p);

// Print the results
for(int i = 0;i<plantsByLifeCycle.length;i++){
System.out.printf("%s: %s%n", Plant.LifeCycle.values()[i], plantsByLifeCycle[i]);
}

// Using an EnumMap to associate data with an enum
Map<Plant.LifeCycle, Set<Plant>> plantsByLifeCycle =new EnumMap<>(Plant.LifeCycle.class);
for(Plant.LifeCycle lc:Plant.LifeCycle.values())
plantsByLifeCycle.put(lc,new HashSet<>());

for(Plant p:garden)
plantsByLifeCycle.get(p.lifeCycle).add(p);
System.out.println(plantsByLifeCycle);

ITEM 38: Emulate extensible enums with interfaces

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// Emulated extensible enum using an interface
public interface Operation {
double apply(double x, double y);
}

public enum BasicOperation implements Operation {
PLUS("+") {
public double apply(double x, double y) {
return x + y;
}
},
MINUS("-") {
public double apply(double x, double y) {
return x - y;
}
},
TIMES("*") {
public double apply(double x, double y) {
return x * y;
}
},
DIVIDE("/") {
public double apply(double x, double y) {
return x / y;
}
};
private final String symbol;

BasicOperation(String symbol) {
this.symbol = symbol;
}

@Override
public String toString() {
return symbol;
}
}


private static <T extends Enum<T> & Operation> void test(Class<T> opEnumType, double x, double y) {
for (Operation op : opEnumType.getEnumConstants())
System.out.printf("%f %s %f = %f%n", x, op, y, op.apply(x, y));
}

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
2
3
4
5
6
7
8
9
10
11
// Anonymous class instance as a function object -obsolete!
Collections.sort(words, new Comparator<String>() {
public int compare(String s1, String s2) {
return Integer.compare(s1.length(), s2.length());
}
});


// Lambda expression as function object (replaces anonymous class)
Collections.sort(words, (s1, s2) -> Integer.compare(s1.length(), s2.length()));

ITEM 43: Prefer method references to lambdas

All five kinds of method
references are summarized in the table below:

Method Ref TypeExampleLambda Equivalent
StaticInteger::parseIntstr -> Integer.parseInt(str)
BoundInstant.now()::isAfterInstant then = Instant.now(); t -> isAfter();
UnboundString::toLowerCasestr -> str.toLowerCase()
Class ConstructorTreeMap<K, V>::new() -> new TreeMap<K, V>
Array Constructorint[]::new len -> new int[len]

In summary, method references often provide a more succinct(简洁) alternative to lambdas. Where method references are shorter and clearer, use them; where they aren’t, stick with lambdas.

ITEM 44: Favor the use of standard functional interfaces

ITEM 45: Use Streams judiciously

The streams API was added in Java 8 to ease the task of performing bulk operations, sequentially or in parallel. This API provides two key abstractions: the stream, which represents a finite or infinite sequence of data elements, and the stream pipeline, which represents a multistage computation on these elements.

ITEM 46: Prefer side-effect-free functions in streams

ITEM 47: Prefer collection to stream as a return type

ITEM 48: Use caution when making streams parallel


Chapter 08. Methods

ITEM 49: Check parameters for validity

ITEM 50: Make defensive(防守) copies when needed

ITEM 51: Design method signatures carefully

ITEM 52: Use overloading judiciously

ITEM 53: Use varargs judiciously

ITEM 54: Return empty collections or arrays,not nulls

ITEM 55: Return optionals judiciously

ITEM 56: Write doc comments for all exposed API elements


Chapter 09. General Programming

ITEM 57: Minimize the scope of local variables

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.

ITEM 58: Prefer for-each loop to traditional for loops

ITEM 59: Know and use the libraries

ITEM 60: Avoid float and double if exact answers are required

The float and double types are particularly ill-suited for monetary calculations because itis impossible to represent 0.1 (or any other negative power of ten) as a float or double exactly.
The right way to solve this problem is to useBigDecimal, int, or long for monetary calculations.

ITEM 61: Prefer primitive types to boxed primitives

Java has a two-part type system, consisting of primitives, such as int, double, and boolean, and reference types, such as String and List. Every primitive type has a corresponding reference type, called a boxed primitive. The boxed primitives corresponding to int, double, and boolean are Integer, Double, and Boolean.

ITEM 62: Avoid strings where other types are more appropriate

ITEM 63: Beware the performance of string concatenation

Using the string concatenation operator repeatedly to concatenate n strings requires time quadratic in n.

The moral is simple: Don’t use the string concatenation operator to combine more than a few strings unless performance is irrelevant.Use StringBuilder’s append method instead.

1
2
3
4
5
6
7
8
9
10
11
//Inappropriate use of string concatenation - Performs poorly!
String result = "";
for (int i = 0; i < numItems(); i++)
result += lineForItem(i); // String concatenation


StringBuilder b = new StringBuilder();
for (int i = 0; i < numItems(); i++){
b.append(lineForItem(i));
}

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
2
// Good - uses interface as type
Set<Son> sonSet = new LinkedHashSet<>();

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

ITEM 78: Synchronize access to shared mutable data

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Broken! - How long would you expect this program to run?
public class StopThread {
private static boolean stopRequested;

public static void main(String[] args) throws InterruptedException {
Thread backgroundThread = new Thread(() -> {
int i = 0;
while (!stopRequested)
i++;
});
backgroundThread.start();
TimeUnit.SECONDS.sleep(1);
stopRequested = true;
}
}

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
2
while (!stopRequested)
i++;

into this code:

1
2
3
if (!stopRequested)
while (true)
i++;

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