why is this?
String str1 = "one";
String str2 = "two";
System.out.println(str1.equals(str1 = str2)); // false, doesn't assignment of ref. to string object memory location happens after???
System.out.println(str1.equals(str1 = str2)); // true, same statement
I was asked this in a mock interview and yet I still don't get it.
Value of the StringValue is changed. It will run with the new value being stored in the argument object, instead of a string representing the property being changed. This event, like other changed events, can be used to track when an StringValue changes and to track the different values that it may change to.
Strings are used to store a sequence of characters in Java, they are treated as objects. The String class of the java. lang package represents a String. You can create a String either by using the new keyword (like any other object) or, by assigning value to the literal (like any other primitive datatype).
Whenever you create a string object using string literal, that object is stored in the string constant pool and whenever you create a string object using new keyword, such object is stored in the heap memory.
I think the answer is in the Java Language Specification that describes the Evaluation Order in point 15.7. Generally this is done from left-to-right. In your case the evaluation will be
str1.equals
) -> "one"
str1 = str2
) -> "two"
str1 = str2
) -> str1 is now "two"
str1 = str2
is "two"
, but remember that for this statement the str1 was already loaded in the first step. It will not be loaded again."one"
(which was already loaded, see above) with the parameter "two"
-> falseYou can also have a look at the decompiled Java Code (run javap -c ClassName
on your classpath) which will kind of show you this order as well:
0: ldc #7 // String one
2: astore_1
3: ldc #9 // String two
5: astore_2
[...]
9: aload_1 // <-- loads the value of str1
10: aload_2 // <-- loads the value of str2
11: dup
12: astore_1 // <-- stores to str1
13: invokevirtual #17 // <-- invokes equals
What this does not do is load the value of str1 again after storing to it.
The language specification has some more examples of edge-cases and how they are handled (like "what happens if one of the arguments of a function is a function that throws an exception).
Java specification 15.12.4. Run-Time Evaluation of Method Invocation, governs this. It states; "At run time, method invocation requires five steps. First, a target reference may be computed. Second, the argument expressions are evaluated. ..."
So we first compute the target and then evaluate the arguments.
The target expression in your example is str1
, which prior to evaluating the arguments will evaluate to a specific string object. So we call the Equals
method on that object. At this point we no longer care about the variable str1, and the fact that we change the object that this variable points to when evaluating the arguments is irrelevant.
In my first answer, I stated that each of these lines could be broken down into 3 individual statements: the assignment, the equals
, the println
. However, this produced different bytecode and thus, my claim was wrong.
Here is what happens internally, demonstrated using the byte code that the 3rd line of OP's snippet produces:
0: ldc #2 // String one
2: astore_1
3: ldc #3 // String two
5: astore_2
6: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;
9: aload_1
10: aload_2
11: dup
12: astore_1
13: invokevirtual #5 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
16: invokevirtual #6 // Method java/io/PrintStream.println:(Z)V
The noteworthy part here is that the aload_1
in offset 9 stores the initial value of str1
on the stack. Offsets 10, 11 and 12 then perform the reassignment, but the value on the stack is left untouched and thus still contains the old reference.
That is the reason why str1.equals(str1=str2) is false.
I guess this is prime example why inline assignments should be avoided.
// ref, str1 refers to string object "one"
String str1 = "one";
// ref, str2 refers to string object "two"
String str2 = "two";
// 1. "one".equals(str1 = str2): replace str1 with ref value which is "one"
// 2. "one".equals("two"): ref str1 is assigned with ref str2, so str1 now refers to "two"
System.out.println(str1.equals(str1 = str2)); // false
// 1. "two".equals(str1 = str2): replace str1 with ref value which is "two"
// 2. "two".equals("two"): same as №2 previous step
System.out.println(str1.equals(str1 = str2)); // true
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
....
}
Short answer: in the first statement this
holds "one", therefore operates on the 'old version' of str1
, even if the object changed in the meantime.
L1: String str1 = "one";
L2: String str2 = "two";
L3: System.out.println(str1.equals(str1 = str2)); // false, doesn't assignment of ref. to string object memory location happens after???
L4: System.out.println(str1.equals(str1 = str2)); // true, same statement
in L3 you compare str1 with the newly assigned value, but compare to the 'old' one, therefore false
in L4 you compare to the new one, and as both are now the same it's true
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With