The old pen test

Some old pen tests ask you to determine whether s1==s2 is false or true, and s1.equals(s2) is false or true.

String s1 = new String("xyz");
String s2 = "xyz";
System.out.println(s1 == s2);
System.out.println(s1.equals(s2));
Copy the code

You can always come up with a standard answer pretty quickly: == compares object addresses, and equals compares real character arrays. So the output is false and true.

The questions above belong to the lowest level and have no difficulty.

Now the old question has gradually disappeared, and some new variations have taken its place:

String s1 = "aa";
String s2 = "bb";
String str1 = s1 + s2;
String str2 = "aabb";
// Output what??
System.out.println(str1 == str2);

final String s3 = "cc";
final String s4 = "dd";
String str3 = s3 + s4;
String str4 = "ccdd";
// What is output?
System.out.println(str3 == str4);
Copy the code

It’s a little harder, but if you think about it, it’s not hard to get false and true.

Today’s article is based on these topics.

String creation

Here’s a quick look at the structure of the String class:

As you can see, the String contains a value property, which is the actual char array that stores the characters.

String s = “xyz”; Create a String object in the heap, a char array object.

How do we prove that we created a String and a char array? We can verify this through the Debug function of IDEA:

Note the position in my screenshot, after executing String s = “xyz”; After clicking Load Classes again, Diff String and char[] are added by 1, indicating that a char array object and a String object have been added to memory.

Now let’s look at String s = new String(“xyz”); Several objects are created.

String s = new String(“xyz”); String s = new String(“xyz”); After that, we create two Strings and a char array object.

String s = new String(“xyz”); The s reference to can only point to one object, and can draw a memory map:

As you can see, in the heap, there are two Strings whose values point to the same char array object.

The String below is not referenced at all, meaning it is not used, so what does it do?

Is it a design flaw in the JDK that takes up memory and doesn’t use it?

It’s obviously not a bug in the JDK. The JDK does have a design bug, but it’s not as obvious or stupid as this.

So what’s that String down there for?

The answer is to host in the string constant pool. Note that I’m using a host here, rather than putting objects directly into the string constant pool. We’ll talk about the difference later.

Here comes the concept of a String constant pool, I created several instances in String s = new String(“xyz”) can you really get that right? There has also been a more detailed introduction, interested can go to have a look, here will not repeat.

All you need to know is that the equivalent class for the string constant pool in the JVM source code is StringTable, and the underlying implementation is a Hashtable.

We take String s = new String(“xyz”); For example:

See if you can find a reference to the string “xyz” in the string constant pool. If you can’t find it in the string constant pool:

  • Create a String and a char array
  • Encapsulate the created String as HashtableEntry and store it as the value of a StringTable
  • New String(” XYZ “) creates another String in the heap, and the char array points directly to the char array that was created

If the string constant pool can be found:

  • The new String(” XYZ “) creates an object in the heap that points directly to an existing char array object

And String s = “xyz”; What is the logic of:

See if you can find a reference to the string “xyz” in the string constant pool.

  • Create a String and a char array
  • Encapsulate the created String as HashtableEntry and store it as the value of a StringTable
  • Returns the created String

If the string constant pool can be found:

  • Return to find the String corresponding to the reference

To sum up:

For String s = new String(“xyz”); This form creates a String object, if it can be found in the String constant pool, creates a String object; If not found in the String constant pool, create two strings.

For String s = “xyz”; This creates a String object, not a String object if it can be found in the String constant pool; If not found in the String constant pool, create a String object.

So, in everyday development, you can use String s = “xyz”; String s = new String(“xyz”); Because you can create one less object and save some space.

It’s important to note that the String constant pool is not a String, it’s not a String, it’s a HashtableEntry, and the value inside a HashtableEntry is a String, so just to keep things simple, I omitted HashtableEntry, but that doesn’t mean it doesn’t exist.

The resident mentioned above is the process of creating a HashtableEntry to point to a String and storing a HashtableEntry into a String constant pool.

In some articles on the Internet, some authors may be to let readers better understand, omit some of these, we must pay attention to distinguish.

With that in mind, let’s go back to the old pen-and-paper test.

String s1 = new String("xyz");
String s2 = "xyz";
// Why is the output false?
System.out.println(s1 == s2);
// Why is the output true?
System.out.println(s1.equals(s2));
Copy the code

