Effective Java 3rd

2018-03-22  本文已影响0人  LaMole

Creating and Destroying Object

Consider static factory methods instead of constructors

advantage

1.they have names
2.they are not required to create a new object each time they're invoked
3.they can return an object of any subtype of their return type.->static factory methods for an interface named Type were put in a noninstantiable companion class named Types.(java.util.Collection and java.util.Collections) The returned objects are all nonpublic.
As of Java 8, it`s necessary to put the bulk of the implementation code behind these static methods in a separate package-private class.
Java 9 allows private static methods, but static fields and static member classes are still required to be public.
4.the class of the returned object can vary from call to call as a function of the input parameters.
In the OpenJDK implementation, they return an instance of one of two subclasses, depending on the size of the underlying enum type: if it has sixty-four or fewer elements, as most enum types do, the static factories return a RegularEnumSet instance, which is backed by a single long; if the enum type has sixty-five or more elements, the factories return a JumboEnumSet instance, backed by a long array.
5.the class of the returned object need not exist when the class containing the method is written.

disadvantage

1.classes without public or protected constructors cannot be subclassed.
2.they are hard for programmers to find.

Consider a builder when faced with many constructor parameters

// Builder Pattern
public class NutritionFacts {
    private final int servingSize;
    private final int servings;
    private final int calories;
    private final int fat;
    private final int sodium;
    private final int carbohydrate;
    public static class Builder {
        // Required parameters
        private final int servingSize;
        private final int servings;
        // Optional parameters - initialized to default values
        private int calories      = 0;
        private int fat           = 0;
        private int sodium        = 0;
        private int carbohydrate  = 0;
        public Builder(int servingSize, int servings) {
            this.servingSize = servingSize;
            this.servings    = servings;
        } 
        public Builder calories(int val)
            { calories = val;      return this; }
        public Builder fat(int val)
            { fat = val;           return this; }
        public Builder sodium(int val)
            { sodium = val;        return this; }
        public Builder carbohydrate(int val)
            { carbohydrate = val;  return this; }
        public NutritionFacts build() {
            return new NutritionFacts(this);
        } 
    } 
    private NutritionFacts(Builder builder) {
        servingSize  = builder.servingSize;
        servings     = builder.servings;
        calories     = builder.calories;
        fat          = builder.fat;
        sodium       = builder.sodium;
        carbohydrate = builder.carbohydrate;
    }
}

NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8)
           .calories(100).sodium(35).carbohydrate(27).build();
public abstract class Pizza {
    public enum Topping { HAM, MUSHROOM, ONION, PEPPER, SAUSAGE }
    final Set<Topping> toppings; 
    
    abstract static class Builder<T extends Builder<T>> {
        EnumSet<Topping> toppings = EnumSet.noneOf(Topping.class); 
        public T addTopping(Topping topping) {
            toppings.add(Objects.requireNonNull(topping));
            return self();
        }
        abstract Pizza build();
        // Subclasses must override this method to return "this"
        protected abstract T self();
    }
    Pizza(Builder<?> builder) {
        toppings = builder.toppings.clone(); // See Item 50
    }
} 

public class NyPizza extends Pizza {
    public enum Size { SMALL, MEDIUM, LARGE }
    private final Size size;
    
    public static class Builder extends Pizza.Builder<Builder> {
        private final Size size;
        public Builder(Size size) {
            this.size = Objects.requireNonNull(size);
        }
        @Override 
        public NyPizza build() {
            return new NyPizza(this);
        }
        @Override 
        protected Builder self() { return this; }
    }
        private NyPizza(Builder builder) {
            super(builder);
            size = builder.size;
        }
}
public class Calzone extends Pizza {
    private final boolean sauceInside;
    public static class Builder extends Pizza.Builder<Builder> {
        private boolean sauceInside = false; // Default
        public Builder sauceInside() {
            sauceInside = true;
            return this;
        }
        @Override
        public Calzone build() {
            return new Calzone(this);
        }
        @Override
        protected Builder self() { return this; }
    }
    private Calzone(Builder builder) {
        super(builder);
        sauceInside = builder.sauceInside;
    }
} 

Enforce the singleton property with a private constructor or an enum type

Singletons typically represent either a stateless object or a system component that is intrinsically unique.

Method of Singleton:

  1. public field approach
    it clear that the class is a singleton.
    it’s simpler
  2. static factory approach
    it gives you the flexibility to change your mind about whether the class is a singleton without changing its API.
    you can write a generic singleton factory if your application requires it
    a method reference can be used as a supplier
  3. declare a single-element enum
    can against serializable.
    a single-element enum type is often the best way to implement a singleton.
    Note that you can’t use this approach if your singleton must extend a superclass other than Enum

Enforce noninstantiability with a private constructor

Util class should be noninstantiability

Prefer dependency injection to hardwiring resources

It preserves immutability (Item 17), so multiple clients can share dependent objects (assuming the clients desire the same underlying resources).
Dependency injection is equally applicable to constructors, static factories (Item 1), and builders (Item 2).
do not use a singleton or static utility class to implement a class that depends on one or more underlying resources whose behavior affects that of the class, and do not have the class create these resources directly.

Mosaic create(Supplier<? extends Tile> tileFactory) { ... }

Avoid creating unnecessary objects

String s = new String("bikini"); // DON'T DO THIS! 

While String.matches is the easiest way to check if a string matches a regular expression, it’s not suitable for repeated use in performance-critical situations. The problem is that it internally creates a Pattern instance for the regular expression and uses it only once, after which it becomes eligible for garbage collection.
Creating a Pattern instance is expensive because it requires compiling the regular expression into a finite state machine.

As is often the case with lazy initialization, it would compli- cate the implementation with no measurable performance improvement

 // Hideously slow! Can you spot the object creation?
private static long sum() {
    Long sum = 0L; should-> long sum = 0L;
    for (long i = 0; i <= Integer.MAX_VALUE; i++)
        sum += i;
    return sum; 
} 

prefer primitives to boxed primitives, and watch out for unintentional autoboxing.

Avoiding object creation by maintaining your own object pool is a bad idea unless the objects in the pool are extremely heavyweight.

Eliminate obsolete object references

This occurs naturally if you define each variable in the narrowest possible scope

whenever a class manages its own memory, the programmer should be alert for memory leaks.

  1. make obsolete object null
  2. use WeakHashMap and EG->LinkedHashMap.removeEldestEntry

where will occur

  1. a class managers its own memory
  2. caches
  3. listeners and callbacks

Avoid finalizers and cleaners

Cleaners are a bit better than finalizers in this regard because class authors have control over their own cleaner threads, but cleaners still run in the background, under the control of the garbage collector, so there can be no guarantee of prompt cleaning.

Normally, an uncaught exception will terminate the thread and print a stack trace, but not if it occurs in a finalizer—it won’t even print a warning. Cleaners do not have this problem because a library using a cleaner has control over its thread.

To protect nonfinal classes from finalizer attacks, write a final finalize method that does nothing.

So what should you do instead of writing a finalizer or cleaner for a class whose objects encapsulate resources that require termination, such as files or threads? Just have your class implement AutoCloseable, and require its clients to invoke the close method on each instance when it is no longer needed, typically using try-with-resources to ensure termination even in the face of exceptions (Item 9).

==JAVA9 Cleaner will be read next time==

Prefer try-with-resources to try-finally

under try-finally circumstances, the second exception will completely obliterate the first one

implements AutoCloseable interface

These suppressed exceptions are not merely discarded; they are printed in the stack trace with a notation saying that they were suppressed.

You can also access them programmatically with the getSuppressed method, which was added to Throwable in Java 7.

Methods Common to All Objects

Obey the general contract when overriding equals

The easiest way to avoid problems is not to override the equals method, in which case each instance of the class is equal only to itself. This is the right thing to do if any of the following conditions apply:

@Override public boolean equals(Object o) {
    throw new AssertionError(); // Method is never called.
}

The Contract of overriding equals method is:
• 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.

The equals implementation for Timestamp does violate symmetry and can cause erratic behavior if Timestamp and Date objects are used in the same collection or are otherwise intermixed.

equals methods should perform only deterministic computations on memory-resident objects.

the instanceof operator is specified to return false if its first operand is null, regardless of what type appears in the second operand

Putting it all together, here’s a recipe for a high-quality equals method:

  1. Use the == operator to check if the argument is a reference to this object.
  2. Use the instanceof operator to check if the argument has the correct type. If not, return false.
  3. Cast the argument to the correct type.
  4. For each “significant” field in the class, check if that field of the argument matches the corresponding field of this object.

Here are a few final caveats:

An excellent alternative to writing and testing these methods manually is to use Google’s open source AutoValue framework, which automatically generates these methods for you, triggered by a single annotation on the class .

Always override hashCode when you override equals

Even if the two instances happen to hash to the same bucket, the get method will almost certainly return null, because HashMap has an optimization that caches the hash code associated with each entry and doesn’t bother checking for object equality if the hash codes don’t match.

Luckily it’s not too hard to achieve a fair approximation. Here is a simple recipe:

  1. Declare an int variable named result, and initialize it to the hash code c for the first significant field in your object, as computed in step 2.a. (Recall from Item 10 that a significant field is a field that affects equals comparisons.)

  2. For every remaining significant field f in your object, do the following:

    1. Compute an int hash code c for the field:
      1. If the field is of a primitive type, compute Type.hashCode(f), where Type is the boxed primitive class corresponding to f’ s type.
      2. If the field is an object reference and this class’s equals method compares the field by recursively invoking equals, recursively invoke hashCode on the field. If a more complex comparison is required, compute a “canonical representation” for this field and invoke hashCode on the canonical representation. If the value of the field is null, use 0 (or some other constant, but 0 is traditional).
      3. If the field is an array, treat it as if each significant element were a separate field. That is, compute a hash code for each significant element by applying these rules recursively, and combine the values per step 2.b. If the array has no significant elements, use a constant, preferably not 0. If all elements are significant, use Arrays.hashCode.
    2. Combine the hash code c computed in step 2.a into result as follows: result = 31 * result + c;
  3. Return result.

You must exclude any fields that are not used in equals com- parisons

A nice property of 31 is that the multiplication can be replaced by a shift and a subtraction for better performance on some architectures: 31 * i == (i << 5) - i. Modern VMs do this sort of optimi- zation automatically.

If you have a bona fide need for hash functions less likely to produce collisions, see Guava’s com.google.common.hash.Hashing [Guava].

This style of hash function is recommended for use only in situations where performance is not critical.

@Override public int hashCode() {
    return Objects.hash(lineNum, prefix, areaCode);
} 

If a class is immutable and the cost of computing the hash code is significant, you might consider caching the hash code in the object rather than recalculating it each time it is requested.

If you believe that most objects of this type will be used as hash keys, then you should calculate the hash code when the instance is created.

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.

Always override toString

The toString method is automatically invoked when an object is passed to println, printf, the string concatenation operator, or assert, or is printed by a debugger.

If you specify the format, it’s usually a good idea to provide a matching static factory or constructor so programmers can easily translate back and forth between the object and its string representation.

Whether or not you specify the format, provide programmatic access to the information contained in the value returned by toString.

Override clone judiciously

Creates and returns a copy of this object. The precise meaning of “copy” may depend on the class of the object. The general intent is that, for any object x, the expression

x.clone() != x 

will be true, and the expression

x.clone().getClass() == x.getClass()

will be true, but these are not absolute requirements. While it is typically the
case that

x.clone().equals(x)

will be true, this is not an absolute requirement.
By convention, the object returned by this method should be obtained by calling super.clone. If a class and all of its superclasses (except Object) obey this convention, it will be the case that

x.clone().getClass() == x.getClass(). 

By convention, the returned object should be independent of the object being cloned. To achieve this independence, it may be necessary to modify one or more fields of the object returned by super.clone before returning it.

covariant return types : An overriding method’s return type can be a subclass of the overridden method’s return type

So what does Cloneable do, given that it contains no methods? It determines the behavior of Object’s protected clone implementation: if a class implements Cloneable, Object’s clone method returns a field-by-field copy of the object; otherwise it throws CloneNotSupportedException. This is a highly atypical use of interfaces and not one to be emulated. Normally, implementing an interface says something about what a class can do for its clients. In this case, it modifies the behavior of a protected method on a superclass.

the Cloneable architecture is incompatible with normal use of final fields referring to mutable objects

invoking itself recursively will occus a stack overflow

Object’s clone method is declared to throw CloneNotSupportedException, but overriding methods need not. Public clone methods should omit the throws clause, as methods that don’t throw checked exceptions are easier to use (Item 71).

If you write a thread-safe class that implements Cloneable, remember that its clone method must be properly syn- chronized, just like any other method (Item 78).

A final approach to cloning complex mutable objects is to call super.clone, set all of the fields in the resulting object to their initial state, and then call higher- level methods to regenerate the state of the original object.

Alternatively, you may choose not to implement a working clone method, and to prevent subclasses from implementing one, by providing the following degenerate clone implementation:

// clone method for extendable class not supporting Cloneable
@Override
protected final Object clone() throws CloneNotSupportedException {
    throw new CloneNotSupportedException();
}

To recap, all classes that implement Cloneable should override clone with a public method whose return type is the class itself. This method should first call super.clone, then fix any fields that need fixing. Typically, this means copying any mutable objects that comprise the internal “deep structure” of the object and replacing the clone’s references to these objects with references to their copies.While these internal copies can usually be made by calling clone recursively, this is not always the best approach. If the class contains only primitive fields or refer- ences to immutable objects, then it is likely the case that no fields need to be fixed. There are exceptions to this rule. For example, a field representing a serial number or other unique ID will need to be fixed even if it is primitive or immutable.

A better approach to object copying is to provide a copy constructor or copy factory.

As a rule, copy functionality is best provided by constructors or factories. A notable exception to this rule is arrays, which are best copied with the clone method.

Consider implementing Comparable

• The implementor must ensure that sgn(x.compareTo(y)) == -sgn(y. compareTo(x)) for all x and y. (This implies that x.compareTo(y) must throw an exception if and only if y.compareTo(x) throws an exception.)
• The implementor must also ensure that the relation is transitive: (x. compareTo(y) > 0 && y.compareTo(z) > 0) implies x.compareTo(z) > 0.
• Finally, the implementor must ensure that x.compareTo(y) == 0 implies that sgn(x.compareTo(z)) == sgn(y.compareTo(z)), for all z.
• It is strongly recommended, but not required, that (x.compareTo(y) == 0) == (x.equals(y)). Generally speaking, any class that implements the Comparable interface and violates this condition should clearly indicate this fact. The recommended language is “Note: This class has a natural ordering that is inconsistent with equals.”
This is because the general contracts for these interfaces are defined in terms of the equals method, but sorted collections use the equality test imposed by compareTo in place of equals. It is not a catastrophe if this happens, but it’s something to be aware of.

the BigDecimal class, whose compareTo method is inconsistent with equals.

In a compareTo method, fields are compared for order rather than equality. To compare object reference fields, invoke the compareTo method recursively. If a field does not implement Comparable or you need a nonstandard ordering, use a Comparator instead. You can write your own comparator or use an existing one, as in this compareTo method for CaseInsensitiveString in Item 10:

Prior editions of this book recommended that compareTo methods compare integral primitive fields using the relational operators < and >, and floating point primitive fields using the static methods Double.compare and Float.compare. In Java 7, static compare methods were added to all of Java’s boxed primitive classes. Use of the relational operators < and > in compareTo methods is verbose and error-prone and no longer recommended.

In Java 8, the Comparator interface was outfitted with a set of comparator construction methods, which enable fluent construction of comparators. These comparators can then be used to implement a compareTo method, as required by the Comparable interface. Many programmers prefer the conciseness of this approach, though it does come at a modest performance cost

Comparator provides a way for you to provide custom comparison logic for types that you have no control over.

Summary Of Chapter 2 And 3

上一篇 下一篇

猜你喜欢

热点阅读