Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Comparing strings with == which are declared final in Java

Tags:

java

string

final

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?

like image 488
Tiny Avatar asked Oct 14 '22 06:10

Tiny


People also ask

What happens when you use == to compare strings in Java?

In String, the == operator is used to comparing the reference of the given strings, depending on if they are referring to the same objects. When you compare two strings using == operator, it will return true if the string variables are pointing toward the same java object. Otherwise, it will return false .

Can you compare strings with == in Java?

To compare these strings in Java, we need to use the equals() method of the string. You should not use == (equality operator) to compare these strings because they compare the reference of the string, i.e. whether they are the same object or not.

Can string declared as a final?

In Java, we know that String objects are immutable means we can't change anything to the existing String objects. final means that you can't change the object's reference to point to another reference or another object, but you can still mutate its state (using setter methods e.g).

When comparing strings Why is == not a good idea?

Do not use the == operator to compare Strings. Note: When comparing two strings in java, we should not use the == or != operators. These operators actually test references, and since multiple String objects can represent the same String, this is liable to give the wrong answer.


1 Answers

When you declare a String (which is immutable) variable as final, 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:

String concat = "str" + "ing";  // which then becomes `String concat = "string";`

which when compared to "string" will give you true, because string literals are interned.

From JLS §4.12.4 - final Variables:

A variable of primitive type or type String, that is final and initialized with a compile-time constant expression (§15.28), is called a constant variable.

Also from JLS §15.28 - Constant Expression:

Compile-time constant expressions of type String are always "interned" so as to share unique instances, using the method String#intern().


This is not the case in your first code example, where the String variables are not final. 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 new String 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:

  Code:
   0:   ldc     #2; //String str
   2:   astore_1
   3:   ldc     #3; //String ing
   5:   astore_2
   6:   new     #4; //class java/lang/StringBuilder
   9:   dup
   10:  invokespecial   #5; //Method java/lang/StringBuilder."<init>":()V
   13:  aload_1
   14:  invokevirtual   #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   17:  aload_2
   18:  invokevirtual   #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   21:  invokevirtual   #7; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   24:  astore_3
   25:  getstatic       #8; //Field java/lang/System.out:Ljava/io/PrintStream;
   28:  aload_3
   29:  ldc     #9; //String string
   31:  if_acmpne       38
   34:  iconst_1
   35:  goto    39
   38:  iconst_0
   39:  invokevirtual   #10; //Method java/io/PrintStream.println:(Z)V
   42:  return

Clearly it is storing str and ing in two separate variables, and using StringBuilder to perform the concatenation operation.

Whereas, your second code example (final version) looks like this:

  Code:
   0:   ldc     #2; //String string
   2:   astore_3
   3:   getstatic       #3; //Field java/lang/System.out:Ljava/io/PrintStream;
   6:   aload_3
   7:   ldc     #2; //String string
   9:   if_acmpne       16
   12:  iconst_1
   13:  goto    17
   16:  iconst_0
   17:  invokevirtual   #4; //Method java/io/PrintStream.println:(Z)V
   20:  return

So it directly inlines the final variable to create String string at compile time, which is loaded by ldc operation in step 0. Then the second string literal is loaded by ldc operation in step 7. It doesn't involve creation of any new String object at runtime. The String is already known at compile time, and they are interned.

like image 235
Rohit Jain Avatar answered Oct 16 '22 18:10

Rohit Jain