eziceice / blog

Personal blog for programming
4 stars 2 forks source link

Effective Java #2

Open eziceice opened 5 years ago

eziceice commented 5 years ago

Chapter 2. Creating and Destorying Objects

Consider static factory methods instead of constructors

Advantage:

public class A { private A() throws Exception { throw new Exception(); } }


- Static factory could return different object based on the different input parameters. Java library EnumSet is a good example for this. 

public static <E extends Enum> EnumSet noneOf(Class elementType) { Enum<?>[] universe = getUniverse(elementType); if (universe == null) throw new ClassCastException(elementType + " not an enum");

    if (universe.length <= 64)
        return new RegularEnumSet<>(elementType, universe);
    else
        return new JumboEnumSet<>(elementType, universe);
}

- The Static factory class of the returned object doesn't need to exist when the class containing the method is written.   
Service provider framework: 

   - Component 1: Service Interface, which represents an implementation.
   - Component 2: Provider registration API, which provides use to register implementations.
   - Component 3: Service access API, which clients use to obtain instances of the service. This may allow clients to specify criteria for choosing an implementation. In the absence of such criteria, the API returns an instance of a default implementation, or allows the client to cycle through all available implementations. **The service access API is the flexible static factory that forms the basis of the service provider framework.** 

**Limitation**

- Static factory methods cannot be extended compared to public or protected constructor. A class can extend from its parent class but the subclass cannot override its parent class static method. A static method is belonged to the class itself, not belonged to any instance of this class. In addition, it is defined in the compile time, not in the run time.

-  Normally a static method is hard for programmers to find. Constructor is very easy for programmers to distinguish the difference. It's better to put the appropriate Java doc there for other people to know this is a static factory method.    

## Consider a builder when faced with many constructor parameters

- **Static factories and constructors share a limitation: they do not scale well to large numbers of optional parameters.** There are two alternative ways but still they have some limitations: 

1. Sometimes the telescoping constructor pattern works, but it is hard to write client code when there are many parameters, and harder still to read it. 
2. An alternative way is using Java bean, but again, you need to call setter several times, which prevents the possibility of the immutable class or need to ensure thread safety. 

- **Build Pattern:** The NutritionFacts class is immutable, and all parameters default values are in one place. A single builders can have multiple varargs(可变参数) parameters because each parameter is specified in its own method. It is quite flexible and a single builder can be used to build multiple objects. 

// 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;
}   

}


- Builder Pattern is not suitable for less parameters (less than 5), it can cause some performance issues. However, it is still better than using the telescoping constructors because normally the parameters will go large in the end. Moreover, it is really helpful for the optional parameters. 

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

- Normally we use private constructor and static factory to implement singleton. However, if you have a singleton object, you do serialize and de-serialize on it, you could get two singleton objects. To maintain the singleton guarantee, declare all instance fields transient and provide a readResolve method. 
**The best way to preserve singleton is define the singleton object as a Enum. The limitation for this approach is you can't extent it from a superclass except Enum**

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


## Enforce noninstantiability with a private constructor 

- For some utility classes, normally people don't want them to be initialized and maintain them as static class. Attempting to enforce noninstantiability by making a class abstract does not work.  The best way is to add a private constructor and let it throw an exception. This will guarantee the class will never be instantiated under any circumstances.  As a side effect, this constructor also prevents the class from being subclassed. 

// Noninstantiable utility class public class UtilityClass { // Suppress default constructor for noninstantiability private UtilityClass() { throw new AssertionError(); } ... // Remainder omitted }


## Prefer dependency injection to handwriting resources

-  Static utility classes and singletons are inappropriate for classes whose behavior is parameterized by an underlying resource. 

// Pass the dependency to the constructor to initial different field in that class - Dependency injection public class SpellChecker { private final Lexicon dictionary; public SpellChecker(Lexicon dictionary) { this.dictionary = Objects.requireNonNull(dictionary); } public boolean isValid(String word) { ... } public List suggestions(String typo) { ... } }

In conclusion, 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. Instead, pass the resources, or factories to create them, into the constructor (or static factory or builder). 

## Avoid creating unnecessary objects

- An object can always be reused if it is immutable. You can often avoid creating unnecessary objects by using static factory methods in preference to constructors on immutable classes that provide both. In addition to reusing immutable objects, you can also reuse mutable objects if you know they won't be modified. For example, it is better to use Boolean.valueOf(String) instead of use Boolean(String) as the second method always create a new object in memory. 

