First understand the Java language - the difference and relationship between String, StringBuilder and StringBuffer!!! [suggested collection]

After we have learned about inheritance and polymorphism in Java, now we can learn a very important thing: String and StringBuilder and StringBuffer. We started directly!!!

Previous articles

Preface - how is IDEA configured? Make it easier for you to type code!

First knowledge of Java language (I) - basic data types and operators

First knowledge of Java language (II) - Methods and recursion

First knowledge of Java language (III) - array

First knowledge of Java language (IV) - classes and objects

First knowledge of Java language (5) - package and inheritance

First knowledge of Java language (6) - polymorphism, abstract classes and interfaces

1, String

Common ways to construct strings:

  1. Declare a variable of type String, and then initialize it directly

    String str = "hello world";
    
  2. Another is new, an object of type String

    String str = new String("hello world"); //Put the string in parentheses
    

The above two are the most common string construction methods. Of course, there are others. Let's take a look at the help manual!!!

3. String(byte[] bytes), this construction method converts a byte array into a string

byte[] bytes = {'a', 'b', 'c', 'd'};
String str = new String(bytes); //This results in the "abcd" string

4. String (byte [] bytes, charset, charset). This construction method will encode the byte array according to the encoding method of charset

byte[] bytes = {'a', 'b', 'c', 'd'};
String str = new String(bytes, "utf-8"); //The coding method is utf-8

5. String(byte[] bytes, int offset, int length). Start the conversion according to the offset and convert the data of length bytes

byte[] bytes =  {'a', 'b', 'c', 'd', 'e'};
String str = new String(bytes, 1, 3); //Start with a character with an offset of 1 and continue to convert 3 characters. That is "bcd"
//Remember that the offset and length entered should be within the range of bytes array, otherwise an exception will be reported

