When “” == s is false but “”.equals( s ) is true

给你一囗甜甜゛ 提交于 2019-11-27 06:16:36
String s = "";
String s2 = someUserInputVariale.toLowercase(); // where the user entered in ""

Something like that would cause s == s2 to evaluate to false.

Lots of code sill create new Strings without exposing the call to new String().

Bill the Lizard
"" == value // yields false

and

"".equals( value ) // yields true

any time the value of the variable value has not been interned. This will be the case if the value is computed at run time. See the JLS section 3.10.5 String Literals for example code illustrating this:

Thus, the test program consisting of the compilation unit (§7.3):

package testPackage;
class Test {
    public static void main(String[] args) {
        String hello = "Hello", lo = "lo";
        System.out.print((hello == "Hello") + " ");
        System.out.print((Other.hello == hello) + " ");
        System.out.print((other.Other.hello == hello) + " ");
        System.out.print((hello == ("Hel"+"lo")) + " ");
        System.out.print((hello == ("Hel"+lo)) + " ");
        System.out.println(hello == ("Hel"+lo).intern());
    }
}
class Other { static String hello = "Hello"; }

and the compilation unit:

package other;
public class Other { static String hello = "Hello"; }

produces the output:

true true true true false true

This example illustrates six points:

  • Literal strings within the same class (§8) in the same package (§7) represent references to the same String object (§4.3.1).
  • Literal strings within different classes in the same package represent references to the same String object.
  • Literal strings within different classes in different packages likewise represent references to the same String object.
  • Strings computed by constant expressions (§15.28) are computed at compile time and then treated as if they were literals.
  • Strings computed at run time are newly created and therefore distinct.
  • The result of explicitly interning a computed string is the same string as any pre-existing literal string with the same contents.

If you can grab a hold of the book Java Puzzlers by Joshua Bloch and Neal Gafter, and look at puzzle 13, "Animal Farm"... he has great advice on this issue. I am going to copy some relevant text:

"You may be aware that compile-time constants of type String are interned [JLS 15.28]. In other words any two constant expressions of type String that designate the same character sequence are represented by identical object references... Your code should rarely, if ever, depend on the interning of string constants. Interning was designed solely to reduce the memory footprint of the virtual machine, not as a tool for programmers... When comparing object references, you should use the equals method in preference to the == operator unless you need to compare object identity rather than value."

That's from the above reference I mentioned... pages 30 - 31 in my book.

Would you expect "abcde".substring(1,2) and "zbcdefgh".substring(1,2) to yield the same String object?

They both yield "equal" sub-strings extracted from two different Strings, but it seems quite reasonable that tehy are different objects, so == sees them as different.

Now consider when the substring has length 0, substring(1, 1). It yields a zero length String, but it's not surprising that the "abcde".substring(1,1) is a different object from "zbcdefgh".substring(1,2) and hence at least one of them is a different object from "".

As I understand it while compiling the Java code to bytecode or while running the program same strings will be referenced to the same object in the most cases to save memory. So sometimes you get away with == comparisons of strings. But this is a compiler optimization you can not rely on.

But then sometimes it happens that the compiler decides to not do this optimization or there is no way for the program to see that the strings are the same and out of the sudden the check fails since you are relying on some underlying optimization voodoo that depends on implementation of the jvm you are using and so on.

So using equals is always the good thing to do. For empty strings there are other possibilities like comparing with length == 0 or if you don't care about backwards compatibility there is string.empty().

Paul Sonier

You should try considering String.length() == 0.

The javadoc for String.intern() has some good commentary on == vs. .equals().

The documentation also clarifies that every string literal is intern'd.

public String intern()

Returns a canonical representation for the string object.

A pool of strings, initially empty, is maintained privately by the class String.

When the intern method is invoked, if the pool already contains a string equal to this String object as determined by the equals(Object) method, then the string from the pool is returned. Otherwise, this String object is added to the pool and a reference to this String object is returned.

It follows that for any two strings s and t, s.intern() == t.intern() is true if and only if s.equals(t) is true.

All literal strings and string-valued constant expressions are interned. String literals are defined in §3.10.5 of the Java Language Specification

Returns: a string that has the same contents as this string, but is guaranteed to be from a pool of unique strings.

If you use google code search, you can find lots of places where people make this same error: google for file:.java \=\=\ \"\" Of course, this can be a correct idiom in carefully controlled circumstances, but usually, its just a bug.

JeeBee

Why not use:

if (value != null && value.length == 0) {
    // do stuff (above could be "== null ||"
}

You should use equals() because == for objects compares references, i.e., are they the same object. Whilst at compile time Java finds identical strings and makes them share the same reference (Strings are immutable), at runtime it is easy to create empty strings that have different references, where == fails for your typical intention of equals().

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!