Creating and Destroying Objects

When and how to create objects, when and how to avoid creating them, how to ensure they are destroyed in a timely manner, and how to manage any cleanup actions that must precede their destruction.

Introduce

EffectiveJava third edition reading notes, if you think the translation is wrong or the content is wrong, please contact me, please correct. Original link.

Item 1: Consider static factory methods instead of constructors.

Sometimes we create an object without necessarily calling the object’s constructor, but rather in the form of static factory methods, which often provide better performance and memory benefits.

For example, if we create a new Boolean object, we can use the Boolean valueOf method instead of the constructor method:

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

This avoids repeating the construction of Boolean objects, and instead uses objects that are already preconstructed inside the Boolean, reducing the burden on the JVM.

The static factory approach has its advantages and disadvantages.

Advantage:

  • Static factory methods have their own method names. A good method name not only makes it easier to use, but also improves the readability of the code. In the constructor, changing the order of the two arguments produces a new constructor, but it is very error-prone for the user to not look at the corresponding API documentation, with serious consequences. Static factory methods don’t have this problem and can be distinguished by their distinct method names.

  • Calling static methods sometimes does not necessarily require the creation of a new object. Many immutable classes tend to preinstance objects or cache part of the object in the construction of these objects can be reused to avoid creating duplicate objects, which can significantly improve the performance of the program. Such as Boolean internally defined TRUE and FALSE objects, valueOf can be used to obtain predefined object references.

  • The object returned by calling a static method does not have to be the object itself, but can be a subclass of the object. This gives you great flexibility when you return an object: the internal implementation is hidden, and when you need to change the internal implementation, the external call doesn’t need to change it, or even notice.

  • Calling static methods returns objects that can be differentiated based on the parameters passed. More different parameters can return different subtypes of objects, leading to better customization. For example, the noneOf method in EnumSet is distinguished by the number of enumerations. If the number of objects exceeds 64, the JumboEnumSet object is returned; otherwise, a RegularEnumSet object is returned.

  • When this method is called, the returned object does not need to be instantiated by itself at this time. For example, in our Service Provider Framework, when we request a certain Service from the Service Provider, we don’t need to instantiate it ourselves, we just need to know how to apply for it, and we don’t need to know how to implement it.

Disadvantage:

  • If the class does not have a public or protected constructor, the method cannot be subclassed or modified.

  • Difficult for users to find and use. Common static method names are: Of, from, valueOf, instance, getInstance, create, newInstance, getType(Type refers to the object name, such as getFileStore), newType, Type.

When we look at static factory methods and constructors, we can consider the advantages and disadvantages of both, and most of the time static factory methods provide a more flexible implementation.

Item 2: Consider a builder when faced with many constructor parameters.

Neither static factory methods nor constructors are good at handling cases with many arguments. When faced with objects with many parameters, programmers generally think of three methods: telescopic constructors, JavaBeans mode, and Builder mode.

//Telescoping constructor pattern - does not scale well
public class NutritionFacts {
	private final int servingSize;	// (mL) required
	private final int servings;		// (per container) required
	private final int calories;		// (per serving) optional
	private final int fat;			// (g/serving) optional
	private final int sodium;		// (mg/serving) optional
	private final int carbohydrate;	// (g/sering) optional
	
	public NutritionFacts(int servingSize, int servings) {
		this(servingSize, servings, 0);
	}
	
	public NutritionFacts(int servingSize, int servings, int calories) {
		this(servingSize, servings, calories, 0);
	}
	
	public NutritionFacts(int servingSize, int servings, int calories, int fat) {
		this(servingSize, servings, calories, fat, 0);
	}
	
	public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium) {
		this(servingSize, servings, calories, fat, sodium, 0);
	}
	
	public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium, int carbohydrate) {
		this.servingSize = servingSize;
		this.servings = servings;
		this.calories = calories;
		this.fat = fat;
		this.sodium = sodium;
		this.carbohydrate = carbohydrate; }}Copy the code

Retractable constructors can take care of some arguments that are too large, but when they get too large, they become very difficult to handle, and we must be careful to validate every value passed, as subtle errors can lead to the wrong construction of the object. In short, when the parameters become many, the code becomes very complex and difficult to read.

