Some talks about the String class

Some talks about the String class

As a frequently used String class, a qualified programmer needs to understand its original appearance.

Everyone knows that the basic types do not include the String class. Most of the objects in Java are stored in the heap, but the String object is a special case, which is stored in the constant pool;
when an object of the String class is created, it will first In the constant pool, it checks whether this class exists. If it exists, it will not be created. If it does not exist, it will be created. This part of the programmer is invisible.

String class:

public final class String	
    implements java.io.Serializable, Comparable<String>, CharSequence {
    
    }

Clicking on String will find that String is modified by final, that is, String cannot be inherited. At the same time, it implements Serializable interface to serialize and deserialize, implements Comparable to support string comparison, implements CharSequence interface to explain it Is a sequence of characters.

Member variables:

private final char value[];  
private int hash; //字符串的hashcode,默认是0
/** use serialVersionUID from JDK 1.0.2 for interoperability */
private static final long serialVersionUID = -6849794470754667710L;

value[] is used to store strings. String strings are actually composed of character arrays, and this array is also finalized, otherwise it will cause data insecurity.

Parameter structure:

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

This constructor is often used as an interview question, asking new String("abc"); How many objects have been created? The
answer is two, the string "abc" creates an object in the constant pool, new String() Create another object and put it in the heap.

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

Construct a String object through the entire char array parameter, and actually copy the parameter char array value to the char array of the String object.

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;
        }
    }
    // Note: offset or count might be near -1>>>1.
    if (offset > value.length - count) {
    
    
        throw new StringIndexOutOfBoundsException(offset + count);
    }
    this.value = Arrays.copyOfRange(value, offset, offset+count);
}

A part of the parameter group is intercepted to construct a String object. The specific part is determined by offset and count. Some parameter checks are done. If an illegal parameter is passed in, it will report an array out-of-bounds exception StringIndexOutOfBoundsException

//…

Common method:
Equals(): (Interview may ask: How to realize Equals?)

public boolean equals(Object anObject) {
    
      // Object是所有类直接或间接的父类
    if (this == anObject) {
    
       //首先是跟自己比,看是否是相同的对象,如果是直接返回true,且这里比的是内存地址
        return true;
    }
    if (anObject instanceof String) {
    
          //判断被比较的对象是不是String类的,只有是同类才有比下去的必要
        String anotherString = (String)anObject;
        int n = value.length;
        if (n == anotherString.value.length) {
    
      //比较长度是不是一样
            char v1[] = value;         //将比较的字符串转成字符数组
            char v2[] = anotherString.value;
            int i = 0;
            while (n-- != 0) {
    
         //循环遍历两个字符数组中对应的字符是否相等
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}

length():

public int length() {
    
    
    return value.length;
}

Obtaining the length of the string is actually returning the length of the internal array. Because the char array is immutable by final modification, the length of the content in the char array will not change as long as the construction is completed, so the length of the array can be directly returned here.

substring(int beginIndex):

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);   //如果beginIndex=0则返回原串,否则创建一个新的字符串返回。
}

Intercept a substring of the string. Intercept the content of the string from the specified position beginIndex (including this position) to the end of the string.

substring(int beginIndex, int endIndex):

public String substring(int beginIndex, int endIndex) {
    
    
    if (beginIndex < 0) {
    
                     //对输入的参数进行必要的效验
        throw new StringIndexOutOfBoundsException(beginIndex);
    }
    if (endIndex > value.length) {
    
    
        throw new StringIndexOutOfBoundsException(endIndex);
    }
    int subLen = endIndex - beginIndex;
    if (subLen < 0) {
    
    
        throw new StringIndexOutOfBoundsException(subLen);
    }
    return ((beginIndex == 0) && (endIndex == value.length)) ? this
            : new String(value, beginIndex, subLen);//如果beginIndex,endIndex是从0到字符串的长度,就返回本身,否则新建字符串返回
}

Intercepting the substring of the original string is similar to the previous method. The end position of the previous method is the end of the original string. This method is to specify the end position endIndex (not including this position). It should be noted that this method includes the head but not the tail.

hashCode():

public int hashCode() {
    
    
    int h = hash;
    if (h == 0 && value.length > 0) {
    
    
        char val[] = value;
        for (int i = 0; i < value.length; i++) {
    
    
            h = 31 * h + val[i];
        }
        hash = h;
    }
    return h;
}

The hashCode here overrides the hashCode method of Object.

The hashCode in java has two functions.
One: Object's hashCode returns the memory address of the object.
Two: The object-rewritten hashCode works normally with hash-based collections. Such hash collections include HashSet, HashMap, HashTable, etc. When comparing a large number of elements, comparing equals directly is inefficient. You can judge the hashCode first and then equals, because different objects may return the same hashCode (such as the hashCode of "Aa" and "BB"), so you sometimes need to compare Compare equals. HashCode only plays a supporting role.

In order to make the hashCode calculated by the string as less repetitive as possible, that is, to reduce the collision rate of the hash algorithm, the designer chose a multiplier of 31. There are two advantages to choosing 31. 1:31 is a prime number that is not too big or too small. It is one of the preferred prime numbers for hashCode multipliers. Others like 37, 41, and 43 are also good multiplier choices. 2: 31 can be optimized by JVM, 31 * i = (i << 5)-i. The principle of calculating hashCode is also very simple, that is, multiplying the original hashCode by 31 and adding each value of the char array.

replace(char oldChar, char newChar):
Replace all old characters oldChar in the string with new characters newChar

public String replace(char oldChar, char newChar) {
    
    
    if (oldChar != newChar) {
    
    
        int len = value.length;
        int i = -1;
        char[] val = value; /* avoid getfield opcode */
        while (++i < len) {
    
          //先找到字符串中第一次出现oldChar字符的位置i
            if (val[i] == oldChar) {
    
    
                break;
            }
        }
        if (i < len) {
    
    
            char buf[] = new char[len];
            for (int j = 0; j < i; j++) {
    
       //将之前的字符数组复制给新数组buf
                buf[j] = val[j];
            } 

/**然后从i后将字符数组中的内容复制给buf,如果字符为oldCha则替换为newChar.然后再通过buf创建新的字符串返回**/
            while (i < len) {
    
         
                char c = val[i];
                buf[i] = (c == oldChar) ? newChar : c;
                i++;
            }
            return new String(buf, true);
        }
    }
    return this;
}

Intern():

public native String intern();

This method is a local method and has no method body.
Using String's intern method can save memory. In some cases, you can use the intern() method, which enables different strings in memory to have only one instance object.
When it is not found in the constant pool, a copy is copied and placed in the constant pool. After 1.7, the address reference on the heap is copied to the constant pool.

Look at the following example:

public static void main(String[] args) {
    
    
    String str2 = new String("hello") + new String("world");
    str2.intern();         // 使用intern方法后str2与str1指向同一个对象,否则它们指向两个不同的对象。这样就能达到节省内存的效果。
    String str1 = "helloworld";
    System.out.println("比较结果:");
    System.out.println(str2 == str1);
}

Operation result:
Insert picture description here
Refer to the original class for the remaining methods.

Welcome to leave a message to discuss, thank you for your attention!

Guess you like

Origin blog.csdn.net/weixin_42409759/article/details/107136339