Das Symptom
Der Entwickler vergleicht zwei Strings mit ==, testet kurz mit zwei Literalen im selben Programm, bekommt true und hält die Sache für erledigt. Im Betrieb kommt der eine String aus einem Formular, einer Datei oder einer Datenbank, und derselbe Vergleich liefert false, obwohl beide Texte sichtbar identisch sind. Der Login schlägt fehl, der Statusabgleich greift nie, die Bedingung im if wird nie wahr. Weil es auf dem eigenen Rechner einmal funktioniert hat, wird der Fehler an ganz anderer Stelle gesucht: im Encoding, in unsichtbaren Leerzeichen, in der Datenbank.
Die „Layer 8“-Wahrheit
Die Java-Sprachspezifikation regelt in Abschnitt 15.21.3 unmissverständlich, dass == bei Referenztypen Objektidentität prüft: „At run time, the result of == is true if the operand values are both null or both refer to the same object or array; otherwise, the result is false.“ Für Strings sagt die Spezifikation es sogar ausdrücklich: „While == may be used to compare references of type String, such an equality test determines whether or not the two operands refer to the same String object. The result is false if the operands are distinct String objects, even if they contain the same sequence of characters (§3.10.5, §3.10.6).“
Dass der Schnelltest mit Literalen true ergab, liegt nur daran, dass gleiche Konstanten im selben Programm auf dasselbe Objekt zusammengelegt werden; die Spezifikation verweist dafür auf die Abschnitte 3.10.5 und 3.10.6. Ein zur Laufzeit erzeugter String ist ein neues Objekt, und das ist der Normalfall für alles, was von außen kommt. Der Test hat also einen Sonderfall geprüft und daraus eine Regel gemacht.
Den richtigen Weg nennt die Spezifikation im selben Absatz: „The contents of two strings s and t can be tested for equality by the method invocation s.equals(t).“ Für Vergleiche, bei denen eine Seite null sein kann, bietet sich Objects.equals(a, b) an, für Konstanten das Muster KONSTANTE.equals(eingabe), das nie auf einer null-Referenz aufruft.
Der Fehler ist deshalb so hartnäckig, weil er aussieht wie ein Datenproblem. Niemand sucht die Ursache im Operator, wenn zwei Texte auf dem Bildschirm gleich sind. Aber der Operator hat die Texte nie verglichen. Er hat zwei Adressen verglichen und die richtige Antwort auf eine Frage gegeben, die niemand stellen wollte.
Statische Analyse findet den Vergleich übrigens zuverlässig, weil er rein syntaktisch ist: Zwei Ausdrücke vom Typ String, dazwischen ein doppeltes Gleichheitszeichen. Eine Warnung, die auf Fehler gestellt ist, bricht den Bau ab, bevor der Vergleich je in die Produktion gelangt. Das ist der seltene Fall, in dem ein Werkzeug einen Denkfehler vollständig abfangen kann, und es gibt wenig Gründe, ihm das nicht zu erlauben.