Hi,
String Literals will represent different references if they are newly created at runtime and hence return false. If the compiler is able to compute string literals at compile time,then, it will represent same references(because it can be found in the string pool),and hence return true.That is why when we declare a static final String and do the same thing you did, we get true.
Hope it helps! Please see the sample code..
Sample Code Check :
public final static String x3="ab";
public static void main(String[] args){
String x1 = "abc";
String x2 ="ab";
x2 = x2 + "c";
String x4 = x3+"c";
String x5= x1;
//Case 1 println: returns false
System.out.println(x1==x2);
// Case 2 println : returns true because we are comparing canonical representations
System.out.println(x1.intern() == x2.intern());
//Case 3 println: returns true because literal string can be computed at compile time
System.out.println(x1==x4);
//Case 4 println

oints to same reference,hence returns true
System.out.println(x1==x5);
}