Interview Questions-Immutable String Class

I recently read an article by my second brother, and I have deep feelings about how to do one thing well. After reading it, I also summarized the following sentences: keep learning, keep thinking, keep practicing. The basic knowledge is solid, and the next thing to do is to go deeper. People go high!

I believe that everyone is familiar with the String class. It may be used every day (as long as you write the code). Today, I also read some bloggers’ explanations to compare the understandings and gaps between myself and them, so I sorted it out. Just to prepare for the interview!

To the point

01 Warm up with a question

 		String str = new String("abc");
        String str2 = "abc";

Essential knowledge points:

Talk about two ways to create string objects

  • Constructor creates objects
  • Create object with double quotes

What is the difference between the constructor and the double-quoted object creation?
The constructor creates an object: new String("abc") will create two objects, one in the heap memory and one in the constant pool. The heap memory object is a copy of the constant pool object.

Note:
If the "abc" object exists in the constant pool, only one object is created (in the heap memory).
If the "abc" object does not exist in the constant pool, two objects are created

Double quotes create an object: constant pool (because new String ("abc" created two objects, there is already "abc" in the constant pool, no more objects)) so the memory address of "abc" is assigned to str2, str2 is based on the memory Address, pointing to "abc" in the constant pool)

                         ------------- 实战演练-------------

Sledgehammer test

Determine whether str1 and str2 are equal

public class StringTest {
    
    
    public static void main(String[] args) {
    
    
 		String str1 = "abc";
        String str2 = "abc";
        System.out.println(str1==str2);//true
        System.out.println(str1.equals(str2));//true
    }
}        

By the way, review the difference between == and equals method?
For == is
, if the comparison is the basic data variables, direct comparison of "value."
If the comparison is a reference type variable, the comparison is a memory address.
For equals,
if the equals method is overridden, the comparison is the content of the reference variable pointing to the object.
If the equal method is not overridden, the comparison is the memory address of the reference variable pointing to the object.
Such as the String class and Date have rewritten the equals method.

Analysis: The
first print statement string belongs to the reference type and str1 and str2 point to the same memory address, so the == comparison result is equal to true. The
second print statement has an equals method, so we first consider whether the String class overrides this method. It is known from the JDK1.8 document that
this string is compared with the specified object. The result is true if and only if the parameter is not null and is a String object, which means that the same character sequence is used as the object.
In other words, str1 and str2 are the same character sequence object, so the result of equals comparison is true

Diagram:
Insert picture description here

  1. Compile the current class before running and compile it into StringTest.class (bytecode file), and load the file into the method area of ​​the memory
  2. The main method is pushed into the stack memory
  3. The constant pool creates an "abc" object, generates a memory address, and assigns it to str1 under the main method, and str1 points to the memory address according to the memory address
  4. The program runs to str2 and finds that the same object already exists in the constant pool, so it is not being created (for the purpose of saving memory overhead), str2 also points to the memory address
  5. str1 and str2 all point to the same address, so the == result is true
  6. Since the string class rewrites the equals method, the contents of str1 and str2 are equal (the same character sequence), so the equals result is true

First show the edge,
judge whether str1 and str2 are equal

public class StringTest {
    
    
    public static void main(String[] args) {
    
    
        String str = new String("abc");
        String str2 = "abc";
        System.out.println(str==str2);//false
        System.out.println(str1.equals(str2));//true
    }
}

Analysis: The
first print statement str2 refers to the constant pool memory address, and the str refers to the heap memory address, so the == result is false. The
second print statement string class rewrites the equal method. The content of the method str1 and str2 is the same (the same Character sequence), so the equals result is true

Illustration: The
Insert picture description here
memory process is roughly as follows:

1) Compile before running to generate a StringTest.class file (bytecode file) and load it into the method area of ​​the memory.
2) The main method is pushed into the stack memory.
3) New String ("abc") creates an object in the constant pool and generates a memory address. The str assigned to the main method, str points to the heap memory according to the memory address, and copies the heap memory at the same time.
4) When creating an object with double quotes, it is found that the constant pool already exists and will not be created again. str2 points to the memory address of the constant pool. .
5) str points to the heap memory str2 points to the address of the constant pool so == the result is false
6) str and str2 have the same content (same character sequence) so the equals result is true

