effective Java learning notes (Chapter 2)

Posted by noeledoran on Wed, 23 Feb 2022 13:43:40 +0100

Compiled from effective Java Chinese version 3

Non instantiation using private constructor

Sometimes I write some tool classes, which are all static methods and static member variables. At this time, I don't need to instantiate this class at all, so I can use this class normally.
How to ensure that this class will not be instantiated? When there is no constructor displayed in the class, a constructor will be generated by default. We can prevent this class from being instantiated by showing the privatization of constructors. Because the method is private, there is no way to call it outside the class.

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

However, the privatization of construction methods is equivalent to prohibiting subclass instantiation, because subclass instantiation will show or implicitly call the parent class construction.

Avoid creating unnecessary objects

Some objects can be created without repeated creation, such as constants. There are also some objects that are expensive to create, and it is recommended to cache them for reuse.
For example, you need a method to determine whether a string conforms to a regular expression:

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})$");
}

The matches method of String will create a Pattern instance for the regular expression internally and use it only once. Creating a Pattern instance is expensive because it requires compiling regular expressions into a finite state machine.
To improve performance, you can cache a Pattern instance during initialization and use it directly in the method:

public class CalculateUtil {
    private static final Pattern PATTERN = Pattern.compile("^(?=.)M*(C[MD]|D?C{0,3})"
                                            + "(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$");
        static boolean isRomanNumeral(String s) {
            return PATTERN.matcher(s).matches();
    }
}

Automatic boxing also creates unnecessary objects. Wired use basic types instead of packing basic types, and pay attention to unconscious automatic packing.

Eliminate out of date object references

There will also be memory leaks in java. Although there is a garbage collection mechanism, in some cases, references to objects will not be recycled.

  1. Unintentional object retention can lead to memory leaks. The object reference should be cleared in time, that is, element = null
  2. Leaks in cache. Once an object reference is put into the cache, it's easy to forget its existence and remain in the cache after it becomes irrelevant. You can use WeakHashMap to represent the cache.
  3. Listener and callback leaks. If there is no deregistration callback, they will accumulate.

To ensure garbage collection, you need to keep methods or objects weak references, such as using WeakHashMap.

Explain point 1 by implementing Stack

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

    // If a stack grows and shrinks, objects ejected from the stack will not be garbage collected, even programs that use the stack
    // These objects are no longer referenced. This is because the stack maintains obsolete references to these objects. 
    // An expired reference is simply a reference that will never be released.
    public Object pop() {
        if (size == 0) {
            throw new EmptyStackException();
        }
        return elements[--size];
    }

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

For the objects after pop in the above, the expired references are kept and have not been recycled. The object references should be cleared manually.

public class Stack {
    public Object pop() {
        if (size == 0) {
            throw new EmptyStackException();
        }
        Object element = elements[--size];
        System.out.println("1 :" + element);
        System.out.println("2 :" + elements[size]);
        elements[size] = null;
        System.out.println("3 :" + elements[size]);
        return element;
    }

    public static void main(String[] args) {
        Stack stack = new Stack();
        stack.push("abc");
        Object pop = stack.pop();
        System.out.println("4 :" + pop);
    }
}
  

Modify the pop method and use main for simple test. The input results are as follows:

1 :abc
2 :abc
3 :null
4 :abc

Avoid using Finalizer and Cleaner

  • One disadvantage of Finalizer and Cleaner mechanisms is that they can not be guaranteed to execute in time, and the execution time is arbitrary.
  • There is a serious security problem in the finalizer mechanism: finalizer mechanism attack

    The idea of finalizer mechanism attack is very simple: if an exception is thrown from the constructor or its serialization - readObject and readResolve methods - the finalizer mechanism of malicious subclasses can run on some of the constructor objects that should be. The finalizer mechanism can record the reference to the object in the static word attribute to prevent it from being garbage collected. Once a defective object is recorded, you can simply call any methods on the object that should not have been allowed to exist. Throwing an exception from a constructor should be sufficient to prevent the object from appearing; In the presence of the finalizer mechanism, it is not. Such an attack would have terrible consequences. The final class is not affected by the finalizer mechanism attack, because no one can write a malicious subclass of the final class. In order to protect non final classes from the finalizer mechanism, write a final finalize method, which does nothing.

Solution: encapsulate the resources that need to end for the object, such as threads or files. Implement the autoclosable interface and call the close method when not in use.

Legal uses of Finalizer and Cleaner mechanisms

  • If the resource owner forgets to call close, the resource can be released finally by using this mechanism. Although it is not clear when to release, it is better than never releasing
  • It is used to clean up local peer classes (a local non java object entrusted by ordinary objects). Because the local peer class is not a java object, jc does not know it. When the java object is recycled, the local peer class will not be recycled.

Use the try with resources statement instead of the try finally statement

When dealing with resources that must be closed, use the try with resources statement instead of the try finally statement. The generated code is simpler and clearer, and the generated exceptions are more useful. The try with resources statement is easier and error free when writing code that must close resources, and it is actually impossible to use the try finally statement.

Topics: Java