Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

String assignment of ref. to string object to memory happens after this statement?

Tags:

java

string

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.

like image 765
Mann Avatar asked Jul 13 '21 13:07

Mann


People also ask

What happens when string value is changed?

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.

What happens in string memory?

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).

In which memory string is stored when we create?

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.


Video Answer


6 Answers

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

  1. load str1 (from str1.equals) -> "one"
  2. load str2 (from str1 = str2) -> "two"
  3. store value to str1 (from str1 = str2) -> str1 is now "two"
  4. the value of the complete expression 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.
  5. invoke equals on "one" (which was already loaded, see above) with the parameter "two" -> false

You 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).

like image 136
Boris Avatar answered Oct 23 '22 23:10

Boris


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.

like image 34
Taemyr Avatar answered Oct 23 '22 23:10

Taemyr


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.

like image 7
f1sh Avatar answered Oct 23 '22 21:10

f1sh


// 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
like image 2
oleg.cherednik Avatar answered Oct 23 '22 22:10

oleg.cherednik


 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.

like image 1
sinclair Avatar answered Oct 23 '22 21:10

sinclair


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

like image 1
divadpoc Avatar answered Oct 23 '22 23:10

divadpoc