The above five construction methods are common and simple in ordinary times. Next, we will talk about how String strings are stored in memory and how strings are judged to be unequal.

  1. String str1 = "hello";
    String str2 = "hello";
    System.out.println(str1 == str2); //true or false?
    

    The answer is undoubtedly true. Then why are they equal? Let's look at the memory:

    There is also the concept of string constant pool in the heap, but there is no constant pool in the specific memory division. The constant pool is written in a hash table.

    What exactly is a string constant pool???

    As like as two peas, it is a simple area. This area is specially used to store constant strings. Whenever a new string is created, it will be queried in the string constant pool to see if there is the same string in the pool. If it already exists, then JVM will return the address of the string that has already been, and will not put the same string in the pool again. The purpose of this is to save space.

    As like as two peas in the above picture, when str1 is in the new string, "hello" is not in the pool, then put it in the string and assign the address to str1. Then str2 finds that there is a same string in the pool, then it returns the address of the string directly in the pool. Therefore, str1 and str2 strings point to the same memory space. So it's true.

  2. String str1 = "hello";
    String str2 = new String("hello");
    System.out.println(str1 == str2); //true or false?
    

    A string such as str1 is called a string literal constant. str2 is a new object. Since it is new, it must have opened up a memory space on the heap. See the following figure for details:

    As shown in the above figure, str2 will first create a new String class in the heap, and then there is a member variable of value in the object of this String class to store the address of "hello", and this String will eventually be stored in the constant pool. However, at this time, the constant pool has "hello", so the value variable points to the existing "hello" String.

    As can be seen from the figure, str1 directly points to the "hello" String, and str2 directly points to an object of String type. Directly judge the address, which must be false.

  3. String str1 = "hello";
    String str2 = "hel" + "lo";
    System.out.println(str1 == str2); //true or false
    

    Here, we need to know that at this time, the three strings in lines 1 and 2 are called string literal constants. In Java, constants will be calculated directly when compiling, that is, after compiling, the value of str2 is "hello", and then when allocating space at runtime, we will return to our first question above, That is, both str1 and str2 point to the same memory space. So the final answer is true.

  4. String str1 = "hello";
    String str2 = new String("hel") + "lo";
    System.out.println(str1 == str2); //true or false?
    

    At this time, this question is very similar to question 3 above. The answer must be false.

    At this time, after the JVM compilation is completed, the value of str2 still does not change, because there is a variable (new String()) to the right of the equal sign. At this time, the compiler does not know what is stored in this variable when compiling. It can only know what is stored when the code is executed to this step. As shown below:

    As shown in the figure, str1 is still a String pointing to the constant pool, while str2 is an object of another String class plus a "lo", so another memory space will be opened on the heap to store the result of the addition, that is, str2 points to an object of String class. So the answer is false.

  5. String str1 = "hello";
    String str2 = new String("hel") + new String("lo");
    System.out.println(str1 == str2); // true or false
    

    The answer is obviously false.

    Obviously, str1 points to the String of the constant pool and str2 points to the object of the String class on the heap. The memory addresses of the two are not equal.

  6. String s3 = new String("1") + new String("1");
    s3.intern(); //Manually, put the string into the string constant pool
    String s4 = "11";
    System.out.println(s3 == s4); //true or false
    

    In the first line of code, s3 must point to the object of the String class on the heap, that is, at this time, the value of s3 is the String "11" on the heap. Then execute the code in line 2 and manually put the "11" on the heap into the String constant pool. At this time, it is divided into two cases: 1. If the constant pool does not have the String "11", then the address of the "11" on the heap will be put into the String constant pool (after JDK1.7); 2. At this time, the constant pool already has the String "11", so "11" will not be put into the constant pool manually. To put it simply, it means doing nothing.

    When the third line of code is executed, there is a string "11" in the constant pool, so there is no need to put it in, just take the address of the existing string. As shown below:

    In JDK1.6, the intern method directly creates a new string in the string constant pool and stores it. After JDK1.7, there is no new string, but directly puts the address of the string on the heap into the constant pool.

    So the output of this question is true.

  7. String s3 = new String("1") + new String("1");
    String s4 = "11";
    s3.intern();
    System.out.println(s3 == s4); //true or false
    

    This problem is very similar to the above problem, but the order of intern methods is different. When the code in line 3 is executed, the String constant pool already has the "11" String pointed to by the s4 variable. At this time, the String pointed to by s3 is still on the heap and not in the constant pool. Now, call the intern method. The constant pool already has the "11" String, so you don't have to put it in. At this time, s3 is still an object pointing to the String class on the heap, and s4 is also a String pointing to the constant pool. Therefore, the memory addresses of the two are not equal, that is, false.

  8. String str1 = "hello";
    String str2 = str1;
    
    //The value of str2 is modified
    str2 = "world";
    System,out.println(str1);
    System.out.println(str2); 
    

    When we modify the value of str2, will the value of str1 change???

    The answer is definitely not. This is different from the pointer of C language. If the pointer is used, I can change the value in memory through the address. Reference in Java is impossible. Here, we just recreate a string "world", put it into the string constant pool, and then assign the address of this string to str2, so str1 has not changed. This is very important.

Common methods of String class

string comparison

  1. equals method.

    The comparison is whether the contents in the string are equal, which is also the largest comparison method used in peacetime.

    String str1 = "hello";
    String str2 = "hello";
    Ststem.out.println(str1.equals(str2));
    //Remember that the caller of the equals method cannot be null, or an exception will be reported. That is, str1 cannot be null
    
  2. equalsIgnoreCase method. This method is more advanced. It ignores case differences

    String str1 = "hello";
    String str2 = "HELLO";
    System.out.println(str1.equalsIgnoreCase(str2)); //true at this time
    
  3. compareTo method. This method compares the dictionary order, which is similar to the strcmp method of C language

    String str1 = "hello";
    String str2 = "helloo";
    System.out.println(str1.compareTo(str2)); //Number of returned less than 0
    //This method will compare each character of the two strings. During the comparison, if a character of the caller is less than that of the other party, it will return a negative number.
    //If the character of the caller is larger than that of the other party, a positive number is returned
    //If two strings are equal in length and each character is equal, 0 is returned
    

