Read the Java String of the source code carefully (I)

Close reading of the source code is the column of appreciating the source code under the horse soldier education. The purpose of our appreciation of the source code is not to show off our skills, but to understand the author's design ideas and extract the essence, so as to write more excellent code. On the other hand, you can also give extra points to the interview. The evaluation of good or bad code will inevitably be replaced by personal subjective color, and everyone is harmonious but different.

The String class in Java is definitely the most widely used class. This article will take you to read the String source code under JDK8, which mainly includes the following aspects:

I String immutable property

II String core field analysis

III String common method analysis

IV String in jdk1 Differences between version 6 and 1.8

I Immutable property of String

1. What is an immutable object?

 

If an object cannot change its state after it is created, the object is immutable.

 

2. Benefits of designing string to be immutable

 

A. Only when the string is immutable, can the string constant pool be realized, and can all strings with the same literal value point to the same address space in the constant pool, so as to save heap memory;

B. The string is immutable and must be thread safe, so that the same string instance can be safely shared by multiple threads without any synchronous operation;

C. Only when the String is immutable can the hash value be cached to avoid repeated calculation, so that String, as the Key of HashMap, has good performance.

 

3. How to realize immutability of string class

 

1. The definition of String class uses final modification and cannot be inherited, so all methods of String cannot be overridden;

2. The field storing string data is declared as private final char value [], which is decorated with private final. The value of value itself cannot be modified or accessed externally after initialization;

3. The constructor of String ensures that the internal value field of String cannot be accessed externally by copying;

4. Any method that needs to change the String content creates a new String object, while the original object remains unchanged, such as substring,trim,toUpperCase, etc;

5. The methods to obtain value return a copy of value, such as toCharArray.

 

4. Non variability destruction of string class

Although the value field is modified by private, it can still be obtained and modified by reflection. The code is as follows:

 

private static 
void main() throws IllegalAccessException, NoSuchFieldException {
    String a = "1";
    Field valueField = String.class.getDeclaredField("value");
    System.out.println(a);
    valueField.setAccessible(true);
    char[] value = (char[]) valueField.get(a);
    value[0] = '0';
    System.out.println(a);
}

Through the above code, the value of variable a is changed from 1 to 0.

 

II Core field of String

 

1.value

Value is defined as private final char value [], which stores every character in the string and ensures that it will not be accessed externally.

 

2.hash

Hash is defined as private int hash, which stores the value of hashCode of string. There are two problems associated with hash:

A. Why store hash values?

Because the hashCode method of string is very time-consuming and the time complexity is O(N), it needs to be calculated every time, which seriously affects the performance, so it is necessary to cache the hash value;

 

2. Why is the hash field not decorated with final?

If the final modification is used, the hash value must be assigned before the object initialization is completed, and the calculation of the hash value is very time-consuming. For those strings that do not need to use hash, it will cause a waste of CPU. Therefore, the strategy of delaying calculation is adopted here. The default value is 0. It can be calculated when it needs to be used. To achieve this purpose, the hash field cannot be modified by final.

 

III String common method analysis

 

1.String addition

 

Operator overloading is not allowed in Java, so String addition is realized through syntax sugar (compilation means), which can be divided into the following two cases:

 

A. When the addition operation is performed, the result value is determined at the compilation time, and the added result is directly used for replacement, which eliminates the addition operation. For example:

Example 1:

  •  
public static void main(String[] args) {
    String value = "a" + "b";
    System.out.println(value);
}
 

Because the strings "a" and "b" are literal constants, the value of value has been determined at the time of compilation, and the value is "ab", so "ab" is directly assigned to value. We decompile the class file generated by the above code and get the equivalent code as follows:

public static void main(String[] args) {
    String value = "ab";
    System.out.println(value);
}

  

Example 2:

public static void main(String[] args) {
    final String a = "a";
    final String b = "b";
    String value = a + b;
    System.out.println(value);
}

Because variables A and b are decorated with final, the value is a literal constant, which is determined during compilation. All values of a + b determined during compilation are "ab", so "ab" is directly assigned to value. We decompile the class file generated by the above code, and the equivalent code is as follows:

public static void main(String[] args) {
    String a = "a";
    String b = "b";
    String value = "ab";
    System.out.println(value);
}

B. When performing addition operation, if the result value cannot be determined during compilation, use StringBuilder for splicing, for example:

 

Example 3:

public static void main(String[] args) {
    String a = "a";
    String b = "b";
    String c = "c";
    String value = a + b + c;
    System.out.println(value);
}

Because there is no final modification for variables a, B and C, the value may be modified during operation, so the value of value is uncertain during compilation. During compilation, it is converted to use StringBuilder for splicing. After decompilation, the equivalent code is as follows:

public static void main(String[] args) {
    String a = "a";
    String b = "b";
    String c = "c";
    String value = new StringBuilder().append(a).append(b).append(c).toString();
    System.out.println(value);
}

Example 4:

public static void main(String[] args) {
    final String a = String.valueOf(System.currentTimeMillis());
    final String b = String.valueOf(System.currentTimeMillis());
    String value = a + b;
    System.out.println(value);
}

Although variables A and b are decorated with final, the values of a and b are uncertain during compilation, so the value is also uncertain. Finally, StringBuilder can only be used for splicing. The equivalent code after decompilation is as follows:

public static void main6(String[] args) {
    final String a = String.valueOf(System.currentTimeMillis());
    final String b = String.valueOf(System.currentTimeMillis());
    String value = new StringBuilder().append(a).append(b).toString();
    System.out.println(value);
}

To explain, whether String addition is directly replaced with the result value or spliced with StringBuilder during compilation has no direct relationship with whether the added variables are modified with final. The only judgment basis is whether the value of the addition result is determined during compilation, which is replaced if it is determined, and spliced if it is not determined.

 

Example 5:

public static void main(String[] args) {
    String a = "";
    for (int i = 0; i < 26; i++) {
        a += (char) ('A' + i);
    }
    System.out.println(a);
}

This example is used to illustrate the inefficient use of String addition operation. This process will frequently create String and StringBuilder objects, so we must avoid using the plus sign to splice strings in the loop. The equivalent code after decompilation of the above code is as follows:

public static void main(String[] args) {
    String a = "";
    for (int i = 0; i < 26; i++) {
        a = new StringBuilder().append(a).append((char) ('A' + i)).toString();
    }
    System.out.println(a);
}

The writing method with higher effect abandons addition and directly uses StringBuilder to splice strings. Only one StringBuilder object is created and builder is called only once Tostring() method, the code is as follows:

public static void main(String[] args) {
    StringBuilder builder = new StringBuilder();
    for (int i = 0; i < 26; i++) {
        builder.append((char) ('A' + i));
    }
    String value = builder.toString();
    System.out.println(value);
}

Learn about advanced java courses, scan code and add wechat!!

 

Keywords: Java Programming string source code

Added by mashamit on Mon, 31 Jan 2022 18:30:11 +0200