4

Possibly this is a duplicate, but couldn't find the explanation I needed. Can someone explain me this.

If I'm correct:

String s1 = "a";
String s2 = "a";

Both s1 and s2 will point to the same address in String pool and there will be only one object with value "a".

Now, if I do this:

String s1 = "a"; //line A
s1 = s1.concat("b"); //line B; I don't understand what's happening here in terms of references
String s2 = "ab";//line C
System.out.println(s1 == s2); //false

Why do I get false?

My way of seeing this (probably wrong), goes like this:

after line A -> object is created (with value a) in String pool, referenced by s1; after line B -> object with value b is created in String pool (with no reference), then new object with value ab is created in String pool (referenced by existing s1) after line C -> (this is probably I'm wrong) Since existing object with value ab (referenced by s1) is created using concat() (or +), JVM will not reuse this object to be pointed by reference s2, it will rather just create new object in String pool with value ab pointed by reference s2;

Where am I wrong?

Wrapper
  • 794
  • 10
  • 25
  • 1
    https://www.baeldung.com/java-string-pool – OldProgrammer Aug 04 '20 at 17:40
  • @OldProgrammer thank you for the link, useful. But it doesn't help my situation. He's wrting about interning and difference between String literal and String created with new keyword. – Wrapper Aug 04 '20 at 17:51
  • 3
    As explained in the linked article, only String *literals* are interned into the String pool. When you invoke the `concat` method, a new String object is created, which will therefore not have the same address as the `"ab"` literal. @MiljanPuletic using the concat method creates a new String object in the same way that using the `new` keyword does. – ajc2000 Aug 04 '20 at 17:51
  • 1
    @MiljanPuletic finally I found the answer, and will now post it. :) looks like whatever I knew well, I had forgotten it for some time.. :) – Giorgi Tsiklauri Aug 04 '20 at 18:39
  • @GiorgiTsiklauri That's okey, I'll forgive you this time :) ) – Wrapper Aug 04 '20 at 18:43

4 Answers4

5

TL;DR - the point of your confusion is the Java memory model for Strings, namely the Heap and String Constant Pool areas of the memory.


Deep Dive into String memory model

Design Motivation

In Java, String is probably the most heavily used object. Because of this, Java maintains String objects with a special memory design strategy, holding them either in the Heap, in the isolated subset of the heap called String Constant Pool, or in both.

String Constant Pool is a special space in the Heap memory, which holds String objects of the unique "literal value"s. Anytime you create a String with its literal value, JVM first checks if the object of the same value is available in the String pool, and if it is, reference to the same object is returned, if it doesn't - the new object is allocated in the String Constant Pool, and the same happens for all other String literal creations again and again.

Reason, why having the Constant Pool is a good idea, is the semantics of this phrase itself - because it stores the constant and immutable String objects, and as you see, this is a great idea for the occasions when you might be creating many String objects with the same literal content - in all those cases, only one object for one "literal value" will be referenced each time and no newer objects will be created for the existing String literal object.

Note, that this is only possible because, String is immutable by definition. Also, note, that a pool of strings, which initially is empty, is maintained privately by the class String.

Where does Java place String objects?

Now this is where things get interesting. Important point to bear in mind, is that whenever you create String object with a new String() instruction, you force Java to allocate the new object into Heap; however, if you create a String object with the "string literal", it gets allocated in String Constant Pool. As we've said, the String Constant Pool exists mainly to reduce memory usage and improve the re-use of existing String objects in the memory.

So, if you'll write:

String s1 = "a";
String s2 = "a";
String s3 = new String("a");
  1. String object will be created into String Constant Pool and reference to that object will be stored into variable s1;
  2. String Constant Pool will be looked-up, and because of there is an object with the same literal value ("a") found in the pool, reference to the same object will be returned;
  3. String object will be explicitly created on the Heap area and the reference will be returned and stored into variable s3.

Internig Strings

If you wish to move the String object, created with new operator, into the String Constant Pool, you can invoke "your_string_text".intern(); method, and one of two will happen:

  1. 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 will be returned;
  2. otherwise, this String object will be added to the pool and a reference to this String object will be returned.

What happens in your code?

String s1 = "a";
String s2 = "a";

Both s1 and s2 will point to the same address in String pool and there will be only one object with value "a".

True. Initially, String object will be created and it will be placed into String Constant Pool. After that, as there is already String with value "a", no new object will be created for s2 and reference stored in s1 will be similarly stored into s2.

Now, let's finally have a look at your question:

String s1 = "a"; //allocated in the String Constant Pool
s1 = s1.concat("b"); //contact() returns a new String object, allocated in the Heap
String s2 = "ab";//"ab" still does NOT exist in the String Constant Pool, and it gets allocated there
System.out.println(s1 == s2); //returns false, because one object is in the Heap, and another is in the String Constant Pool, and as there already exists the object in the pool, with the same value, existing object will be returned by `intern()`.

If you will, however, execute

System.out.println(s1.intern() == s2);

this will return true, and I hope, by now, you understand - why. Because intern() will move the object referenced via s1 from Heap to the String Constant Pool.

Giorgi Tsiklauri
  • 9,715
  • 8
  • 45
  • 66
  • 1
    All strings are created in the heap memory. The string pool is only a table of *references* to some of those strings. Pretending that the pool was some kind of memory, is a widespread misconception. Therefore, `intern()` doesn’t move anything. It may add the *reference* to the pool if no matching entry existed and will return the contained reference. – Holger Aug 17 '20 at 12:20
