The introduction
This article explains several properties of strings.
String immutability
For starters, it’s easy to get the idea that strings can change, especially when the + link appears to change. However, strings cannot be modified once created. Next, let’s step through how strings maintain their immutable nature;
1. Approach one: Final classes and private members of Final
Let’s take a look at some of the String source code:
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; // use serialVersionUID from JDK 1.0.2for interoperability */
private static final long serialVersionUID = -6849794470754667710L;
}
Copy the code
We can see that String is a final class, and all three members are private, which means that String cannot be inherited. This prevents programmers from making the String class “mutable” by inheriting methods that override the String class.
From the source code, each String maintains a char array — the private member value. Array value is an underlying array of strings, used to store the contents of strings, and is private final. But arrays are references, so references can only be restricted from changing. That is, the values of array elements can be changed, and strings have a constructor that can be passed into arrays. Can we “modify” the contents of a String by modifying the elements of the external char array?
Let’s do an experiment as follows:
public static void main(String[] args) {
char[] arr = new char[]{'a'.'b'.'c'.'d'};
String str = new String(arr);
arr[3]='e';
System.out.println("str= "+str);
System.out.println("arr[]= "+Arrays.toString(arr));
}
Copy the code
The results
str= abcd arr[]= [a, b, c, e]
It didn’t turn out the way we expected. The string STR uses the array ARr to construct an object. When the array ARR modifies its element value, the string STR does not change. Let’s look at how this constructor works:
public String(char value[]) {
this.value = Arrays.copyOf(value, value.length);
}
Copy the code
When a String uses an external char array to construct an object, it copies the external char array so that changes to the external char array do not affect the String object.
2. Method 2: Change the method of creating objects
From the above analysis, we know that we cannot change a String externally, so it is not possible to use the methods provided by String. There are many methods that seem to change a String, such as replace(), replaceAll(), subString (), and so on. Take substring() as an example and look at the source code:
public String substring(int beginIndex, int endIndex) {
//........
return ((beginIndex == 0) && (endIndex == value.length)) ? this
: new String(value, beginIndex, subLen);
}
Copy the code
As you can see from the source, if the string is not cut, a new object will be created. That is, whenever it is not equal to the original String, a new String is created.
extension
The wrapper classes for the primitive type are very similar to String in that they are final, immutable objects, and maintain a private final member that stores content. Such as Integer:
public final class Integer extends Number implements Comparable<Integer> {
private final int value;
}
Copy the code
String + operation and String constant pool
Let’s start with an example:
public class MyTest {
public static void main(String[] args) {
String s = "Love You";
String s2 = "Love"+" You";
String s3 = s2 + "";
String s4 = new String("Love You");
System.out.println("s == s2 "+(s==s2));
System.out.println("s == s3 "+(s==s3));
System.out.println("s == s4 "+(s==s4)); }}Copy the code
Running results:
S == s2 True S == S3 false s == S4 false
Are you confused about the results? Don’t worry. Let’s figure this out. First, the compiler has the advantage of optimizing code as much as possible at compile time, so calculations that can be done by the compiler, such as constant expressions, do not wait until run time. Therefore, s2 is computed at compile time and is the same value as S, so they are equal, i.e. both are literal constants created and maintained in the string constant pool at class load time. S3, however, contains s2, which can only be evaluated at run time. In other words, the result is computed at run time and objects are created in the heap, which is not equal to S. S4 uses new to create objects directly in the heap, which is even less likely to be equal.
So how do you do that at runtime, because strings are immutable objects? To see how this works, use jad mytest. class to decompress the calss file in the example above and return it to the Java code:
public class MyTest
{
public MyTest()
{
}
public static void main(String args[])
{
String s = "Love You";
String s2 = "Love You"; String s3 = (new StringBuilder(string.valueof (s2))).toString(); String s4 = new String("Love You");
System.out.println((new StringBuilder("s == s2 ")).append(s == s2).toString());
System.out.println((new StringBuilder("s == s3 ")).append(s == s3).toString());
System.out.println((new StringBuilder("s == s4 ")).append(s == s4).toString()); }}Copy the code
As you can see, the compiler handles the + sign into the stringBuilder.append () method. That is, at run time, the link string is evaluated by creating a StringBuilder object, calling the append() method, and creating a StringBuilder object for each link string expression. Therefore, when repeating string links in a loop, you should consider using StringBuilder instead of + links to avoid the performance overhead of repeatedly creating StringBuilders.
String constant pool
You can refer to my last article on constant pools. I won’t go into depth here, but I’ll just cover the parts related to strings.
The contents of the string constant pool are largely derived from compiled string literal constants. It also increases during runtime,
String intern () :
Returns the normalized representation of a string object. An initially empty String pool maintained privately by the class String. When the intern method is called, if the pool already contains a String equal to this String Object (determined by equals(Object)), the String from the pool is returned. Otherwise, the String is added to the pool and a reference to the String is returned. It follows the following rule: for any two strings s and t, s.intern() == T.intern () is true if and only if s.quals (t) is true.
Another point worth noting is that although the return value of string.intern () is always equal to the String constant. But this does not mean that the same string intern() will return the same at all times in the system (although more than 95% of the time, it will). Because there is a possibility that after an intern() call, the string is reclaimed at some point, and then after an intern() call, the literal string is readded to the constant pool, but the reference location is different.
String hashCode (
String is also subject to equals(s.quals (s1) is true, and s.hashcode ()==s1.hashCode() is true. Instead of focusing on the eqauls method, we’ll look at the hashCode() method. String.hashcode () is interesting and might be asked in an interview. Let’s look at the code:
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;
}
Copy the code
## Why choose 31 as the multiplier?
From the information on the Internet, there are generally two reasons:
-
31 is a moderate prime number, one of the preferred primes to be used as a hashCode multiplier. Other close primes, such as 37, 41, 43, and so on, are also good choices. So why did you pick 31? Look at the second reason.
-
31 can be optimized by the JVM, 31 * I = (I << 5) -i.
Reference: http://www.cnblogs.com/jinggod/p/8425182.html
If there are any improper articles, please correct them. You can also pay attention to my wechat public number: Learn Java well and get high quality resources.