// The performance can be greatly improved because every time this method is called, // a new Pattern is created and the regex is being compiled. It takes a lot of time. static boolean isRomanNumeral(String s) { return s.matches("^(?=.)M*(C[MD]|D?C{0,3})"

// The improvement version here, there will be only one Pattern which can be reused many times // It also improves the readability of this class private static final Pattern ROMAN = Pattern.compile( "^(?=.)M*(C[MD]|D?C{0,3})"


- Be careful of using autoboxing. Sometimes it can cause the performance issue because of creating some unnecessary objects. 

// Try to use primitive type instead of autoboxing. private static long sum() { Long sum = 0L; // many Long objects will be created in the loop, should use long instead for (long i = 0; i <= Integer.MAX_VALUE; i++) sum += i; return sum; }


## Eliminate obsolete object references

- Consider this part code below: In a very rare situation, it may cause the failure with **OutOfMemoryError**, which can be considered as a memory leak.

public class Stack { private Object[] elements; private int size = 0; private static final int DEFAULT_INITIAL_CAPACITY = 16;

public Stack() {
    elements = new Object[DEFAULT_INITIAL_CAPACITY];
}

public void push(Object e) {
    ensureCapacity();
    elements[size++] = e;
}

public Object pop() {
    if (size == 0)
        throw new EmptyStackException();
    return elements[--size];
}

/**
 * Ensure space for at least one more element,
 * roughly
 * doubling the capacity each time the array needs
 * to grow.
 */
private void ensureCapacity() {
    if (elements.length == size)
        elements = Arrays.copyOf(elements, 2 * size + 1);
}

}


- The reason above previous class is that if a stack grows and then shrinks, the objects that were popped off the stack will not be garbage collected, even if the program using the stack has no more references to them. This is because the stack maintain **obsolete references** to these projects. An obsolete reference is simply a reference that will never be referenced again.  The fix for this sort of problem is simple: **null out references once they become obsolete**. 

