"==" on objects (and arrays) is physical equality. That is, for objects a and b, a == b is true if a and b point to the same object.
The compiler does some optimizations for you. For example, the string "length" is only stored once as a constant. Hence s1 and s2 point to the same (constant) string.
Perhaps it will help to look at the outputted bytecode. Note that this is jasmin assembly code generated from a non-standard java compiler. You can confirm that the java compiler does something similar by running "javap -c" (the output is less readable, though).
Load reference to constant string "length" and store in s1.
ldc "length"
astore_1
Load reference to (the same) constant string "length" and store in s2.
ldc "length"
astore_2
Compare and print the result. It's not too readable.
getstatic java/lang/System/out Ljava/io/PrintStream;
new java/lang/StringBuffer
dup
invokespecial java/lang/StringBuffer/<init>()V
ldc "EQUAL: "
invokevirtual java/lang/StringBuffer/append(Ljava/lang/String;)Ljava/lang/StringBuffer;
aload_1
aload_2
if_acmpeq true0
iconst_0
goto end1
true0:
iconst_1
end1:
invokevirtual java/lang/StringBuffer/append(Z)Ljava/lang/StringBuffer;
invokevirtual java/lang/StringBuffer/toString()Ljava/lang/String;
invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V
Load "length: 10" and store in s3
ldc "length: 10"
astore_3
Concatenate "length: " with the length and store in s4. Note that concatenating will create a new string (and hence a new reference).
new java/lang/StringBuffer
dup
invokespecial java/lang/StringBuffer/<init>()V
ldc "length: "
invokevirtual java/lang/StringBuffer/append(Ljava/lang/String;)Ljava/lang/StringBuffer;
aload_3
invokevirtual java/lang/String/length()I
invokevirtual java/lang/StringBuffer/append(I)Ljava/lang/StringBuffer;
invokevirtual java/lang/StringBuffer/toString()Ljava/lang/String;
astore 4
Print as before and return.
As you can see, the first two variables are referring to the same string in the "constant pool". The variable s3 is referring to a string in the constant pool, too, but s4 is referring to a new string built from a constant string and a number.
Btw, you shouldn't rely on this behavior.