With that in mind, let’s draw the corresponding memory diagram and see why s1 == s2 is false.

Because the equals method compares real char data, and s1 and s2 both end up referring to the same char array object, s1.equals(s2) equals true.

The argument that they all end up pointing to the same char array object can also be proved by reflection:

I changed the value of the String pointed to by str1. The object pointed to by str2 is also affected.

String splicing

Now, let’s look at the variation:

String s1 = "aa";
String s2 = "bb";
String str1 = s1 + s2;
String str2 = "aabb";
// Why is the output false
System.out.println(str1 == str2);
Copy the code

For this problem, we need to first look at the bytecode of this code.

It doesn’t matter if you can’t read the bytecode instructions, just look at the part I’ve circled in red, and you can see the StringBuilder.

What does that mean? It means String str1 = s1 + s2; New StringBuilder().appEnd (“aa”).appEnd (“bb”).toString();

The append method in StringBuilder operates on a char array. What does the toString method in StringBuilder do?

ToString (char value[], int offset, int count); toString (char value[], int offset, int count);

  • Copy a char array object based on the arguments. A copy!
  • Create a String whose value points to the copied char array.

Note that it does not reside in the string constant pool, which is critical!! To understand this, draw a picture:

That is, str2 does not reside in the String constant pool, whereas str1 does reside in the String constant pool, and they are not the same object. So str1 = str2 is still false

Since we copy a char array object, if we change one char array, the other char array does not matter:

One of the strings is ugly, but the other is handsome, indicating that the two strings are not using the same char array.

Intern method

Strings created by calling toString on StringBuilder do not reside in the String constant pool. What if I want to reside in the String constant pool instead? Is there any way?

Yes, the String intern method will do that for you.

Take this code as an example:

String s1 = "aa";
String s2 = "bb";
String str = s1 + s2;
str.intern();
Copy the code

In performing STR. Intern (); Previously, the memory map looked like this:

In performing STR. Intern (); After that, the memory map looks like this:

The intern method creates a HashtableEntry object, points its value to a String, and hash the HashtableEntry into the corresponding String as a constant pool. That is, of course, if there is no HashtableEntry in the string constant pool.

No, intern method. It’s that simple. I’ll give it to you in one sentence.

There is also an interesting story about intern methods. If you are interested, you can read why god’s article “Understanding the Java Virtual Machine in Depth”. The hole dug in version 2 was finally filled in by R in version 3

Compiler optimization

At this point, there seems to be only one hole left unfilled. It is this item entitled What output is true.

final String s3 = "cc";
final String s4 = "dd";
String str3 = s3 + s4;
String str4 = "ccdd";
// Why is the output true?
System.out.println(str3 == str4);
Copy the code

In addition, two final keywords have been added. Let’s first look at the bytecode of this code:

Another bytecode instruction that doesn’t need to be read, you can click #4 and see the “CCDD” string.

String str3 = s3 + S4; String str3 = s3 + s4; Optimized to String str3 = “CCDD”.

So the original question is equivalent to:

String str3 = "ccdd";
String str4 = "ccdd";
// Why is the output true?
System.out.println(str3 == str4);
Copy the code

Is such a problem still difficult? Isn’t that so no matter how str3 and str4 are compared, they’re going to be the same.

conclusion

String is the “most familiar stranger” to a Java programmer. You say String is simple, and it is. You say it’s hard, and it’s hard to dig into, but these problems are easy if you have a memory map in your head.

The interview questions are only going to get harder, and the industry seems to be getting harder and harder, but as long as I learn fast, the exam won’t catch me.

Okay, that’s it for today. I’m going to play video games.

I hope this article will be of some help to you.

Write in the last

I am responsible for every article that goes out, and I try to find and verify the theory of knowledge in official documents and authoritative books. But even then, I can’t guarantee that every point in the article is correct. If you find something wrong, please point it out and I’ll fix it.

Creation is not easy, in order to better expression, need to draw a lot of pictures, these are MY own hands with PPT drawing, drawing is also very hard!

So, don’t hesitate to give positive feedback. Your attention is welcome and appreciated.

I’m CoderW, a programmer.

Thanks for reading, and see you next time!