Why does the heap memory make a copy?
JDK API 1.6 explains the construction method like this-String (String original)
initializes the newly created String object so that it represents the same character sequence as the parameter; in other words, the newly created string is a copy of the parameter string.

The soldiers are not bloody to
judge whether str1 and str2 are equal

public class StringTest {
    
    
    public static void main(String[] args) {
    
    
        String str1 = "ab";
        String str2 = "abc";
        String str3 = st1 + "c";
        System.out.println(str2 == str3);//false
        System.out.println(str2.equals(str3));//true
    }
}

Analysis: The
first print statement == the result is false. If the above knowledge cannot be solved, it must be understood with the help of API and decompiler tools. The
second print statement equals results are suddenly not difficult to understand because they are all the same character sequence and the result is true

The reason for the doubt is that the str3 object is created through the connection string (+). For the (connection string) +JDK1.8 help document, the Java language also
provides special support for the string concatenation operator (+), and converts other objects to String. String connection is achieved through the StringBuilder (or StringBuffer) class and its append method. String conversion is realized by the method toString, defined by the following formula Object and inherited by all classes in Java.

If you want to see +how it is converted to StringBuiler operation, you can use the decompiler jad to achieve (the article will be shared at the end)

public class StringTest 
{
    
    

    public StringTest ()
    {
    
    
    }

    public static void main(String args[])
    {
    
    
        String s = "ab";
        String s1 = "abc";
        String s2 = (new StringBuilder()).append(s).append("c").toString();
        System.out.println(s1 == s2);
        System.out.println(s1.equals(s2));
    }
}

Through the decompilation tool, it can be found that the actual operation of String str3 = st1 + "c" is
String s2 = (new StringBuilder()).append(s).append("c").toString()

This is much clearer.
Because the objects created by new StringBuilder() through the append method are stored in the heap memory, a memory address of 0x0011 will be generated, but this is not a String object, so it needs to be converted into a String object through the toString method in the object class. The String object is also A memory address 0x002 is generated in the heap memory and then assigned to s2 and s2 to point to the heap memory address 0x0022

Diagram: The
Insert picture description here
general memory process is as follows:

  1. The constant pool creates an "ab" object, generates a memory address and assigns it to str1, and str1 points to "ab";
  2. The constant pool created "abc" for this, and generated a memory address assigned to str2, str2 pointed to "abc";
  3. The connection string will be created by java through the append method of the StringBuilder method to create a new object to get "abc". At
    this time, the memory address 0x0011 represents a StringBuilder object, not a String object.
  4. Need to be converted to String object through the toString method of object
  5. String object assigns memory address 0x0022 to str3, str3 points to "abc"

The front cannot be used to
determine whether str1 and str2 are equal

public class StringTest {
    
    
    public static void main(String[] args) {
    
    
        String str1 = "a" + "b" + "c";
        String str2 = "abc";
        System.out.println(str1 == str2);//true
        System.out.println(str1.equals(str2));//true
    }
}        

Analysis: The
first print statement is obtained through the decompilation tool. The original str1 = "a" + "b" + "c"; after decompilation, String s = "abc"; the
conclusion can be drawn:

1. If the left and right sides of the connection string are strings, create them directly in the constant pool
. 2. If there are reference variables on either side of the connection string, the object (new object) will be created through the append method of StringBuilder or (StringBuffer) Are stored in the heap memory)
so str1 and str2 point to the same address == the result is true

The second print statement str1 and str2 are the same character sequence, so the content is the same and the result is true

public class StringTest
{
    
    

    public StringTest()
    {
    
    
    }

    public static void main(String args[])
    {
    
    
        String s = "abc";
        String s1 = "abc";
        System.out.println(s == s1);
        System.out.println(s.equals(s1));
    }
}

Did the following questions

		String s1 = new String("zs");
        String s2 = new String("zs");
        System.out.println(s1==s2);//false
        String s3="zs";
        String s4="zs";
        System.out.println(s3==s4);//true
        System.out.println(s3==s1);//false
        String s5="zszs";
        String s6=s3+s4;
        System.out.println(s5==s6);//false
        final String s7="zs";
        final String s8="zs";
        String s9=s7+s8;
        System.out.println(s5==s9);//true
        final String s10=s3+s4;
        System.out.println(s5==s10);//false

I won’t go into too much detail in the previous section, but I will focus on the final modified member variables (the value can only be initialized in the construction method, and then cannot be modified).
String s9=s7+s8;This line of code should create an object in the heap memory according to the above understanding, but The final modified member variable is detained in the constant pool, so the object returned by the connection string is stored in the constant pool, and because "zszs" has been created, the s5 and s9 references point to the same memory address == the result is true
final String s10=s3+s4;This line of code looks the same as above. Although the final is modified, it is actually found that the connection string is still valid through decompilation. This is because the final is detained after the object is created, so the heap memory has already created the object at this time.
So s5 points to the constant pool address, and s10 points to the heap memory address == the result is false.
If you don’t understand, you can use jad decompilation or drawing to understand

public class Test
{
    
    

    public Test()
    {
    
    
    }

    public static void main(String args[])
    {
    
    
        String s = new String("zs");
        String s1 = new String("zs");
        System.out.println(s == s1);
        String s2 = "zs";
        String s3 = "zs";
        System.out.println(s2 == s3);
        System.out.println(s2 == s);
        String s4 = "zszs";
        String s5 = (new StringBuilder()).append(s2).append(s3).toString();
        System.out.println(s4 == s5);
        String s6 = "zszs";
        System.out.println(s4 == s6);
        String s7 = (new StringBuilder()).append(s2).append(s3).toString();
        System.out.println(s4 == s7);
    }
}

Some methods in the 02String class

JAVA provides the String class to create and manipulate strings. As you can see in the source code, the internal implementation of the String class is also a byte array, which is of final type. Therefore, String is an immutable object. Every time a change is made to the String class, a new String object will be generated, and then the pointer will point to the new String object.

 /** The value is used for character storage. */
    private final char value[];

The methods in the String class also confirmed that manipulating strings does not change itself, but creates new objects, such as concat, replaceAll, and replace.

for example:

        String s = "abcd";
        s = s.concat("ef");
        System.out.println(s);

The operation on the string "abcd" connects the specified string to the end of the string. What
is returned in the source code is a new object, so the created string object "abcdef" is stored in the heap memory.

public String concat(String str) {
    
    
        int otherLen = str.length();
        if (otherLen == 0) {
    
    
            return this;
        }
        int len = value.length;
        char buf[] = Arrays.copyOf(value, len + otherLen);
        str.getChars(buf, len);
        return new String(buf, true);
    }

03 Why is String an immutable class?

I have done a lot of foreshadowing in the front, and finally summarize the reasons why String is an immutable class.

  1. The need of the constant pool The
    string constant pool is a special storage area in the Java heap memory. When creating a string object, if the constant pool exists, it will not be created again. The object is directly referenced. The purpose is to save memory overhead and improve operating efficiency.
  2. Hashcode needs to be
    because the string is immutable, so when it is created, its hashCode is cached, so it is very suitable as a hash value (for example, as the key of HashMap), multiple calls only return the same value, to Improve efficiency.
  3. Thread safety
    Since immutable objects cannot be changed, they can be freely shared among multiple threads. This eliminates the need for synchronization

04Share decompilation tools

Decompilation tool jad
link: https://pan.baidu.com/s/1F8oUkDTTBqkl1UkomESgyA
Extraction code: 7ns3

The specific installation operation is as follows:
http://www.itwanger.com/java/2019/10/22/javac-jad.html

Guess you like

Origin blog.csdn.net/lirui1212/article/details/109631247