//JavaBeans Pattern - allow inconsistency, mandates mutability
public class NutritionFacts {
	private final int servingSize = -1; //Required
	private final int servings = -1; 	//Required
	private final int calories = 0;
	private final int fat = 0;
	private final int sodium = 0;
	private final int carbohydrate = 0;
	
	public NutritionFacts(a) {}
	
	//Setters
	public void setServingSize(int val) { servingSize = val; }
	public void setServings(int val) { servings = val; }
	public void setCalories(int val) { calories = val; }
	public void setFat(int val) { fat = val; }
	public void setSodium(int val) { sodium = val; }
	public void setCarbohydrate(int val) { carbohydrate = val; }}Copy the code

When using JavaBeans patterns, methods are usually called like this:

NutritionFacts cocaCola = new NutritionFacts();
cocaCola.setServingSize(240);
cocaCola.setServings(8);
cocaCola.setCalories(100); .Copy the code

The constructed parts can be divided into many rows, in which the object is not guaranteed to be consistent, and there is no mandatory method to ensure consistency, when the wrong call to inconsistent objects, can cause serious consequences (multi-threaded access). This also results in JavaBean objects not being immutable and requiring additional work to ensure thread-safety.

Combining the security of the telescopic constructor with the readability of the JavaBeans pattern, a new pattern was formed: the Builder pattern. Instead of creating objects directly, the Builder mode sets various properties through the Builder object, and finally builds an object by calling the Build method.

//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 valueOf
		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(a) {
			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; }}Copy the code

With this pattern, the NutritionFacts class is immutable, and the feature of returning new Builder via setter methods of Builder can be called using the Fluent API.

NutritionFacts cocaCola = new NutritionFacts.builder(240.8).calories(100).sodium(35).carbohydrate(27).build();
Copy the code

Such code is very easy to write and, more importantly, very easy to read. The validation of parameters is not implemented here. It can be quickly checked and implemented in the Builder constructor or inside the set method of a single property or in the build method.

Builder mode also supports class inheritance, through two parallel class inheritance, each class corresponds to its own parent class.

//Builder pattern for class hierarchies
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(a);
		
		// Subclasses must override this method to return "this"
		protected abstract T self(a);
	}
	
	Pizza(Builder<?> builder) {
		toppings = builder.toppings.clone();
	}
}
Copy the code

Pizza.Builder uses generics and also adds a self method, which allows method chain calls to be perfectly applied to subclasses without conversion.

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 MyPizza build(a) {
			return new NyPizza(this);
		}
		
		@Override
		protected Builder self(a) {
			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(a) {
			sauceInside = true;
			return this;
		}
		
		@Override 
		public Calzone build(a) {
			return new Calzone(this);
		}
		
		@Override
		protected Builder self(a) {
			return this; }}private Calzone(Builder builder){
		super(builder); sauceInside = builder.sauceInside; }}Copy the code

Build is declared to return the corresponding subclass, such as NyPizza.Builder returns NyPizza, and calzone. Builder returns Calzone. This technique, in which a method of a subclass declares a type that returns a subclass, is called a covariant return type. And the way we construct it is as follows:

NyPizza pizza = new NyPizza.Builder(SMALL).addTopping(SAUSAGE).addTopping(ONION).build();
Calzone calzone = new Calzone.Builder().addTopping(HAM).sauceInside().build();
Copy the code

The Builder pattern also has its drawbacks. In order to create an object, you must first create a Buidler object of that object, which may be fine in general, but may not be appropriate for extreme performance. The Builder pattern is also more detailed and complex than the telescopic constructor, and it is often worth the tradeoff. This is generally recommended when there are more than four or more parameters.

In summary, Builder mode is a good choice when you are designing a class with many member variables (especially if some parameters are optional).

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

In the singleton pattern, a class is instantiated only once. The singleton pattern often represents a state invariant object (such as a function) or a unique system component. At the same time, the singleton pattern is very difficult to test, and there is no way to simulate the implementation of the singleton (unless it implements the interface).

