Nie chcę się specjalnie czepiać, ale warto uściślić że to co napisał Bogdans odnosi się do stałych (tudzież literałów) typu String. Jest to efekt połączenia dwóch optymalizacji. Pierwszej polegającej na zwijaniu stałych oraz drugiej polegającej na zwijaniu stałych obiektów typu String.
W przykładzie powyżej kompilator zwija stałe podczas kompilacji, co daje mu dwie stałe o wartości "Baba Jaga", a następnie redukuje ich wszystkie wystąpienia do jednego (dzięki temu, że String jest niezmienne i wbudowane). Z s3 ta sztuczka nie może się udać ponieważ operacja += wykonuje się podczas wykonania programu, a nie kompilacji.
Krótko mówiąc referencje s1 i s2 są różnymi zmiennymi, ale odnoszą się do tego samego obiektu stałego "Baba Jaga".
Dzięki tej właściwości Javy jeżeli porównujemy zmienną String ze stałą, to można bezpiecznie użyć "==", zamiast equals (dlatego takie operacje występują w kodzie bibliotek Javy). Tyle, że trzeba mieć absolutną pewność, że jednym z operandów jest stała.