String substitution

  1. The replace method is used to replace some characters in the string

    String str1 = "hellohellohello";
    String str2 = str1.replace('h', 'H'); //Replace lowercase h with uppercase H
    //Remember, the str1 string itself will not be affected here, because the string in Java is immutable,
    //Only a new string will be created and changed here.
    
  2. The replaceFirst method replaces the string that appears for the first time

    String str1 = "hellohellohello";
    String str2 = str1.replaceFirst("ll", "LL"); //Replace the "ll" string for the first time
    

String lookup

  1. The contains method is used to determine whether a string contains another string

String str1 = "hello world";
System.out.println(str1.contains("world")); //Determine whether str1 has a world substring
  1. indexOf method is used to return the starting position of a substring in the main string, which is implemented by the well-known KMP algorithm

String  str1 = "hello KMP";
System.out.println(str1.indexOf("KMP")); //The return value is subscript 6
  1. The startsWith method determines whether the main string starts with this substring (prefix)

    String str1 = "hello world";
    System.out.println(str1.startsWith("hello")); //Determine whether it starts with hello
    
  2. endsWith method to judge whether this substring ends in the main string

    String str1 = "hello world";
    System.out.println(str1.endsWith("world")); //Judge whether the main string ends in world
    

String interception

The split method is used to split a string with a character and return an array of strings

This method, I use a lot when brushing questions, cooperate with buffering the input stream, read a line of data, and then divide it.

String str1 = "I love you";
String[] res = str1.split(" "); //Split with spaces

In addition, there is a split method that limits the number of arrays after splitting

String str1 = "I love you";
String[] res = str1.split(" ", 2); //Split into two arrays with spaces
//That is, in the array divided by the above code, there are only two parts of data: I and love you

Split method has another usage. For example, given a string, I want to split it with multiple characters. Assuming that the given string is I love*you, how to split the space and * sign together. As follows:

String str = "I love*you";
 //A space in front and a * sign in the back, separated by | in the middle, can realize the segmentation of multiple characters
String[] res = str.split(" |*");

Of course, the split method also needs to pay attention to one point when dividing the ip address, that is, the decimal separator of the ip address needs to be replaced by the escape character, as follows:

String str = "192.168.1.1";
String[] res = str.split("\\."); //Escape first with two slashes

Other methods

  1. isEmpty method, which is used to determine whether the string is empty. Remember that the empty string here means that there is nothing in the string, not null
  2. intern method to manually put the string into the constant pool
  3. trim method to remove the spaces at the beginning and end of the string
  4. The toUpperCase method converts lowercase characters of a string to uppercase
  5. The toLowerCase method converts uppercase characters of a string to lowercase

Wait... I won't list it here.

2, StringBuilder and StringBuffer

In the above, we have learned about the simple use of the String class. After reading the memory allocation above, we may feel that when splicing strings, the String class will create many objects, such as the following code:

String str = "hello";
for (int i = 0; i < 100; i++) {
    str = str + i;
}
System.out.println(str);

In the above code, str string has been splicing new strings to form new strings. According to the memory allocation diagram above, we can figure out how to waste memory. Each time we splice, we need to create a new object on the heap and splice it. This way is a waste of space.

Therefore, there are two string related classes, StringBuilder and StringBuffer.

Let's first look at the differences between String, StringBuilder and StringBuffer!

  • StringBuffer is very similar to StringBuilder. Both represent variable character sequences, and the methods are the same
  • String is an immutable string
  • StringBuffer is a variable string with low execution efficiency, but thread safety. It is suitable for multithreading
  • StringBuilder is a variable string with high execution efficiency, but thread is not safe. It is suitable for single thread

The inheritance relationship between the three is shown in the figure below:

Having said so much, one may ask, how should we use these two classes? Let's talk about it now.

Similarly, let's start with the construction method, whether there is a parameter structure or not. The most commonly used are the following two:

String str = "hello";
StringBuilder sb = new StringBuilder(); //Nonparametric construction method
sb.append(str); //With this method, you can add "hello" to the StringBuilder

StringBuilder sb2 = new StringBuilder(str); //You can also pass in a string directly in the constructor

The above two methods are the most commonly used. The same is true for StringBuffer.

String str = "world";
StringBuffer sb = new StringBuffer(); //Nonparametric structure
sb.append(str);

StringBuffer sb2 = new StringBuffer(str); //Parametric structure

Let's discuss an interview question

//How does the following code splice strings? Specific process?
String str1 = "hello ";
String str2 = str2 + "world";

Let's decompile to see what the code does.

Therefore, according to the above figure, it seems that StringBuilder is not used. In fact, in order to optimize the JVM, StringBuilder is added to it. Add strings through the append method of StringBuilder, and then convert them into strings.

So now, when we look back at the above code, do we think it's a waste of time and space?

String str = "hello";
for (int i = 0; i < 100; i++) {
    str = str + i;
}
System.out.println(str);

For each round of loop, the JVM needs new a StringBuilder class, which is the same for each loop. So writing code like this is very low. When we write code in the future, we need to avoid this writing method. We just need to manually create a new StringBuilder class outside the loop, and then call the append method inside the loop.

Now let's talk about the common methods of this StringBuilder class; The essence is that there are many methods in this class, as well as in the String class. We only need to know several other unknown methods:

  1. toString method to convert the object of StringBuilder class to string type

    StringBuilder sb = new StringBuilder("hello world");
    //String res = sb; //error, that won't work
    String res = sb.toString(); //You must call the toString method of this class
    
  2. Reverse method. I remember an interview question about how to reverse a string. At this point, we can convert the string to the StringBuilder class and then call the reverse method.

    String str = "hello world";
    StringBuilder sb = new StringBuilder(str);
    str = sb.reverse().toString(); //StringBuilder, which can be called in a chain
    //As above, just after calling the reverse method, you can directly call the toString method.
    //Because its return value is the object of StringBuilder itself
    
  3. append method. When adding strings, remember this method. You can add characters, numeric values, strings, etc.

    String str = "hello world";
    StringBuilder sb = new StringBuilder();
    sb.append(str).append("good morning");//It can also be called in a chain
    
  4. length method. Used to calculate the number of characters in the current string builder

    StringBuilder sb = new StringBuilder("hello world");
    System.out.println(sb.length()); //11
    
  5. Delete method. This method is used to delete the string in the current StringBuilder. This method has two parameters. The first is the offset of the start position and the second is the offset of the end position.

    StringBuilder sb = new StringBuilder("hello world");
    sb.delete(1, 4); //From the position with offset of 1 to position 4, remember that it is the left closed right open interval [1,4)
    System.out.println(sb.toString()); //Output: ho world
    
  6. insert method. insert a new parameter. There are two parameters. The first parameter is the offset and the second parameter is the inserted content.

    StringBuilder sb = new StringBuilder("I you");
    sb.insert(2, "love "); //At an offset of 2, start the insertion
    System.out.println(sb.toString());  //Output result: I love you
    

All the above codes are also applicable in StringBuffer. StringBuilder and StringBuffer, like brothers in the same school, learn every move and skill similar.

Is there no difference between them? There must be. Let's take a look at the underlying source code of the two:

We can see that every method in the source StringBuffer class is modified by synchronized. In short, it is like a lock to ensure thread safety. Therefore, StringBuffer is thread safe. Each method of StringBuilder does not have this key word, so it is thread unsafe.

Another problem is: string to StringBuilder, or StringBuilder to String. The former conversion can only be done by calling the construction method of StringBuilder, or first new a StringBuilder object, and then calling append method to add.

Well, all the above are all the contents of this issue. This update is over!!! See you in the next issue!!!

Tags: Java Back-end JavaSE

Posted on Mon, 20 Sep 2021 10:57:42 -0400 by kentopolis