Reason: Strings are handled differently that other variables. They are immutable and once created they are stored in a string pool for reuse (note, the strings themselves NOT the references). If a reference is created to a string that already exists ("abc") then that string is reused from the string pool, as it can never change - i.e there is only one string "abc" created above. However, there are 3 string references created s, s1 and s2. The first thing to understand is the the == checks if the references refer to the same object in memory, it does not check if their contents are equal. Here, s and s1 refer to the same object on the heap and hence s == s1 returns true. However, s2 is created using the new operator meaning that s2 refers to a new object on the heap meaning that s == s2 will be false (they do NOT refer to the same object).
To check if they are meaningfully equal (using their values) you use the .equals() method. This has been overridden by the String class to check if the strings that the string reference variables refer to are equal. Hence both s.equals(s1) and s.equals(s2) return true.
You should always use the .equals() method to check if objects are meaningfully equal. This method (along with hashcode()) should be overridden in a class that you write if you need to know what makes the object equivalent. For example: