I looked it up in a book, which is usually more thorough in terms of explanations than a website.
Take this for ex.:
if (nickname == "Bob")
The condition will be true only if nickname
is referring to the same String object.
Here is a sentence I found confusing, can anyone please explain to why this is the case:
For efficiency, Java makes only one string object for every string constant.
The book points out that the way of assembling the object "Bob" also affects whether the condition will be true of not, which confuses me the most.
For ex.:
String nickname = "Bob";
...
if (nickname == "Bob") //TRUE
But if "Bob" is created from .substring()
method, condition will be FALSE.
String name = "Robert";
String nickname = name.substring(0,3);
...
if (nickname == "Rob")//FALSE
Why is this so?
Edit: in the end of the book's explanation, I found a sentence which also confuses me a lot:
Because string objects are always constructed by the compiler, you never have an interest in whether two strings objects are shared.
Doesn't everything we write get constructed by the compiler?
You should not use == (equality operator) to compare these strings because they compare the reference of the string, i.e. whether they are the same object or not. On the other hand, equals() method compares whether the value of the strings is equal, and not the object itself.
“Using the equality (==) and inequality (!=) operators to compare two objects does not check to see if they have the same values. Rather it checks to see if both object references point to exactly the same object in memory. The vast majority of the time, this is not what you want to do.”
The major difference between the == operator and . equals() method is that one is an operator, and the other is the method. Both these == operators and equals() are used to compare objects to mark equality.
The == operator compares whether two object references point to the same object. For example: System.
You need to understand 2 things
String a = "Bob";
String b = "Bob";
System.out.println(a.equals(b));
System.out.println(a == b);
How do you think? What the output?
true
true
What doing this? First string created in string pool in permanent generation memory. Second string get existing object from pool.
String a = "Bob"; // create object in string pool(perm-gen)
String b = "Bob"; // getting existing object.
How right you noticed :
For efficiency, Java makes only one string object for every string constant.
String nickname = name.substring(0,3);
As String is immutable object name.substring(0,3);
created new String("Rob")
in heap memory, not in perm-gen.
In Java 8 String pool is created in PermGen area of Heap, garbage collection can occur in perm space but depends upon JVM to JVM. By the way from JDK 1.7 update, String pool is moved to heap area where objects are created.
Read more here.
String literals are internally handled by the JVM so that for every unique String literal, it always refers to the same object if it has the same value. For example, a string literal "test" in class A will be the exact same object as a string literal "test" in class B.
Doesn't everything we write get constructed by the compiler?
The compiler simply adds a the string literal to the classes constant pool
upon compilation and loads it with a special instruction called LDC
, the rest is handled by the JVM, which loads the string constant from a special string constant pool that never removes / garbage-collects any objects (previously permgen).
However, you can get the 'internal' version of any string (as if it was a string literal) using String#internal()
, which would cause the ==
operator to work again.
It's about objects.
Since these aren't primitives ==
doesn't compare what they are. ==
compares where they are (in heap memory).
.equals()
should (if implemented) compare what's contained in that memory.
This is a detail that is easily forgotten because small strings and boxed numbers often don't get new memory when created because it's more optimal to instead point you to cached version of the same thing. Thus you can ask for a new "Bob" over and over and just get handed a reference (memory address) to the same "Bob". This tempts us to compare them like primitives since that seems to work the same way. But not every object will have this happen to it so it's a bad habit to let yourself develop.
This trick works only when 1) a matching object already exists, 2) it's immutable so you can't surprise users of other "copies" by changing it.
To abuse an old metaphor, if two people have the same address it's a safe bet that they keep the same things at home, since it's the same home. However, just because two people have different addresses doesn't mean they don't keep exactly the same things at home.
Implementing .equals()
is all about defining what we care about when comparing what is kept in these objects.
So only trust ==
to compare values of primitives. Use .equals()
to ask an object what it think's it's equal to.
Also, this isn't just a java issue. Every object oriented language that lets you directly handle primitives and object references/pointers/memory address will force you to deal with them differently because a reference to an object is not the object it self.
The objects value is not the same as it's identity. If it was there would only ever be one copy of an object with the same contents. Since the language can't perfectly make that happen you're stuck having to deal with these two concepts differently.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With