I have a simple question about strings in Java. The following segment of simple code just concatenates two strings and then compares them with ==
.
String str1="str";
String str2="ing";
String concat=str1+str2;
System.out.println(concat=="string");
The comparison expression concat=="string"
returns false
as obvious (I understand the difference between equals()
and ==
).
When these two strings are declared final
like so,
final String str1="str";
final String str2="ing";
String concat=str1+str2;
System.out.println(concat=="string");
The comparison expression concat=="string"
, in this case returns true
. Why does final
make a difference? Does it have to do something with the intern pool or I'm just being misled?
Stack and string conts pool concept
When you declare a
String
(which is immutable) variable asfinal
, and initialize it with a compile-time constant expression, it also becomes a compile-time constant expression, and its value is inlined by the compiler where it is used. So, in your second code example, after inlining the values, the string concatenation is translated by the compiler to:which when compared to
"string"
will give youtrue
, because string literals are interned.From JLS §4.12.4 -
final
Variables:Also from JLS §15.28 - Constant Expression:
This is not the case in your first code example, where the
String
variables are notfinal
. So, they are not a compile-time constant expressions. The concatenation operation there will be delayed till runtime, thus leading to the creation of a newString
object. You can verify this by comparing byte code of both the codes.The first code example (non-
final
version) is compiled to the following byte code:Clearly it is storing
str
anding
in two separate variables, and usingStringBuilder
to perform the concatenation operation.Whereas, your second code example (
final
version) looks like this:So it directly inlines the final variable to create String
string
at compile time, which is loaded byldc
operation in step0
. Then the second string literal is loaded byldc
operation in step7
. It doesn't involve creation of any newString
object at runtime. The String is already known at compile time, and they are interned.Let's see some byte code for the
final
exampleAt
0:
and2:
, theString
"string"
is pushed onto the stack (from the constant pool) and stored into the local variableconcat
directly. You can deduce that the compiler is creating (concatenating) theString
"string"
itself at compilation time.The non
final
byte codeHere you have two
String
constants,"str"
and"ing"
which need to be concatenated at runtime with aStringBuilder
.As per my research, all the
final String
are interned in Java. From one of the blog post:So it means if you call
String.intern()
you can compare two strings using==
operator. But hereString.intern()
is not necessary because in Javafinal String
are internally interned.You can find more information String comparision using == operator and Javadoc for String.intern() method.
Also refer this Stackoverflow post for more information.
If you take a look at this methods
and its decompiled with
javap -c ClassWithTheseMethods
versions you will seeand
So if Strings are not final compiler will have to use
StringBuilder
to concatenatestr1
andstr2
sowill be compiled to
which means that
concat
will be created at runtime so will not come from String pool.Also if Strings are final then compiler can assume that they will never change so instead of using
StringBuilder
it can safely concatenate its values socan be changed to
and concatenated into
which means that
concate
will become sting literal which will be interned in string pool and then compared with same string literal from that pool inif
statement.Though, when you create using String literal notation of Java, it automatically call intern() method to put that object into String pool, provided it was not present in the pool already.
Compiler knows the final variable never gonna change, when we add these final variables the output goes to String Pool because of
str1 + str2
expression output also never gonna change, So finally compiler calls inter method after output of the above two final variables. In case of non-final variable compiler do not call intern method.