Chapter II Create and destroy objects

Article 1: use static factory method instead of constructor
advantage:
1. The static factory method has a name and is easier to use.
2. You don't have to create a new object every time you call them.
3. They can return objects of any subtype of the original return type.
4. The class of the returned object can change with each call, depending on the parameter value of the static factory method.
5. The class of the object returned by the method may not exist when writing the class containing the static method.

{(Note: service provider framework: multiple service providers implement a service, and the system provides multiple implementations for the client of the service provider and decouples them from multiple implementations)
(Note: important components of the service provider framework:
Service interface: implemented by the provider.
Provider registration API: used by providers to register implementations.
Service access API: used by the client to obtain service instances.
Service provider interface: it represents the factory object that generates the instance of the service interface.)
Example: JDBC
Service interface: connection
Provider registration API: drivermanager registerDriver
Service provider: drivermanager getConnection
Service provider: Driver
The service access API can return richer service interfaces than the provider needs. (bridge mode)
}
Disadvantages:
1. If a class does not contain a public or protected constructor, it cannot be subclassed
2. It's hard for programmers to find them.

Some common names of static factories:
From: Date b = Date.from(instant);
Of: Set faceCards = EnumSet.of(JACK,QUEEN,KING);
valueOf: BigInteger prime = BigInteger.valueOf(Integer.MAX_VALUE);
instance or getInstance: getstackwalker Luke = stackwalker getInstance(options);
create or newinstance: object newarray = array newInstance(classObject,arrayLen);
getType: Filestore fs = Files.getFileStore(path);
newType: BufferedReader br = Files.newBufferedReader(path);
type: Listlitany = Collections.list(legacyLitany);

Rule 2: consider using a constructor when you encounter multiple constructor parameters
Overlapping constructor mode: in this mode, the first constructor provided has only necessary parameters, the second constructor has one optional parameter, the third constructor has two optional parameters, and so on. The last constructor contains all optional parameters. This pattern works, but when there are many parameters, the client code will be difficult to write and still difficult to read.

JavaBeans mode: call a parameterless constructor to create an object, and use the setter method to set each necessary parameter and each related optional parameter. This mode makes up for the shortcomings of overlapping constructors and has high readability, but it has serious disadvantages: in the case of high concurrency, it may be in an inconsistent state in the construction process (similar to the unreal state in the database), while the JavaBeans mode eliminates the possibility of making classes immutable, This requires extra efforts for programmers to ensure their thread safe operation. (two threads may read a number at the same time.)

Builder mode (builder mode): instead of directly generating the desired object, it allows the client to call the constructor (or static factory) with all necessary parameters to get a builder object, and then the client calls a method similar to setter on the builder object to set each relevant optional parameter. Finally, the client calls the parameterless build method to generate objects that are usually immutable. This builder is usually a static member class of the class he builds.
(I think we can think so. The builder class creates a builder object with necessary elements, and this object is not immutable. The builder class also contains methods to add parameters, and the return value of these methods is the builder object that has added these parameters. Therefore, through continuous calls, we can create a builder object with any parameters we want. After that, I We then use the build method to create a private and basically immutable object)

The embodiment of the readability of the Builder pattern: simulates the named optional parameters

In addition, the builder pattern is also applicable to the class hierarchy. (my understanding is that the builder class can be inherited)

Covariant return type: subclass method declaration returns the subtype of the return type declared in the superclass, which allows the client to use these builders without converting the type.

If the constructor or static factory of a class has multiple parameters, the Builder pattern is a good choice when designing this class. The client code using Builder mode will be easier to read and write, and the Builder will be more secure than JavaBeans.

Example: nutrionfacts java
package first;

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{
		private final int servingSize;
		private final int servings;
		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;
	}
}

NutritionFactsTest.java

package first;

public class NutritionFactsTest {
	public static void main(String args[]) {
		NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8).calories(100).sodium(35).carbohydrate(27).build();
		System.out.println(cocaCola);
	}
}

Pizza.java

package first;

import java.util.EnumSet;
import java.util.Objects;
import java.util.Set;

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);//This method accepts an element type parameter elementType and references the class object of the element type of this enumeration set.
		public T addTopping(Topping topping) {
			toppings.add(Objects.requireNonNull(topping));	//Objects.requireNonNull determines whether the object is empty
			return self();
		}
		abstract Pizza build();
		protected abstract T self();
	}
	Pizza(Builder<?> builder){
		toppings = builder.toppings.clone();
	}
}

NyPizza.java

package first;

import java.util.Objects;

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

Calzone.java

package first;

public class Calzone extends Pizza{
	private final boolean sauceInside;
	public static class Builder extends Pizza.Builder<Builder>{
		private boolean sauceInside = false;
		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;
	}
}

PizzaTest.java

package first;

import first.NyPizza.Size;
import first.Pizza.Topping;

public class PizzaTest{
	public static void main(String args[]) {
		NyPizza pizza = new NyPizza.Builder(Size.SMALL).addTopping(Topping.SAUSAGE).addTopping(Topping.ONION).build();
		Calzone calzone = new Calzone.Builder().addTopping(Topping.HAM).addTopping(Topping.MUSHROOM).sauceInside().build();	
		System.out.println(pizza.toppings);
		System.out.println(calzone.toppings);
	}
}

Article 3: strengthen the Singleton attribute with a private constructor or enumeration type

Making the class Singleton will make his client-side testing very difficult, because it is impossible to replace the mock implementation for Singleton unless an interface acting as its type is implemented.