public Object pop(){ if(size==0) throw new EmptyStackException(); Object result=elements[--size]; elements[size]=null; // Eliminate obsolete reference return resul; }


- Generally speaking, **whenever a class manages its own memory, the programmer should be alert for memory leaks**. Whenever an element is freed, any object references contained in the element should be nulled out. 

- Another common source of memory leaks is **caches**. Sometimes the object references into a cache are always forgotten by programmers. Behaviors such as using WeakHashMap, ScheduledThreadPoolExecutor or LinkedHashMap should be used to ensure the caches will be cleaned after the objects are no longer used. 

- A third common source of memory leaks is **listeners and other callbacks**. One way to ensure that callbacks are garbage collected promptly is to store only weak references to them. For instance, by storing them only as keys in a WeakHashMap. 

## Avoid finalizers and cleaners 

- In Java 9, Cleaners are created to replace the Finalizers. Although Cleaners are less dangerous than Finalizers, but still unpredictable, slow and generally unnecessary. There is no guarantee they'll be executed promptly. You should never depend on a finalizer or cleaner to update persistent state. In addition, calling finalizer may throw an exception, which could leave the object in a corrupt state and terminate the application. Moreover, there is a severe performance penalty for using finalizers and cleaners. Next, Finalizers have a serious security problem, they open your class up to finalizer attacks. If you really want to use Finalizers, just have your class implement AutoCloseable. 

## Prefer try-with-resources to try-finally 

- **Conclusion: always use try-with-resources instead of try-finally!!**

- Try-catch-finally is not bad currently, but it is hard to read when you try to read multiple resources, the syntax is a disaster. In addition, in the code below, if there is an exception happened in the read method and then exception happened in the close method again. Under these circumstances, the second exception completely obliterates the first one and there is no record of the first exception in the exception stack trace.  

// resource needs to be closed in different finally block. It's hard to read and understand. static void copy(String src, String dst) throws IOException { InputStream in = new FileInputStream(src); try { OutputStream out = new FileOutputStream(dst); try { byte[] buf = new byte[BUFFER_SIZE]; int n; while ((n = in.read(buf)) >= 0) out.write(buf, 0, n); } finally { out.close(); } } finally { in.close(); } }


- try-with-resources handle this situation easily. It is shorter and more readable than the originals and it will suppress the exception in the invisible close method. In fact, multiple exceptions may be suppressed in order to preserve the exception that you actually want to see.  In addition, you still can add catch block in try-with resources clause, and it will catch the exception you want and do what you want. 

static String firstLineOfFile(String path) throws IOException { try (BufferedReader br = new BufferedReader(new FileReader(path))){ return br.readLine(); } }

static void copy(String src, String dst) throws IOException { try (InputStream in = new FileInputStream(src); OutputStream out = new FileOutputStream(dst)) { byte[] buf = new byte[BUFFER_SIZE]; int n; while ((n = in.read(buf)) >= 0) out.write(buf, 0, n); } }

eziceice commented 5 years ago

Chapter 3. Methods Common to All Objects

Obey the general contract when overriding equals

Some cases that you may don't need to implement equals():

Generally, the appropriate time to override equals is when we want to compare the values for that class, for example, Integer or String. Not only is overriding the equals method necessary to satisfy programmer expectations, it enables instances to serve as map keys or set elements with predicable, desirable behavior. There are five rules for implementing equals.

CaseInsensitiveString cis = new CaseInsensitiveString("Polish"); String s = "polish"; // cis.equals(s) return true // s.equals(cis) return false - case insensitive


 - **Transitivity**: if one object is equal to a second and the second object is equal to a third, then the first object must be equal to the third. **There is no way to extend an instantiable class and add value component while preserving the equals contract, unless you're wiling to forgo the benefits of object-oriented abstraction.** A good work around here is to use composition over inheritance.  **无法在扩展可实例化的类的同时,既增加新的值组件,同时又保留equals的约定.** *Note: In Java, java.sql.Timestamp and java.util.Date violate this feature. You shouldn't use Timestamp and Date at the same collection.*  

// Adds a value component without violating the equals contract public class ColorPoint { private final Point point; private final Color color; public ColorPoint(int x, int y, Color color) { point = new Point(x, y); this.color = Objects.requireNonNull(color); } /**

Here is the list for a high-quality equals method:

To achieve the best performance in equals method, you should first compare fields that are more likely to differ, less expensive to compare, or ideally, both. Don't compare the fields that are not part of an object's logical state, don't compare derived fields,

Few more important points:

// generated file: contains getter, equals, toString and hashcode. // No setter as it is immutable, try to use builder pattern if you want to initial values.

import javax.annotation.Generated;

@Generated("com.google.auto.value.processor.AutoValueProcessor")
final class AutoValue_AutoValueMoney extends AutoValueMoney {

private final String currency;
private final long amount;

AutoValue_AutoValueMoney(
String currency,
long amount) {
if (currency == null) {
throw new NullPointerException("Null currency");
}
this.currency = currency;
this.amount = amount;
}

@Override
public String getCurrency() {
return currency;
}

@Override
public long getAmount() {
return amount;
}

@Override
public String toString() {
return "AutoValueMoney{"

}

## Always override *hashCode* when you override *equals*

 -  **You must override *hashCode* in every class the overrides *equals*.** Failed to do will violate the general contract for hashCode, and also will cause trouble when you're using collections such as *HashMap* or *HashSet*. 
     - HashCode contract:
             - hashCode method must be return the same value of an object in an execution of an application. 
             - **(Key)**: **If two object are equal in equals() method, their hashCode must return the same value**.  You must exclude any fields are not used in equals comparisons, or you risk violating this provision of the hasCode contract.  
             - If two object are not equal in equals() method, their hashCode can be same or not same. However, distinct hashCode can improve the performance of hash tables.  

     - Objects.hash(Object... args) can be used to generate a better hashCode instead of manually creating one. However, this will cause some performance issue as this method contains many boxing and unboxing stuff. It is recommended for use it only when the performance is not critical. 
     - If the hashCode for a kind of class is used as the hash keys often, you can consider to cache the value in the object itself instead of calculating it every time when you call that method. 

Map<PhoneNumber, String> m = new HashMap<>(); m.put(new PhoneNumber(707, 867, 5309), "Jenny"); m.get(new PhoneNumber(707, 867, 5309)) // This will return null / HashCode method is not overridden in PhoneNumber object, which must be implemented first if this object needs be the key of a map. It's better to use immutable object as the key. /


## Always override *toString* 

 - 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, you should clearly document your intentions. 
 - Provide programmatic access to the information contained in the value returned by *toString*, otherwise programmers will try to use the value in *toString* , which is a really bad behavior. 
## Override *clone* judiciously 

 - *Clone* method is a protected method in Object class. If a class implements Cloneable, Object's clone method return a field-by-field copy of the object; otherwise it throws CloneNotSupportedException. **In practice, a class implementing Cloneable is expected to provide a properly functioning public 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 the clone.
 - Two types of copy in Apache:
     - Deepcopy: 拷贝原object中的所有数据,包括基本类型和object类型.
     - Shallowcopy: 拷贝原object中的所有基本类型,以及object类型的引用 - 引用指向的仍然还是原来的object.
 - The Clonealbe architecture is incompatible with normal use of final fields referring to mutable objects. If you really want to clone a object, all final modifiers need to be removed. 
 - As a rule, copy functionality is best provided by constructors or factories. **A notable exception of this rule is arrays, which are best copied with the clone method.** 
## Consider implementing *Comparable*

 - If you are writing a value class with an obvious natural ordering, such as alphabetical order, numerical order, or chronological order, you should implement the *Comparable* interface. 
 - A class that violates the *compareTo* contract can break other classes that depend on comparison, including TreeSet, TreeMap and the utitly classes Collections and Arrays. 
 - Same as equals method, there is no way to extend an instanitable class with a new value component while preserving the compareTo contract, unless you are willing to forgo the benefits of object-oriented abstraction, which is using component instead of inheritance. 
 - compareTo dosen't have to work across objects of different types: when confronted with objects of different types, compareTo is permitted to throw ClassCastException. 
 - It is strongly recommended that the compareTo method should generally return the same results as the equals method.  
 - 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. 

// Multiple-field Comparable with primitive fields
public int compareTo(PhoneNumber pn) {
int result = Short.compare(areaCode, pn.areaCode);
if (result == 0) {
result = Short.compare(prefix, pn.prefix);
if (result == 0)
result = Short.compare(lineNum, pn.lineNum);
}
return result;

 - Using comparator normally comes with a higher performance cost than comparable. 

private static final Comparator COMPARATOR =
comparingInt((PhoneNumber pn) -> pn.areaCode)
.thenComparingInt(pn -> pn.prefix)
.thenComparingInt(pn -> pn.lineNum);

public int compareTo(PhoneNumber pn) {
return COMPARATOR.compare(this, pn);
}

eziceice commented 5 years ago

Chapter 4. Classes and Interfaces

Minimize the accessibility of classes and members

In conclusion, 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 and 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.

Favor composition over inheritance

public class LimitedLengthCollection extends MyCollection { @Override public void add(String s) { if (s.length() == 5) { super.add(s); } } }

public class MyCollection { // Version 2.0 public void add(String s) { // add to inner array }

public void addMany(String[] s) {
    // iterate on each element and add it to inner array
}

}

LimitedLengthCollection c = new LimitedLengthCollection(); c.addMany(new String[] {"a", "b", "c"}); // Now you can add any size Strings - incorrect


 - Using composition could avoid all of the problems described for inheritance, it creates the new class a private field that references an instance of the existing class. 
 - Wrapper class means a class contains 'wraps' another class instance. The disadvantages of wrapper class are few.

      - It is not suited for use in callback frameworks, wherein objects pass self-references to other objects for subsequent invocations ("callbacks"). This is because the wrapper normally doesn't register in the controller to use callback method or the wrapped class and the wrapper both are registered which cause the duplicate issue or **it can say a wrapped object doesn't know of its wrapper.** 
      - Inheritance is appropriate only in circumstances where the subclass really is a subtype of the superclass. If not, then use composition instead. 

// basic class which we will wrap public class Model{ Controller controller;

Model(Controller controller){
    this.controller = controller; 
    controller.register(this); //Pass SELF reference
}

public void makeChange(){
    ... 
}

}

public class Controller{ private final Model model;

public void register(Model model){
    this.model = model;
}

// Here the wrapper just fails to count changes, 
// because it does not know about the wrapped object 
// references leaked
public void doChanges(){
    model.makeChange(); 
} 

}

// wrapper class public class ModelChangesCounter{ private final Model; private int changesMade;

ModelWrapper(Model model){
    this.model = model;
}

// The wrapper is intended to count changes, 
// but those changes which are invoked from 
// Controller are just skipped    
public void makeChange(){
    model.makeChange(); 
    changesMade++;
} 

}


 - 继承的时候有可能会打破封装的特性, 比如说上面的LimitedLengthCollection和MyCollection, 通常来说继承API的时候并不需要知道继承的类究竟是如何是实现的, 只需要知道它是如何使用的,然而上面的情况并不是这样, 你需要知道MyCollection的addAll()是如何使用的才能正确的使用子类里的addAll()方法,  这就打破了封装的特性. 
## Design and document for inheritance or else prohibit it 

 - The class must document precisely the effects of overriding any method. In other words, the class must document its self-use of overridable methods. For each public or protected method, the documentation must indicate which overridable methods the method invokes, in what sequence and how the results of each invocation affect subsequent processing. 
 - More generally, a class must document any circumstances under which it might invoke an overridable method. @implSpec - Implementation Requirements can be used to document the inner workings of the method. 
 - A class may have to provide hooks into its internal workings in the form of judiciously chosen protected methods or, in rare instances, protected fields. 
 - The only way to test a class designed for inheritance is to write subclasses. 
 - Constructors must not invoke overridable methods, because the superclass constructor runs before the subclass constructor, so the overriding method in the subclass will get invoked before the subclass constructor has run. Same thing need to be considered when implementing Cloneable or Seriablizable, becasue clone or readObject method do the same thing as constructor. **Neither clone nor readObject may invoke an overrdable method, directly or indirectly.** 

public class Super {
// Broken - constructor invokes an overridable method
public Super() {
overrideMe();
}
public void overrideMe() { }
}
// Here’s a subclass that overrides the overrideMe method, which is erroneously invoked by Super’s sole constructor:
public final class Sub extends Super {
private final Instant instant; // Blank final, set by constructor Sub() {
instant = Instant.now();
}
// Overriding method invoked by superclass constructor
@Override public void overrideMe() {
System.out.println(instant);
}
public static void main(String[] args) {
Sub sub = new Sub();
sub.overrideMe(); // one null - cuz the subclass has not been initialized // one currenty time }
}

 - In summary, designing a class for inheritance is hard to work. All of its self-use patterns must be documented, and once you've documented them, you must commit to them for the life of the class. 

## Prefer interfaces to abstract classes 

 - A skeletal implementation classes are called AbstractInterface, which is an abstract class implements some interface. (Example in Java - set, abstractSet, map and abstractMap.)

public interface Foo { void bar(); void baz(); }

// Abstract Interface private abstract class AbstractFoo implements Foo { ... }



 - For most implementors of an interface with a skeletal implementation class, extending this class is the obvious choice, but it is strictly optional. If a class cannot be made to extend the skeletal implementation, the class can always implement the interface directly. 
 - To summarize, an interface is generally the best way to define a type that permits multiple implementations and if you export a nontrivial interface, you should strongly consider providing a skeletal implementation to go with it. 

## Design interfaces for posterity 

 - The declaration for a default method includes a default implementation that is used by all classes that implement the interface but do not implement the default method. While the addition of default methods to Java makes it possible to add methods to an existing interface, there is no guarantee that these methods will work in all preexisting implementations. Defaults methods are "injected" into existing implementations without the knowledge or consent of their implementors. 
## Favor static member classes over nonstatic 

 - A nested class should exist only to serve its enclosing class. If a nested class would be useful in some other context, then it should be a top-level class. There are four kinds of nested classes: static member classes, nonstatic member classes, anonymous classes, and local classes. **All but the first kind are known as inner classes**. 
 - A common use of a static member class is as a public helper class, useful only in conjunction with its outer class. 
 - One common use of a nonstatic member class is to define an Adapter that allows an instance of the outer class to be viewed as an instance of some unrelated class. 
 - If you declare a member class that does not require access to an enclosing instance, always put the static modifier in its declaration. If you omit this modifier, each instance will have a hidden extraneous reference to its enclosing instance, storing this reference takes time and space. More seriously, it can result in the enclosing instance being retained when it would otherwise be eligible for garbage collection. 
 - Two common use of anonymous class, small functions of objects (replaced by lambda) and used in implementation of static factory method.
eziceice commented 5 years ago

Chapter5. Generics

Don't use raw types