There are generally two implementations, both based on private constructors and providing a static public method to access instantiated objects.

The first exposes its statically instantiated object:

//Singleton with public final field
public class Elvis {
	public static final Elvis INSTANCE = new private Elvis(a){}; . }Copy the code

When the Elvis class is initialized, the instance object is initialized. When initializing, use public static final to define instances that are guaranteed to be initialized only once. The constructor is then privatized, ensuring that the class cannot be inherited and is exclusive. In addition to a situation: through reflection, set permissions constructor: AccessibleObect. SetAccessible method. If you need to prevent this attack, throw an exception in the constructor to prevent it from being called a second time to create the instance.

The second method provides a static fetch method to get instance objects:

// Singleton with static factory
public class Elvis {
	private static final Elvis INSTANCE = new Elvis();
	private Elvis(a) {};
	
	public static Elvis getInstance(a) {returnINSTANCE; }... }Copy the code

All calls to elvis.getInstance return the same instance reference, and again no other Elvis instances will be created.

Using the second method has many advantages: it gives you the option to implement a singleton (if not, you can change the method you get without changing the API), or it can be used to create a generic singleton factory. Using a static factory can also be used as an Supplier, as in: Elvis::instance. But if none of the above is needed, the first option is still the better choice.

But if you want to serialize a singleton, simply implementing the Serializable interface is not enough (because you can deserialize to get a new instance). To prevent this problem, you can override the deserialization method to return the current singleton.

//readResolve method to preserve singleton property
private Object readResolve(a) {
	//Return the one true Elvis and let the garbage collector
	//take care of Elvis impersonator
	return INSTANCE;
}
Copy the code

There is a third way to implement the singleton pattern:

public enumElvis { INSTANCE; . }Copy the code

Enumerations make it easy to implement the singleton pattern and prevent multi-instantiation attacks (serialization and reflection). Strange as it may seem, this is the best way to implement a singleton. One drawback is that you can’t inherit (consider interfaces at this point).

Item 4: Enforce noninstantiability with a private constructor

In programming, we occasionally want to write classes that contain static methods and static properties that we can call as a utility class. There is no need to instantiate a class like this, but we tend not to (or forget to) give it a constructor, which can be a problem. The JVM assigns a public constructor to all classes by default (if you don’t explicitly declare it). To prevent the utility class from being instantiated, we should create a private constructor that overrides the default constructor, making the class uninstantiable.

//Noninstantiable utility class
public class UtilityClass {
	//Suppress default constructor for noninstantiability 
	private UtilityClass(a) {
		throw newAssertionError(); }...// Remainder omitted
}
Copy the code

In this way, we can ensure that the class is not instantiable. This also has some negative effects: it cannot be inherited (subclasses need to call the constructor of their parent class). But for utility classes, it’s acceptable.

item 5: Prefer dependency injection to hardwiring resource

Many classes depend on one or more underlying Resources. Here’s an example:

//Inappropriate use of static utility - infelxible & untestable!
public class SpellChecker {
	private static finalLexicon dictionary = ... ;private SpellChecker(a) {} // Noninstantiable
	
	public static boolean isValid(String word) {... }public static List<String> suggestions(String typo) {... }}Copy the code

SpellChecker relies on dictionaries, but designing for instance-free objects and singletons is generally inappropriate: this becomes very difficult to modify and test when you need to change dependent resources, or when you support multiple dependent resources. Static utility classes and singletons are inappropriate ways to implement a class that depends on other resources. There is a simple pattern that satisfies this requirement: pass a resource in a constructor when you create an instance. This is an example of dependency injection.

public class SpellChecker {
	private final Lexicon dictionary;
	
	public SpellChecker(Lexicon dictionary) {
		this.dictionary = Objects.requireNonNull(dictionary);
	}
	
	public boolean isValid(String word) {... }... }Copy the code

With this mode, we can inject any resource while maintaining immutability. Although dependency injection can greatly improve flexibility and testability, it can also have the side effect of making the project code very cluttered when the project is large. This can be done with dependency injection frameworks such as Dagger,Guice,Spring, etc.

