Interview question: String a = "ab"; String b = "a" + "b"; Is a == b equal
Interview site
Purpose of the investigation: To investigate the understanding of the basic knowledge of JVM, involving constant pool, JVM runtime data area, etc.
Scope of investigation: Working for 2 to 5 years.
background knowledge
To answer this question, you need to understand the two most basic questions
String a=“ab”
, what happened in the JVM?String b=“a”+“b”
, how is the bottom layer realized?
JVM runtime data
First, let's review the runtime data area of the JVM together.
In order for everyone to have a global perspective, I draw the overall structure from class loading to the JVM runtime data area, as shown in the figure below.
The role of each area is explained in detail in my previous interview series, so I won’t repeat it here.
In the above figure, we need to focus on several categories:
- String constant pool
- Encapsulation class constant pool
- Runtime constant pool
- JIT compiler
These contents are very closely related to this interview question. For the content of the constant pool part, let me first leave a question, and first follow me to learn about the constant pool in the JVM.
What are the constant pools in the JVM
You often hear various constant pools, but you don't know where these constant pools are stored, so there are many questions: Which constant pools are there in the JVM?
The constant pool in JVM can be divided into the following categories:
- Class file constant pool
- Global string constant pool
- Runtime constant pool
Class file constant pool
There is a constant pool in the bytecode of each Class
file, which mainly stores various literals and symbol references generated by the compiler. For a more intuitive understanding, we write the following program.
public class StringExample {
private int value = 1;
public final static int fs=101;
public static void main(String[] args) {
String a="ab";
String b="a"+"b";
String c=a+b;
}
}
After the above program is compiled, javap -v StringExample.class
, and the intercepted part of the content is as follows.
Constant pool:
#1 = Methodref #9.#32 // java/lang/Object."<init>":()V
#2 = Fieldref #8.#33 // org/example/cl07/StringExample.value:I
#3 = String #34 // ab
#4 = Class #35 // java/lang/StringBuilder
#5 = Methodref #4.#32 // java/lang/StringBuilder."<init>":()V
#6 = Methodref #4.#36 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StrvalueingBuilder;
#7 = Methodref #4.#37 // java/lang/StringBuilder.toString:()Ljava/lang/String;
#8 = Class #38 // org/example/cl07/StringExample
#9 = Class #39 // java/lang/Object
#10 = Utf8 value
#11 = Utf8 I
#12 = Utf8 fs
#13 = Utf8 ConstantValue
#14 = Integer 101
#15 = Utf8 <init>
#16 = Utf8 ()V
#17 = Utf8 Code
#18 = Utf8 LineNumberTable
#19 = Utf8 LocalVariableTable
#20 = Utf8 this
#21 = Utf8 Lorg/example/cl07/StringExample;
#22 = Utf8 main
#23 = Utf8 ([Ljava/lang/String;)V
#24 = Utf8 args
#25 = Utf8 [Ljava/lang/String;
#26 = Utf8 a
#27 = Utf8 Ljava/lang/String;
#28 = Utf8 b
#29 = Utf8 c
#30 = Utf8 SourceFile
#31 = Utf8 StringExample.java
#32 = NameAndType #15:#16 // "<init>":()V
#33 = NameAndType #10:#11 // value:I
#34 = Utf8 ab
#35 = Utf8 java/lang/StringBuilder
#36 = NameAndType #40:#41 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#37 = NameAndType #42:#43 // toString:()Ljava/lang/String;
#38 = Utf8 org/example/cl07/StringExample
#39 = Utf8 java/lang/Object
#40 = Utf8 append
#41 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder;
#42 = Utf8 toString
#43 = Utf8 ()Ljava/lang/String;
Let's pay attention to the part described by Constant pool
, which represents the constant pool of the Class
There are mainly two types of constants stored in the constant pool.
- Literal.
- Symbol reference.
literal
Literals, the way to assign values to basic types of variables is called literals or literals. For example:
String a=“b”
, where "b" is a string literal, and the same analogy includes integer denominations, floating-point type literals, and character literals.In the above code, the bytecode of the literal constant is:
#3 = String #34 // ab #26 = Utf8 a #34 = Utf8 ab
Member variables, static variables, instance variables, and local variables modified with
final
#11 = Utf8 I #12 = Utf8 fs #13 = Utf8 ConstantValue #14 = Integer 101
From the above bytecode, the literal and final
modified attributes are stored in the constant pool. These literal values in the constant pool refer to the value of the data, such as ab
, 101
.
For basic data types, such as private int value=1
, in constant pool only retained his field descriptors (I) and
field name (value), it does not exist and the literal constant pool.
#10 = Utf8 value
#11 = Utf8 I
In addition, for
String c=a+b;
,c
is not saved to the constant pool, because during compilation, the values ofa
andb
#29 = Utf8 c #35 = Utf8 java/lang/StringBuilder #36 = NameAndType #40:#41 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder; #37 = NameAndType #42:#43 // toString:()Ljava/lang/String; #39 = Utf8 java/lang/Object #40 = Utf8 append #41 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder;
If we modify the code to the following form
public static void main(String[] args) {
final String a="ab";
final String b="a"+"b";
String c=a+b;
}
After re-generate bytecode, the bytecode can see changes, c
value of this property abab
also be saved to the constant pool.
#26 = Utf8 c
#27 = Utf8 SourceFile
#28 = Utf8 StringExample.java
#29 = NameAndType #12:#13 // "<init>":()V
#30 = NameAndType #7:#8 // value:I
#31 = Utf8 ab
#32 = Utf8 abab
Symbol reference
symbol reference mainly designed to involve concepts in compilation principles, including the following three types of constants:
Full Qualified Name of the class and interface (Full Qualified Name) , which is
Ljava/lang/String;
, is mainly used to parse the direct reference of the class at runtime.#23 = Utf8 ([Ljava/lang/String;)V #25 = Utf8 [Ljava/lang/String; #27 = Utf8 Ljava/lang/String;
The name and descriptor of the variable declared in the class or interface, including class-level variables (static) and instance-level variables .
#1 = Methodref #9.#32 // java/lang/Object."<init>":()V #2 = Fieldref #8.#33 // org/example/cl07/StringExample.value:I #3 = String #34 // ab #4 = Class #35 // java/lang/StringBuilder #5 = Methodref #4.#32 // java/lang/StringBuilder."<init>":()V #6 = Methodref #4.#36 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StrvalueingBuilder; #7 = Methodref #4.#37 // java/lang/StringBuilder.toString:()Ljava/lang/String; #8 = Class #38 // org/example/cl07/StringExample #24 = Utf8 args #26 = Utf8 a #28 = Utf8 b #29 = Utf8 c
method name and descriptor , the description of the method is similar to the "method signature" during JNI dynamic registration, that is, parameter type + return value type , such as the following byte code, which means
main
method andString
return type.#19 = Utf8 main #20 = Utf8 ([Ljava/lang/String;)V
Summary: In the Class file, there are some things that will not change, such as the name of a class, the field name of the class/the data type, the method name/return type/parameter name, constants, literals, etc. These are very important when the JVM interprets and executes the program, so after the compiler compiles the source code into a class
file, it will use a part of the byte classification to store these unchanged codes, and these bytes are called constant pools.
Runtime constant pool
runtime constant pool is the runtime manifestation of the constant pool (Constant Pool) of each class or interface.
We know that the loading process of a class will go through: loading,
connection (verification, preparation, analysis),
initialization process, and in this stage of class loading, you need to do the following things:
- Obtain the binary byte stream of this class through the fully qualified name of a class.
java.lang.Class
object is generated in the heap memory, which represents the loading of this class as the entry point of this class.- Convert
class
byte stream into the runtime data structure of the method area (meta space).
And the third point is that converts the static storage structure represented by the class byte stream into the runtime data structure of the method area, which includes the process of class file constant pool entering the runtime constant pool.
Therefore, the function of the runtime constant pool is to store class
file. In the parsing stage of the class, these symbol references are converted into direct references (the memory address of the instance object), and the translated direct references are also stored in the runtime Time constant pool. Most of the data in the constant pool of the class
The runtime constant pool is stored in the method area (JDK1.8 meta space), which is shared globally, and different classes share a runtime constant pool.
In addition, the runtime constant pool has a dynamic characteristic, and its content is not all the source and compiled class files. Constants can also be generated through code at runtime and put into the runtime constant pool. For example, the
String.intern()
method.
String constant pool
The string constant pool is simply a constant pool specifically designed for the String type.
There are two common ways to create a string constant pool.
String a="Hello";
String b=new String("Mic");
a
is determined during compilation and will enter the string constant pool.b
this variable, bynew
keyword to instantiate,new
is to create an object instance and initialize the instance, so this string object at runtime to determine the instance created on the heap space.
The string constant pool is stored in the heap memory space, and the creation form is shown in the figure below.
When using String a=“Hello”
to create a string object, the JVM first checks whether the string object exists in the string constant pool, and if it exists, it directly returns a reference to the string in the constant pool. Otherwise, a new string will be created in the constant pool and a reference to the string in the constant pool will be returned. (This method can reduce the repeated creation of the same string and save memory, which is also a manifestation of the Flyweight model).
As shown in the figure below, if youString c=“Hello”
Hello
already exists in the constant pool, you can directly return the reference to the string. (Design of Flyweight Mode in String)
When using String b=new String(“Mic”)
to create a string object, due to the immutability of the String itself (subsequent analysis), during the JVM compilation process, Mic
will be placed in the constant pool of the Class file. When the class is loaded, it will be Create the string Mic
the string constant pool. Then use the new
keyword to create a String
object in the heap memory and point to a reference to the Mic
As shown in the figure below, if younew String(“Mic”)
, since the string constant pool already existsMic
, you only need to create aString
object in the heap memory.
To summarize briefly: The reason why the JVM designs the string constant pool separately is some optimizations of the JVM in order to improve performance and reduce memory overhead:
Java
language, the String object is the object that occupies the largest space in the memory. Efficient use of strings can improve the overall performance of the system.- When creating a string constant, first check whether the string exists in the string constant pool. If there is, the reference instance is directly returned. If it does not exist, the string is instantiated and placed in the constant pool.
The string constant pool is a reference table of string instances maintained by the JVM. In the HotSpot VM, it is a global table called StringTable. What is maintained in the string constant pool is the reference of the string instance, and the underlying C++ implementation is a Hashtable. The string instances that these maintained references refer to are called "resident strings" or "interned strings" or commonly referred to as "strings that have entered the string constant pool"!
Encapsulation class constant pool
In addition to string constant pools, most of Java's basic types of wrapper classes also implement constant pools. Including Byte,Short,Integer,Long,Character,Boolean
Note that the floating-point data type Float,Double
does not have a constant pool.
The constant pool of the encapsulation class is implemented in their respective internal classes, such as IntegerCache
(the internal class of Integer
It should be noted that these constant pools have ranges:
- Byte,Short,Integer,Long : [-128~127]
- Character : [0~127]
- Boolean : [True, False]
The test code is as follows:
public static void main(String[] args) {
Character a=129;
Character b=129;
Character c=120;
Character d=120;
System.out.println(a==b);
System.out.println(c==d);
System.out.println("...integer...");
Integer i=100;
Integer n=100;
Integer t=290;
Integer e=290;
System.out.println(i==n);
System.out.println(t==e);
}
operation result:
false
true
...integer...
true
false
The constant pool of the package class is actually the cache instance (not the realization of the JVM virtual machine level) implemented in each package class. For example, in Integer, there is IntegerCache
, which caches data instances between -128 and 127 in advance. It means that the data in this interval all use the same data object. This is why in the above program, the result of the judgment ==
true
.
This design is actually an application of the Flyweight model.
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
The original intention of the encapsulated constant pool is actually the same as String, which is also for caching frequently used data intervals to avoid the memory overhead of frequently creating objects.
Exploring the problem of string constant pool
In the above constant pool, there are still many issues to be explored regarding the design of the String constant pool:
If a string constant already exists in the constant pool, how do you refer to the same string constant when defining the literal of the same string later? That is, the assertion result of the following code is
true
.String a="Mic"; String b="Mic"; assert(a==b); //true
- How big is the capacity of the string constant pool?
- Why design a separate constant pool for strings?
Why design a separate constant pool for strings?
First, let's look at the definition of String.
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; // Default to 0
}
Can be found from the above source code.
- The String class is
final
, which means that the class cannot be inherited. - The member attribute
value[]
String class is alsofinal
, which means that the member attribute cannot be modified.
Therefore, String
is immutable, which means that String
is created, it cannot be changed. There are several advantages of this design.
- Convenient implementation of string constant pool: In Java, because String constants are used a lot, if a String object is created every time a String is declared, it will cause a huge waste of space resources. Java puts forward the concept of String pool, which opens up a storage space String pool in the heap. When initializing a String variable, if the string already exists, it will not create a new string variable, but will return the existing string. A reference to a string that exists. If the string is variable and a string variable changes its value, then the value of the variable pointed to by it will also change, and the String pool will not be able to achieve it!
- Thread safety. In a concurrent scenario, multiple threads can read a resource at the same time. It is safe and will not cause competition. However, it is not safe to write to the resource. Immutable objects cannot be written, so multithreading is guaranteed. Security.
- Ensure that the hash attribute value does not change frequently. Uniqueness is ensured, so that
HashMap
can realize the correspondingkey-value
caching function, so when the object is created, its hashcode can be safely cached without recalculation. This is the reason why Map likes to use String as Key, and the processing speed is faster than other key objects. Therefore, the keys in HashMap often use String.
Note that because of the String
, it is very important to facilitate the realization of the string constant pool. At this time, the premise of the string constant pool is realized.
The string constant pool is actually the design of the Flyweight mode. It is similar to the cache design of IntegerCache and Character encapsulated objects provided in the JDK, except that String is a JVM-level implementation.
The allocation of strings, like other object allocations, consumes a high cost of time and space. In order to improve performance and reduce memory overhead, JVM has made some optimizations when instantiating string constants. In order to reduce the number of strings created in the JVM, the string class maintains a string pool. Whenever the code creates a string constant, the JVM first checks the string constant pool. If the string already exists in the pool, the instance reference in the pool is returned. If the string is not in the pool, a string will be instantiated and placed in the pool. Java can perform such optimizations because strings are immutable and can be shared without worrying about data conflicts.
We regard the string constant pool as a cache. When double quotes, first look it up in the string constant pool, and return the reference of the string constant pool directly if found, otherwise create a new one The string constants are placed in the constant pool.
How big is the constant pool?
I think everyone must be as curious as I am, how many constants can the constant pool store?
As we said before, the constant pool is essentially a hash table, and this hash means that it cannot be dynamically expanded. This means that the linked list in a single bucket is very likely to be very long, resulting in performance degradation.
In JDK1.8, the fixed bucket number of this hash table is 60013, we can configure the specified number through the following parameter
-XX:StringTableSize=N
You can add the following virtual machine parameters to print constant pool data.
-XX:+PrintStringTableStatistics
After adding the parameters, run the following code.
public class StringExample {
private int value = 1;
public final static int fs=101;
public static void main(String[] args) {
final String a="ab";
final String b="a"+"b";
String c=a+b;
}
}
When the JVM exits, the usage of the constant pool will be printed as follows:
SymbolTable statistics:
Number of buckets : 20011 = 160088 bytes, avg 8.000
Number of entries : 12192 = 292608 bytes, avg 24.000
Number of literals : 12192 = 470416 bytes, avg 38.584
Total footprint : = 923112 bytes
Average bucket size : 0.609
Variance of bucket size : 0.613
Std. dev. of bucket size: 0.783
Maximum bucket size : 6
StringTable statistics:
Number of buckets : 60013 = 480104 bytes, avg 8.000
Number of entries : 889 = 21336 bytes, avg 24.000
Number of literals : 889 = 59984 bytes, avg 67.474
Total footprint : = 561424 bytes
Average bucket size : 0.015
Variance of bucket size : 0.015
Std. dev. of bucket size: 0.122
Maximum bucket size : 2
You can see that the total size of the string constant pool is 60013
, where the literal is 889
.
When did the literal enter the string constant pool
String literals, unlike other basic types of literals or constants, are not filled and reside in the string constant pool during the resolve phase of class loading, but are stored in a special form at runtime Run-Time Constant Pool. But only when the string literal is called (such as executing the ldc bytecode instruction on it and adding it to the top of the stack), the HotSpot VM will resolve it and create a corresponding string in the string constant pool. The String instance.
Specifically, it should be when the instruction is executed at 1618899c9cbb30 (the instruction indicates that int, float or String constants are pushed from the constant pool to the top of the stack)
In the HotSpot VM of JDK1.8, this unresolved String literal is called pseudo-string and stored in the runtime constant pool in the form of JVM_CONSTANT_String. At this time, no String instance is created for it. .
At compile time, the string literal is stored in the constant pool of the class file in the form of "CONSTANT_String_info" + "CONSTANT_Utf8_info";
After the class is loaded, the string literal is stored in the Run-time Constant Pool in the form of "JVM_CONSTANT_UnresolvedString(JDK1.7)" or "JVM_CONSTANT_String(JDK1.8)";
When a string literal is used for the first time, the string literal is stored in the String Pool in the form of a real String object.
It can be proved by the following code.
public static void main(String[] args) {
String a =new String(new char[]{'a','b','c'});
String b = a.intern();
System.out.println(a == b);
String x =new String("def");
String y = x.intern();
System.out.println(x == y);
}
The string constructed with new char[]{‘a’,’b’,’c’}
does not use the constant pool when compiling, but when calling a.intern()
, it abc
to the constant pool and returns a reference to the constant pool.
intern() method
In the valueOf
method in Integer, we can see that if the passed value i
is within the range of IntegerCache.low
and IntegerCache.high
, the cached instance object is returned IntegerCache.cache
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
So, in the String type, since there is a string constant pool, is there a way to achieve a function similar to IntegerCache?
The answer is: intern()
method. Since the string pool level virtual machine technology, so String
class definition and no similar IntegerCache
such object pool, String
concept buffer / cell type mentioned only intern () this method.
/**
* Returns a canonical representation for the string object.
* <p>
* A pool of strings, initially empty, is maintained privately by the
* class {@code String}.
* <p>
* When the intern method is invoked, if the pool already contains a
* string equal to this {@code String} object as determined by
* the {@link #equals(Object)} method, then the string from the pool is
* returned. Otherwise, this {@code String} object is added to the
* pool and a reference to this {@code String} object is returned.
* <p>
* It follows that for any two strings {@code s} and {@code t},
* {@code s.intern() == t.intern()} is {@code true}
* if and only if {@code s.equals(t)} is {@code true}.
* <p>
* All literal strings and string-valued constant expressions are
* interned. String literals are defined in section 3.10.5 of the
* <cite>The Java™ Language Specification</cite>.
*
* @return a string that has the same contents as this string, but is
* guaranteed to be from a pool of unique strings.
*/
public native String intern();
The function of this method is: take the contents of the String to look up the table in the Stringtable, if it exists, return the reference, if it does not exist, save the "reference" of the object in the Stringtable table .
For example, the following program:
public static void main(String[] args) {
String str = new String("Hello World");
String str1=str.intern();
String str2 = "Hello World";
System.out.print(str1 == str2);
}
The result of the operation is: true.
Implement the logic as shown, str1
by calling str.intern()
obtaining the constant pool table to Hello World
quoted string, then str2
by literal declare a string constant, since this time Hello World
already present in the string constant pool, the It also returns the Hello World
, so that str1
and str2
have the same reference address, so the operation result is true
.
Summary: The intern method will query whether the current string exists from the string constant pool:
- If it does not exist, it will put the current string into the constant pool and return the local string address reference.
- If it exists, return the string address of the string constant pool.
Note that when all string literals are initialized, the
intern()
method will be called by default.During this procedure, the reason
a==b
, because the statementa
time, will passintern()
way to find out if there is a string constant pool stringHello
, because they do not exist, it is created. The same is true for theb
b
is declared, it is found thatHello
already exists in the character constant pool, so it directly returns the reference of the string constant.public static void main(String[] args) { String a="Hello"; String b="Hello"; }
OK, after learning here, do you feel that you understand? I have a question to test everyone. What is the result of the following program?
public static void main(String[] args) {
String a =new String(new char[]{'a','b','c'});
String b = a.intern();
System.out.println(a == b);
String x =new String("def");
String y = x.intern();
System.out.println(x == y);
}
The correct answer is:
true
false
The second output is false
is understandable, because new String(“def”)
will do two things:
def
in the string constant pool.new
keyword creates an instance objectstring
and points to a reference to the string constant pooldef
And x.intern()
def
obtained from the string constant pool. Their pointing addresses are different, and I will explain them in detail later.
Why is the first output result of true
The description of theintern()
method in the JDK documentation: When calling theintern
method, if the constant pool (built in the JVM) already contains the same string, the string in the pool is returned. Otherwise,String
object to the pool and return a referenceString
In constructing String a
when using new char[]{‘a’,’b’,’c’}
initialization string (not automatically call intern()
, lazy loading string is entered into the constant pool), and is not constructed in a string constant pool abc
string instance. So when the a.intern()
method is String
object will be added to the character constant pool, and a reference String
object will be returned, so the reference addresses pointed to by a
and b
question answer
Interview question: String a = "ab"; String b = "a" + "b"; Is a == b equal
answer : a==b
is equal for the following reasons:
- Variables
a
andb
are constant strings, of whichb
, during compilation, because there are no variable factors, the compiler will directly assign the variableb
toab
(this is in the category of compiler optimization, that is, compiling After that,b
will be saved to the literal amount in the Class constant pool). - For string constants,
a
ab
will be created in the string constant pool and a reference to the string constant pool will be returned. - For the variable
b
, when assigning the valueab
, first check whether the same string exists in the string constant pool, and if it exists, return the string reference. - Therefore, the references pointed to by a and b are the same, so
a==b
established.
conclusion of issue
Regarding the content of the constant pool, it will take some time to have a more in-depth and comprehensive understanding.
For example, after reading the above content, everyone thinks that they have a very in-depth understanding of the string constant pool. Yes, let's look at another question:
public static void main(String[] args) {
String str = new String("Hello World");
String str1=str.intern();
System.out.print(str == str1);
}
The above code obviously returns false
, the reason is shown in the figure below. Obviously, the reference addresses pointed to by str
and str1
But let's modify the above code:
public static void main(String[] args) {
String str = new String("Hello World")+new String("!");
String str1=str.intern();
System.out.print(str == str1);
}
The output of the above program becomes: true
. why?
This is also an optimization at the JVM compiler level. Because String is an immutable type, theoretically, the execution logic of the above program is: when string concatenation is performed +
, it is equivalent to the string constant pointed to by String
HelloWorld
taken out, and another !
pointed to by the String
variable is added to generate a new object.
Assuming that we are for
loop, a large number of objects will be generated. If these objects are not recycled in time, it will cause a very large waste of memory.
So after JVM optimization, it is actually spliced by StringBuilder, that is, only one object instance StringBuilder
will be generated, and then append
method.
To prove what I said, let's take a look at the bytecode of the above code.
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=3, args_size=1
0: new #3 // class java/lang/StringBuilder
3: dup
4: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
7: new #5 // class java/lang/String
10: dup
11: ldc #6 // String Hello World
13: invokespecial #7 // Method java/lang/String."<init>":(Ljava/lang/String;)V
16: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: new #5 // class java/lang/String
22: dup
23: ldc #9 // String !
25: invokespecial #7 // Method java/lang/String."<init>":(Ljava/lang/String;)V
28: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
31: invokevirtual #10 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
34: astore_1
35: aload_1
36: invokevirtual #11 // Method java/lang/String.intern:()Ljava/lang/String;
39: astore_2
40: getstatic #12 // Field java/lang/System.out:Ljava/io/PrintStream;
43: aload_1
44: aload_2
45: if_acmpne 52
48: iconst_1
49: goto 53
52: iconst_0
53: invokevirtual #13 // Method java/io/PrintStream.print:(Z)V
56: return
As you can see from the bytecode, a StringBuilder is constructed,
0: new #3 // class java/lang/StringBuilder
Then the string constants append
method, and finally the toString()
method is called to get a string constant.
16: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
28: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
31: invokevirtual #10 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
Therefore, the above code is equivalent to the following form.
public static void main(String[] args) {
StringBuilder sb=new StringBuilder().append(new String("Hello World")).append(new String("!"));
String str=sb.toString();
String str1=str.intern();
System.out.print(str == str1);
}
Therefore, the result obtained is true
.
There are many variants based on this question. For example, let's change it again. What is the result of the following program?
public static void main(String[] args) {
String s1 = "a";
String s2 = "b";
String s3 = "ab";
String s4 = s1 + s2;
System.out.println(s3 == s4);
}
The answer is false
.
Because the above program is equivalent to s3
and s4
point to different address references, naturally they are not equal.
public static void main(String[] args) {
String s1 = "a";
String s2 = "b";
String s3 = "ab";
StringBuilder sb=new StringBuilder().append(s1).append(s2);
String s4 = sb.toString();
System.out.println(s3 == s4);
}
Summary: Only a clear enough understanding of all the knowledge points related to the string constant pool, no matter how the interview process changes, you can answer accurately, this is the power of knowledge!
Copyright statement: All articles in this blog, except for special statements, adopt the CC BY-NC-SA 4.0 license agreement. Please indicate the reprint fromMic takes you to learn architecture!
If this article is helpful to you, please help me to follow and like. Your persistence is the motivation for my continuous creation. Welcome to follow the WeChat public account of the same name for more technical dry goods!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。