"Effective Java" Study Notes Chapter 2 Creating and Destroying Objects

Chapter 2 Creating and Destroying Objects

When and how objects are created, when and how to avoid creating objects, how to ensure that they can be destroyed in a timely manner, and how to manage the various cleanup actions that must occur before objects are destroyed.
### 1. Consider using static factory methods instead of constructors
Generally , the most common way to get an instance of a class somewhere is to provide a shared constructor, and another way is to provide a shared static factory method ) , which is just a static method that returns an instance of the class.

example:

 public static Boolean valueOf(boolean b){
    return b ? Boolean.TRUE:Boolean.FALSE;
 } 

Note that the static factory method is not the same as the factory method pattern in the design pattern. A class can be made available to its clients through static factory methods instead of constructors. Providing static factory methods instead of public constructors has several advantages:
#### Advantages
- static factory methods, they have Name
- For example, the BigInteter returned by the constructor BIgInteger(int, int, Random) may be prime, which is more clearly represented by a static factory method called BigInteger.probablePrime.
- Don't have to create a new object every time you call them
- This allows immutable classes to use pre-built instances, or cache built instances for reuse, avoiding the common unnecessary duplication of objects , because the program often requests to create the same object, then the cost of creating the object will be high. The Boolean.valueOf(boolean) method illustrates this technique. Static factory methods are also often used to implement the singleton pattern.
- they can return objects of any subtype of the original return type

Flexible static factory methods form the basis of Service Provider Frameworks such as JDBC.
Service provider framework: Multiple service providers implement a service, and the system provides multiple implementations for the service provider's clients and decouples them from multiple implementations. Three components: service interface , which is implemented by service providers; provider registration API , which is used by the system to register implementations and allow clients to access them; service access API , which is used by clients to obtain instances of services.
For JDBC, Connection is its service interface, DriverManager.registerDriver is the provider registration API, DriverManager.getConnection is the service access API, and Driver is the service provider interface.
For example, the following simple implementation contains a service provider interface and a default provider:

/**
 * Created by Newtrek on 2017/10/31.
 * 服务提供者接口
 */
public interface Provider {
//    提供服务实例,用服务接口返回
    Service newService();
}
/**
 * Created by Newtrek on 2017/10/31.
 * 服务接口
 */
public interface Service {
    // TODO: 2017/10/31  服务特有的方法 写在这儿
}

/**
 * Created by Newtrek on 2017/10/31.
 */
public class Services {
//    构造保护
    private Services(){}
//    provider映射表,保存注册的Provider
    private static final Map<String ,Provider> providers=new ConcurrentHashMap<>();
//    默认provider的名字
    public static final String DEFAULT_PROVIDER_NAME="<def>";
//    注册默认的Provider
    public static void registerDefaultProvider(Provider p){
        registerProvider(DEFAULT_PROVIDER_NAME,p);
    }
//    注册provider
    public static void registerProvider(String name,Provider p){
        providers.put(name,p);
    }

    /**
     * 静态工厂方法返回Service实例
     */
    public static Service newInstance(){
        return newInstance(DEFAULT_PROVIDER_NAME);
    }
    public static Service newInstance(String name){
        Provider p = providers.get(name);
        if (p==null){
            throw new IllegalArgumentException("No provider registered with name:"+name);
        }
        return p.newService();
    }
}
  • When creating instances of parameterized types, they make the code more concise

Example: Suppose HashMap provides this static factory

 public static <K,V> HashMap<K,V> newInstance(){
    return new HashMap<K,V>();
 } 

Then you can use the following concise code to get the instance.

 Map<String,List<String>> m=HashMap.newInstance(); 

It is useful to put these methods in your own utility class. But now java7, java8 have implemented the type parameter speculation of HashMap construction

