From The Java Programming Language:
The equality operators (==, !=) test for reference identity, not object equivalence. Two references are identical if they refer to the same object; two objects are equivalent if they logically have the same value.and:
If you use == to compare string objects, you are actually comparing to see if they refer to the same object, not testing if the strings have the same contents.I know this, but it's obviously not as ingrained into my thinking as it needs to be. When I see str1 == str2 I don't immediately see a problem. (It worked fine in C++.)
To complicate matters, it's ok to use == for characters, numbers, enums, and for comparing to null. It's even ok for strings if they're interned or if they came from literals (since they're interned).
After finding this bug, I reviewed every == in my code. I found a half dozen more that should have been .equals (and got sore eyes).
If I had been writing the code from scratch I probably would not have had as much problem with this. But porting the code from C++ was worse because in C++ you handle this by redefining operator== (And I didn't know enough to review every ==)
To add insult to injury, switching to .equals opens up the potential for another error - null values. If you do x.equals(y) and x happens to be null, then you'll get a run-time exception. (Which you wouldn't have gotten with x == y.) This is why you should write "string".equals(x) instead of x.equals("string") because "string" can't be null.
(Interesting that it's called a NullPointerException. I thought Java didn't have pointers :-) Another leaky abstraction.)
So potentially I've introduced new bugs in the process of fixing the ones caused by == instead of .equals
It looks like FindBugs can catch this problem, I'll have to give it a try.
Maybe I'll learn to like it, but right now this part of the Java language seems pretty ugly.