In summary, when your class needs to rely on other resources, do not use singletons and static utility class methods to do so, and do not directly create a resource object. Passing an object in through dependency injection will significantly improve flexibility, reuse, and testability.

Item 6: Avoid creating unnecessary objects.

Sometimes you reuse objects that don’t change, instead of creating objects every time. Such as:

String s = new String("bikini");
Copy the code

When used, this statement creates a String each time. If this statement occurs inside a loop or a frequently called method, thousands of strings will be created (unneeded). The improved version is also simple:

String s = "bikini";
Copy the code

This statement creates a String object, and subsequent calls call a reference to that object instead of creating a new object.

In general, we should use static factory methods instead of constructors when creating objects. For some immutable objects, many preconstructed objects are already cached internally, and constructors can be used to avoid repeated construction of these objects. Such as Boolean. The valueOf (String).

Some object calls are very expensive. If you need to reuse these expensive objects, it is recommended that you cache the object to avoid repeated calls. Unfortunately, many times when you create these expensive objects, it’s not obvious or easy to ignore.

If we need to verify that a String is a Roman numeral:

//Performance can be greatly improved
static boolean isRomanNumeral(String s) {
	return s.matches("^ (? =) M*(C[MD]|D? C {0, 3})"
		+ "(X[CL]|L? X {0, 3}) (I (XV) | V? I {0, 3}) $");
}
Copy the code

This seems perfectly normal and the simplest validation method, but it can be a performance burden if called many times. This is because a Pattern instance is created inside the method to validate the value, but the Pattern instance is used only once.

To improve performance, we can cache this object in advance and reuse it:

//Reusing expensive object for improved performance
public class RomanNumberals {
	private static final Pattern ROMAN = Pattern.compile("^ (? =) M*(C[MD]|D? C {0, 3}) (X (CL) | L? X {0, 3}) (I (XV) | V? I {0, 3}) $");
	
	static boolean isRomanNumberal(String s) {
		returnROMAN.matcher(s).matches(); }}Copy the code

Another possible way to create unnecessary objects is Autoboxing.

private static long sum(a) {
	Long sum = 0L;
	for (lont i = 0; i <= Integer.MAX_VALUE; i++) 
		sum += i;
	
	return sum;
}
Copy the code

This method will get the calculation right, but it will be slower. As soon as we change the definition of sum from Long to Long, the program will create 2^31 fewer Long objects. This is obvious: prioritize raw data types over boxing types, and be careful with the use of boxing types.

This is not to say that object creation must be avoided. For some small objects, the cost of creation and recycling is not high, and sometimes it is worth it for the readability and simplicity of the program. But creating unnecessary objects only affects performance and style.

Item 7: Eliminate obsolete object references

The biggest difference between Java and other languages is the MEMORY management of the JVM. Java dynamically reclaims stale memory through a memory reclamation algorithm. But this format has its drawbacks. Such as:

// Can you spot the "memory leak"?
public class Stack {
	private Object[] elements;
	private int size = 0;
	private static final int DEFAULT_INITIAL_CAPACITY = 16;
	
	public Stack(a) {
		elements = new Object[DEFAULT_INITIAL_CAPACITY];
	}
	
	public void push(Object e) {
		ensureCapacity();
		elements[size++] = e;
	}
	