shortcoming

  • A class cannot be subclassed without a public or protected initializer
  • They are virtually indistinguishable from other static methods
    • In the API documentation, they are not clearly identified in the API documentation like constructors, because it is very difficult to figure out how to instantiate a class for classes that provide static factory methods that are not constructors of. This disadvantage can be remedied by focusing on static factories in class or interface annotations and adhering to standard naming conventions. Below are some idiomatic names for static factory methods.
      • valueOf: is actually a type conversion method.
      • of: a more concise alternative to valueOf
      • getInstance: The returned instance is described by the method's parameters, but cannot be said to have the same value as the parameter. For Singleton, the method has no parameters and returns a unique instance.
      • newInstance: Like getInstance, but newInstance ensures that each instance returned is distinct from all other instances.
      • getType: Like getInstance, but used when the factory method is in a different class. Type represents the type of object returned by the factory method.
      • newType: Like newInstance, but used when the factory method is in a different class, Type represents the type of object returned by the factory method.

In short, both static factory methods and public constructors are useful, and we need to understand their respective strengths. A static factory is usually more appropriate, so don't be tempted to provide a public constructor without thinking about a static factory first.

2. Consider using a builder when encountering multiple constructor parameters

This is the Builder design pattern

3. Enhancing Singleton properties with private constructors or enumeration types

This is a note about the singleton pattern, choose the best singleton pattern implementation

4. Strengthen the ability to be non-instantiable through private constructors

Sometimes it is necessary to write classes that contain only static methods and static fields, these classes have a bad reputation because some people abuse such classes in object oriented languages ​​to write procedural programs, they do have Their benefits, such as the common tool class java.lang.Math, are all like this. For a class that cannot be instantiated like Founder, it is best to set its constructor as private.

E.g:

public class UtilityClass{
  private UtilityClass(){
      throw new AssertionError();
  }
}

5. Avoid creating unnecessary objects

In general, it's better to reuse an object than to create a new object with the same functionality every time you need it. If the object is immutable, it can always be reused.
Simple example: String

String s = new String("stringtest");//不要这样做,因为该语句每次执行的时候都会创建一个新的String实例,没必要
// 改进后的版本,这个版本只用了一个String实例,而不是每次执行的时候都创建一个新的实例。对于再同一台虚拟机中运行的代码,只要它们包含相同的字符串自字面常量,该对象就可以被重用。
String s = "stringtest";

For immutable classes that provide both static and constructor methods, you can often use static factory methods instead of constructors to avoid creating unnecessary objects.

Use primitives in preference to boxed primitives, beware of unintentional autoboxing.

Also don't make the mistake of thinking that this entry implies that "object creation is expensive and we should avoid creating objects whenever possible", instead, since small object constructors do very little explicit work, small object creation and The recycling action is very cheap.

It is not a good practice to avoid creating objects by maintaining your own object pool, unless the objects in the pool are very heavyweight, which is commonly used in database connection pooling.

6 Eliminate expired object references

Don't think that Java has a garbage collection mechanism, which can automatically manage memory and automatically collect garbage, so you can ignore it, but it is not.
Examples of memory leaks


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

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

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

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

    private void ensureCapacity(){
        if (elements.length == size){
            elements = Arrays.copyOf(elements,size*2+1);
        }
    }

}

There is no obvious error in this program. If the stack grows first and then shrinks, the object popped from the stack will not be garbage collected, because it is referenced in the array inside, and the stack internally maintains Expired references to these objects. Expired references are references that will never be released again.

The fix for this type of problem is simple: once the object reference has expired, just empty the apps.

It is not necessary for every object reference to be emptied once the program no longer uses it. Clearing object references should be the exception, not the norm, and the best way to get rid of expired references is to let the variable containing the object end its lifetime. In general, programmers should be wary of memory leaks as long as a class manages its own memory, and once an element is freed, any object references contained within that element should be emptied.

Another common source of memory leaks is the cache. Once you put an object in the cache, it is easily forgotten, leaving it in the cache long after it is no longer useful. WeakHashMap can be used

The third common source of memory leaks is listeners and other callbacks, usually unregistered or with weak references

Memory leaks usually don't manifest as obvious failures, so they can exist in a system for many years, often only by careful inspection of the code, with the help of heap analysis tools.

7 Avoid using finalizers

Finalizers are often unpredictable, dangerous, and generally unnecessary.

The C++ destructor is a conventional method for recycling the resources occupied by an object. It is the necessary counterpart of the constructor, and can also be used to recycle other non-memory resources. In Java, try-finally blocks are generally used to complete similar work

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324605552&siteId=291194637
Recommended