4

From API Documentation

public String concat(String str)
...
If the length of the argument string is 0, then this String object is returned. Otherwise, a new String object is created, representing a character sequence that is the concatenation of the character sequence represented by this String object and the character sequence represented by the argument string.

pirho
  • 11,565
  • 12
  • 43
  • 70
  • Note that the wording has changed in Java 8 to "[...] Otherwise, *a* String object is returned that represents [..] the concatenation [...]", and no longer states that the object is a new one. – Polygnome Aug 04 '20 at 18:01
  • @Polygnome True. The behaviour still seems to be the same (tested Java8) but there is now kept the right to return the string from the pool as I understand the change (maybe wrong). – pirho Aug 04 '20 at 18:14
  • 1
    The method may return an existing string, even when it is not contained in the pool. Just consider `someString.concat("")`. – Holger Aug 17 '20 at 12:23
2

s1.concat("b") returns a new String and therefore s1 == s2 will be false.

It's like the following:

String a = "Hello";
String b = new String("Hello");

System.out.println(a == b); // false

Source code of String#concat in Java 11 just for reference:

public String concat(String str) {
    int olen = str.length();
    if (olen == 0) {
        return this;
    }
    if (coder() == str.coder()) {
        byte[] val = this.value;
        byte[] oval = str.value;
        int len = val.length + oval.length;
        byte[] buf = Arrays.copyOf(val, len);
        System.arraycopy(oval, 0, buf, val.length, oval.length);
        return new String(buf, coder);
    }
    int len = length();
    byte[] buf = StringUTF16.newBytesFor(len + olen);
    getBytes(buf, 0, UTF16);
    str.getBytes(buf, len, UTF16);
    return new String(buf, UTF16);
}
Arvind Kumar Avinash
  • 71,965
  • 6
  • 74
  • 110
  • Is that new String created in String pool or directly on Heap? – Wrapper Aug 04 '20 at 17:52
  • 1
    In the heap @MiljanPuletic. That's what happens every time a String object is created rather than a literal. – ajc2000 Aug 04 '20 at 17:54
  • @MiljanPuletic - whenever `new` operator is used to create a `String` object, it is stored in the heap. – Arvind Kumar Avinash Aug 04 '20 at 17:55
  • 1
    @ajc2000 that's actually wrong. String objects are created in the heap ***and*** in the String pool. So, even `contac()` returns a new string, `String s2 = "ab";//line C` will first look into pool, and if there's an existing String object with a literal value `"ab"`, then it should be fetched out. So, this comment and even this answer seems wrong to me. – Giorgi Tsiklauri Aug 04 '20 at 17:56
  • 1
    What? `String s2 = "ab";` is creating a String literal. Everyone else on this answer and in the comments is talking about what happens when you do `s1 = s1.concat("b");`, *not* `s2`. OP thought `concat` might be using the pool rather than creating a new String object. @ArvindKumarAvinash – ajc2000 Aug 04 '20 at 18:01
  • Which java version are you talking about? Because this changed over time. – Polygnome Aug 04 '20 at 18:02
  • @Polygnome JDK 13 – Wrapper Aug 04 '20 at 18:03
  • @ajc2000 there is no such a thing in Java as *creating String literal*. String literal can be provided as the *literal value* to either create or return existing object's (which is in the constant pool) reference. It's still the object reference. As I've already said, everyone can lookup the docs and see that `concat()` returns a new String. Question here is that `String s2 = "ab";` should be returning the reference to the existing (in the constant pool) String object. – Giorgi Tsiklauri Aug 04 '20 at 18:05
  • @GiorgiTsiklauri - Do you mean `a == b` should return `true` then? – Arvind Kumar Avinash Aug 04 '20 at 18:06
  • @ArvindKumarAvinash no, I don't mean that.. I'm now debugging it.. I'm kind of well aware of how the String works under the hood.. and this questions got me confused as well. :) So, trying to understand what's happening here.. but one thing is definitely true, that: If the equal String value is found on the heap's slice, which is constant pool, then the reference to that object should be returned. – Giorgi Tsiklauri Aug 04 '20 at 18:09
  • Did you read section 4 of this article? https://www.baeldung.com/java-string-pool I think if String pooling worked the way you're describing the results here would be very different @GiorgiTsiklauri – ajc2000 Aug 04 '20 at 18:10
  • From this, I took it this way. line B will create new String (with value 'ab') on the heap (with ref s1 pointing to it). line C -> it will look for a reference in string pool, if no value 'ab' is found (and it haven't been found in this case since existing 'ab' is on heap) then it will create new String on the string pool. When comparing these two references, false! Maybe if someone is willing to write a bit more elaborate example in separate answer for us 2nd year students :) – Wrapper Aug 04 '20 at 18:28
  • @MiljanPuletic - As already mentioned in the answer, `s1.concat("b")` returns a `new String` and whenever a `new String` is returned it will have a different address. I'm sure you understand the example (`a == b` returning `false`) given in the answer. If yes, what is that which is still causing the confusion? – Arvind Kumar Avinash Aug 04 '20 at 18:33
1

I suppose the String concat() method instantiates a new String("ab") which is a different object than the "ab" object.

Piovezan
  • 3,215
  • 1
  • 28
  • 45