	public Object pop(a) {
		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(a) {
		if (elements.length == size)
			elements = Arrays.copyOf(elements, 2 * size + 1); }}Copy the code

There is nothing wrong with this stack implementation. But this program has a Memeroy leak: a memory leak. When the stack expands to a large shrinkage (POP), the removed objects are not collected by the garbage collector, because elements still contains strong references to these objects that are useless. The solution to this problem is very simple: null empty these references.

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

This does not mean that programmers need to null every reference to an object that does not apply. It’s not necessary, and it doesn’t have to be the norm. The best way to eliminate references is to invalidate a variable that contains references beyond its scope. So when should we use NULL to clear references? As the example above shows, the programmer knows that objects after size are not available, but the garbage collector does not, which requires reasonable communication between the programmer and garbage collector (null clearing references). In general, a class should manage its own memory, and programmers should watch out for memory leaks. Once an Element is released, any references to that object should be assigned null.

Another easy memory leak is the cache. Once you put an object reference in the cache, it’s very easy to forget that the object will soon become unavailable. There are a number of workarounds, put the object in the cache into a WeakHashMap, and if the external application key becomes invalid, the object will also be removed. It is important to note that WeakHashMap focuses on the key rather than the content. Another approach is to define a lifetime and purge it periodically. In more complex cases, you should use java.lang.ref directly.

The third type of memory leak is Listeners and callbacks. If you implement an API that supports client registration callbacks, but does not show deletions, they will always add up. One solution is to store only weak references, such as storing them as keys in a WeakHashMap.

Memory leaks tend not to be an explicit problem, but may always be present in the system. This can only be done through code review or debugging tools such as heap profiler. These are very difficult to find and find, so it is important to learn some preventive knowledge in advance to prevent them.

Item 8: Avoid finalizers and cleaners

Finalizers are unpredictable, dangerous, and generally unnecessary functions. Using such functions can cause problems such as uncertain behavior, low performance, and non-portability. Destructors have been marked as deprecatd(deprecated) in Java9, but are still used in Java libraries. The alternative in Java9, the Cleaners, are also unpredictable, slow and often unnecessary. The danger with destructors is that they are executed in the background on a low-priority thread and cannot be guaranteed to be executed in a timely manner. If an exception occurs during execution, no exception is thrown, and objects in the queue will never execute a destructor.

The JVM makes no guarantee that the destructor will run correctly. It is not recommended to do any time-sensitive operations in destructors, or to rely on destructors to free some common resources. The use of destructors and Cleaner comes with serious performance costs. Similarly, Finalize method can bring serious security problems. When you privatize the constructor and throw an exception in order to prevent the class from being instantiated, you might run Finalize () method. Inside, you can connect the reference of the object instance to a static variable. This object will not be collected by the garbage collector. And this object should not exist, can lead to serious consequences. To protect against destructors, declare the destructor final and empty.

If you don’t want to write destructors or Cleaner, but objects that encapsulate resources (files, threads, etc.) need to be closed, you can implement the AutoCloseable interface and override the close() method to close the resource.

Similarly, destructors can be used in two reasonable ways. The first is for safe cleanup, for safety reasons, to do some data security, to free resources, lest programmers forget to call, you can put this part of the closure in a destructor. But before you use it, you need to weigh the uncertainty and cost of destructors. The second is an object used to reclaim some local methods. Local methods are not managed by the JVM, and their closure can be done through destructors.

In general, do not use Clearner, Finalizer, except for security reasons or to terminate some local method resources. But be aware of the uncertainties and performance costs of using these two methods.

Item 9: Prefer try-with-resource to try-finally

Try-with-resource is more efficient and readable than try-finally.

Java libraries contain many resources that must be closed by calling close. InputSream, OutputStream, etc. Closing resources is often forgotten by users, which can lead to serious consequences. Also, to be safe, we tend to close resources in the try-finally.

//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(); }}Copy the code

But when we call both resources, it becomes:

//try-finally is ugly when used with more than one resource!
static String copy(String path, String dst) throws IOException {
	InputStream br = new FileInputStream(path);
	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{ br.close(); }}Copy the code

It is very ugly and unsafe. In the first function, if exception 1 occurs in br.readline () for physical reasons and exception 2 occurs in finally when closing, exception 2 overwrites exception 1. When we debug, we usually want to get information about exception 1, not exception 2.

These issues were finally addressed in Java7, which introduced try-with-resource statements. To use this structure, the resource must implement the AutoCloseable interface. If you are writing a resource class, it is recommended that you implement this interface and close it.

//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))) {
			returnbr.readLine(); }}//Try-with-resources on multiple resources - short and sweet
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); }}Copy the code

The code is much simpler and much more readable. If an exception occurs in the readLine section and the close section, the former will suppress the latter and you will see the first one you want to see. Also the second one has not been removed and you can get the details by using the getSuppressed method.

In short, replace the try-finally with a try-with-resource for a better code experience, and exceptions will be what you need.