Avoid creating unnecessary objects-Chapter 2 Creating and Destroying Objects-Effective Java Study Notes 05

The content of the article comes from the book Joshua Bloch-Effective Java (3rd)-2018.chm

Chapter two

Create and destroy objects

Item 6 Avoid creating unnecessary objects

Avoid creating unnecessary objects Reusing
a single object and creating a new object are functionally equivalent. If an object is constant, it can always be reused

Here is an example of an error:

String s = new String("bikini");  // DON'T DO THIS!

The above statement creates a new unnecessary String object each time it is executed.
If this usage occurs in a loop or frequently called method, it will unnecessarily create millions of string instances. The
following improved method:

String s = "bikini";

Use a single string instance instead of creating a new instance every time it is executed. In addition, it can be guaranteed that the object will be reused by any other code running in the same virtual machine, which contains the same string text

If these two methods exist on immutable classes, they are more inclined to use static factory methods than to use constructors to avoid creating unnecessary objects

For example, the factory method Boolean.valueOf(String) is better than the constructor Boolean(String), which was deprecated in Java 9. The constructor must create a new object each time it is called, and the factory method does not need to do this, and it does not actually do it

In addition to reusing immutable objects, if you know mutable objects that are not modified, you can also reuse them

Some object creation is much more expensive than others. If you want to use such an "expensive object" repeatedly, it is best to cache it for reuse

Here is the simplest way to do this using regular expressions:

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

The problem with this implementation is that it relies on the String.matches matching method.
Although String.matches is the simplest method of matching, it is not suitable for repeated use in performance-critical situations. The reason for the problem is that it creates a pattern instance for the regular expression internally, and it is used only once, after which it will be garbage collected.
Creating a pattern instance is expensive because it needs to compile the regular expression into a finite state machine.
To improve performance, please explicitly compile the regular expression into a pattern instance (immutable), and cache it as part of the class initialization. And reuse the same instance every time the isRomanNumeral method is called:

// Reusing expensive object for improved performance
public class RomanNumerals {
    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 isRomanNumeral(String s) {
        return ROMAN.matcher(s).matches();
    }
}

When an object is immutable, it is obvious that it can be safely reused, but in other cases it is not so obvious, or even counter-intuitive.

Another way to create unnecessary objects is automatic boxing, which allows programmers to mix basic types and boxed basic types, automatically boxing and unboxing as needed. Automatic boxing will blur but will not eliminate the distinction between primitive and boxed primitive types.

Consider the following method, which calculates the sum of all positive int values. For this, the program must use long arithmetic, because the size of int is not large enough to hold the sum of all positive int values:

// Hideously slow! Can you spot the object creation?
private static long sum() {
    Long sum = 0L;
    for (long i = 0; i <= Integer.MAX_VALUE; i++)
        sum += i;

    return sum;
}

The program gets the correct answer, but due to a writing error of a character, it is much slower than it should be. Variable sum is declared as Long rather than long, which means that the program is constructed around 2 31 Ge unnecessary Long instances (each long i is added to the Long sum, about one instance).
On my machine, changing the declaration of sum from Long to long reduced the running time from 6.3 seconds to 0.59 seconds. The lesson is clear: prefer primitives than boxed primitives; pay attention to unintentional automatic boxing.

to sum up

This project should not be misunderstood to mean that object creation is expensive and should be avoided. In contrast, the cost of creating and recycling small objects whose constructors rarely work explicitly is low, especially in modern JVM implementations.

Corresponding to this item is the 50th defensive copy (protective copy). The current entry says, "Don't create new objects when you should reuse existing objects," and entry 50 says, "Don't reuse existing objects when you should create new objects."

Note that the cost of reusing objects when defensive replication is required is far greater than the cost of creating duplicate objects unnecessarily.

Guess you like

Origin blog.csdn.net/weixin_43596589/article/details/112761397
Recommended