3

I'm new-ish to learning Java, but I'm just beginning to understand value types vs reference types. As I understand it, a String is a reference type. Unlike other reference types, however, I can print a String without it printing some weird combination of letters and numbers. Why is that? Furthermore, say I have the code

String s1 = "x";
String s2 = s1;
s1 = "xyz";
System.out.println(s2);

it will print "x", and not "xyz", even though I changed the object s2 is referring to. This doesn't happen when I change, say, an Array. Why are Strings special?

Bryant
  • 47
  • 1
  • 3
    strings are immutable. You didn't change the object `s2` is referring to. You just made `s1` refer to a different string. – Paul Rooney Dec 07 '18 at 11:43
  • java Strings are immutable, so your reassignment actually causes your variable to point to a new instance of String rather than changing the value of the String – Mohsen Dec 07 '18 at 11:43
  • 2
    That isn't really related to immutability, that is just how assigning a different object to a variable works in Java. – Mark Rotteveel Dec 07 '18 at 12:59
  • @Calaf The accepted and other highest upvoted answers to that question are of dubious value and not correct. I'm not sure if it is helpful to suggest as a duplicate. – Mark Rotteveel Dec 07 '18 at 13:09

2 Answers2

5

Unlike other reference types, however, I can print a String without it printing some weird combination of letters and numbers. Why is that?

Because String, as many other types, overrides Object.toString(), and returns another string than what the default implementation returns (it returns itself, of course).

This doesn't happen when I change, say, an Array.

You're confusing "assigning a new value to a variable", with "changing the state of an object". If you do the same thing as what you're doing here with an array, you'll have the same behavior:

char[] s1 = new char[] {'x'};
char[] s2 = s1;
s1 = new char[] {'x', 'y', 'z'};
System.out.println(Arrays.toString(s2));

When you assign a different array to s1, that doesn't change what the variable s2 points to.

Of course, if s1 and s2 points to the same array, and you modify that array, then they both keep pointing to the same, modified array:

char[] s1 = new char[] {'x'};
char[] s2 = s1;
s1[0] = 'y';
System.out.println(Arrays.toString(s2));
JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
1

Have a look at the implementation of Object#toString:

public String toString() {
    return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

Now, this is the implementation of String#toString:

public String toString() {
    return this;
}

Calling System.out.println(String s) prints directly the inner char array representation, whereas System.out.println(Object o) is implemented like this:

public void println(Object x) {
    String s = String.valueOf(x);
    synchronized (this) {
        print(s);
        newLine();
    }
}

And lastly, the implementation of String#valueOf:

public static String valueOf(Object obj) {
    return (obj == null) ? "null" : obj.toString();
}

Due to this, calling System.out.println on an object that did not override the toString method results in calling Object#toString, which returns the name of the class appended by hashCode.

Concerning your second question, see Java String variable setting - reference or value?

Ondra K.
  • 2,767
  • 4
  • 23
  • 39