Immutable shared memory

Table of contents

1. Immutable design

2. Flyweight mode

Three, final principle


1. Immutable design

How does String guarantee immutability?

First of all, there is a char array value in the String class, which is decorated with final , so it can only be assigned a value when it is constructed, and there is no chance to change its reference in the future.

There is a variable hash, which is used to cache the hash code of the string. It will only be generated when the string is called for the first time. Later, it will be cached in the variable to avoid subsequent calculations. The private one has no set method, so there is no way to change it.

The final modification is also added to the class to ensure that the methods in the class cannot be overridden, preventing the series from inadvertently destroying immutability

public final class String
     implements java.io.Serializable, Comparable<String>, CharSequence {
     /** The value is used for character storage. */
     private final char value[];

     /** Cache the hash code for the string */
     private int hash; // Default to 0
 
     // ...
}

final can only ensure that the reference remains unchanged. How does he ensure that the data inside does not change?

Looking at the following code, it can be seen that his constructor is constructed by copying to prevent other places from changing the value of the reference object and thus changing the String (this idea is called: protective copy )

public String(@NotNull String original){
    this.value = original.value;
    this.hash = original.hash;
}

public String(@NotNull char value[]){
    this.value = Arrays.copyOf(value,value.length);
}

It can be seen that the same is true for the source code of substring. When it returns, it also returns a new one, so that substring does not change the original string object.

public String substring(int beginIndex) {
     if (beginIndex < 0) {
         throw new StringIndexOutOfBoundsException(beginIndex);
     }
     int subLen = value.length - beginIndex;
     if (subLen < 0) {
         throw new StringIndexOutOfBoundsException(subLen);
     }
     return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);
}

public String(char value[], int offset, int count) {
     if (offset < 0) {
         throw new StringIndexOutOfBoundsException(offset);
     }
     if (count <= 0) {
         if (count < 0) {
             throw new StringIndexOutOfBoundsException(count);
         }
         if (offset <= value.length) {
             this.value = "".value;
             return;
         }
     }
     if (offset > value.length - count) {
         throw new StringIndexOutOfBoundsException(offset + count);
     }
     this.value = Arrays.copyOfRange(value, offset, offset+count);
}

This method of avoiding sharing by creating duplicate objects is called a protective copy

2. Flyweight mode

Since the protective copy will copy the object every time, it can be solved by Flyweight mode, which is one of the 23 design patterns, and it is used when the same type of object needs to be reused at the lower right of the number

Packages such as Boolean, Byte, Short, Long, Integer, and Character in JDK provide the valueOf method. For example, the valueOf of Long will cache Long objects between -128-127. Objects will be reused between this range, and only when the value is greater than this range New Long object

public static Long valueOf(long l) {
    final int offset = 128;
    if (l >= -128 && l <= 127) { // will cache
        return LongCache.cache[(int)l + offset];
    }
    return new Long(l);
}

How does this cache work? It has a static internal class LongCache that initializes to create an array of size 256, and then initializes the code block to assign values ​​to them in advance, and then calls valueOf to get it directly from the array to avoid repeated creation of objects

Note: The cache range of Byte, Short, and Long is -128-127, the cache of Character is 0-127, and the default of Integer is -128-127, the smallest one cannot be changed, but this 127 is a parameter that can be adjusted Djava.lang. Integer.integerCache.high to change. Boolean caches true and false

Three, final principle

The principle of final setting variables, if a member variable is not marked as final, then he will add a write barrier when assigning a value, and he can ensure that the items before the write barrier will not be reordered after the write barrier

The principle of obtaining final variables: If the final variable is within the scope of the flyweight cache, it will be directly fetched from the stack memory. If it is outside the flyweight cache, it will be fetched from the constant pool. If it is not modified by final, it will be stored in the heap. take

おすすめ

転載: blog.csdn.net/weixin_54232666/article/details/131342042