Single element enumeration types are often the best way to implement Singleton.

example:
Elvis.java

package first;

public class Elvis {
	//Singleton mode
	 public static final Elvis INSTANCE = new Elvis();	//Public static member, unique instance 
	 private Elvis(){}	//Constructor privatization
	 public static Elvis getInstance() {
		 return INSTANCE;
	 }
	 
	 //Prevents a new instance from being created when deserializing a serialized instance
	 private Object readResolve() {
		 return INSTANCE;
	 }
	 public void leaveTheBuilding() { }
}

Elviss.java

package first;

public enum Elviss {
	INSTANCE;
	public void leaveTheBuilding() { }
}

Article 4: strengthen the ability of non instantiation through private constructors

If it is impossible to force the class not to be instantiated by making it an abstract class, the class can be subclassed and the subclass can also be instantiated.
Solution: let this class contain a private constructor, which cannot be instantiated.

Article 5: give priority to dependency injection to reference resources

Static tool classes and singleton classes are not suitable for classes that need to reference the underlying resources. They all assume that all needs can be met with one underlying resource.
The simplest mode to meet this requirement is to transfer the resource to the constructor when creating a new instance, which is a form of dependency injection. The underlying resource is a dependency of the requirement, and the underlying resource is injected into it when creating the requirement. Dependency injection is applicable to any number of resources and any form of dependency. The object resources of dependency injection are immutable, so multiple resources can share dependent objects.
Another variation of this program pattern is to pass the resource factory to the constructor. The interface Supplier added in Java 8 is most suitable for representing the factory.

example:
Use the factory provided by the client to produce each mosaic
Mosaic create(Supplier<? Extends tile> tileFactory)

Although dependency injection greatly improves flexibility, testability and reusability, it can lead to messy large projects because it usually contains thousands of dependencies. This mess can be ended with a dependency injection framework.

Article 6: avoid creating unnecessary objects

For immutable classes that provide both static factory methods and constructors, static methods are usually preferred to avoid creating unnecessary objects.

Although string The matches method is the easiest way to check whether a string matches the regular expression, but it is not suitable for repeated use in performance-oriented situations. The main reason is that it creates a Pattern instance internally, but only once, and then it can be garbage collected. The cost of creating a Pattern instance is very high, so it is necessary to compile the regular expression into a finite state machine.
In order to improve performance, we should explicitly compile the regular expression into a Pattern instance (immutable), make it part of the class initialization, cache it, and reuse the same instance every time.

An adapter refers to an object that delegates functions to a backup object, thus providing an alternative interface for the later object. Because the adapter has no other status information except the backup object, it does not need to create multiple adapter instances for the specific adapter of a given object.

Another method of creating redundant objects is called automatic boxing, which allows programmers to mix basic types with boxed basic types, and automatically box and unpack as needed. Automatic boxing blurs the difference between basic types and boxed basic types, but it is not completely eliminated, at least in terms of performance.
Use basic types first rather than boxing basic types, and beware of unconscious automatic boxing.

Article 7: eliminate expired object references

Expired reference: a reference that will never be released.

Memory leak: unconscious object.

When will this happen?
Class may leak memory when it manages memory by itself. Once an element is released, any object references contained in the element should be cleared.
Once an object is referenced into the cache, it is easy to be forgotten, so that it is no longer useful and remains in the cache for a long time. The solution is that if such a cache is just to be implemented, as long as there is a reference to the key of an item outside the cache, the item is meaningful. The more common solution is that the cache should clear some useless items from time to time. This can be done by a background process, or it can be cleaned up when adding new entries to the cache. For more complex caches, you must directly use Java lang.ref.
The third common source of memory leaks is listeners and other callbacks, and the solution to this situation is to save only their weak references.

Article 8: avoid the use of termination methods and removal methods

Termination methods are usually unpredictable, dangerous and generally unnecessary. Using finalization methods can lead to behavior instability, performance degradation, and portability problems. The removal method is not as dangerous as the termination method, but it is still unpredictable, slow and generally unnecessary.

Time focused tasks should not be completed by termination methods or elimination methods.

You should never rely on finalization or cleanup methods to update important persistent states.

There is a very serious performance loss using the termination method and the cleanup method.

Finalization methods have a serious security problem: they open the door to class for finalization method attacks.
The exception thrown from the constructor should be enough to prevent the object from continuing to exist. With the termination method, this cannot be done.
In order to prevent non final classes from receiving termination method attacks, write an empty final finalize method.

In order to realize planting, it can be ensured as long as the class implements try with resources, that is, it automatically implements autoclosable and the close() method.

Benefits of termination and cleanup methods:
When the owner of a resource forgets to call its close method, the termination method or cleanup method can act as a "safety net".
The local peer is a local object, and the ordinary object delegates to a local object through the local method. Because the local peer is not an ordinary object, the garbage collector will not know it.
Because cleanup methods are unstable, cleanup methods are a suitable tool when resources are not critical and performance is acceptable

Article 9: try with resources takes precedence over try finally

When dealing with resources that must be closed, always give priority to try with resources rather than try finally. In this way, the code is more concise and clear, and the generated exceptions are more valuable. With this statement, it is easier to write the code correctly when using the resources that must be closed. Practice has proved that it is impossible to do this with try finally.

Tags: EffectiveJava

Posted by weyes1 on Wed, 18 May 2022 09:15:26 +0300