Java String variable setting - reference or value? - java

The following Java code segment is from an AP Computer Science practice exam.
String s1 = "ab";
String s2 = s1;
s1 = s1 + "c";
System.out.println(s1 + " " + s2);
The output of this code is "abc ab" on BlueJ. However, one of the possible answer choices is "abc abc". The answer can be either depending on whether Java sets String reference like primitive types (by value) or like Objects (by reference).
To further illustrate this, let's look at an example with primitive types:
int s1 = 1;
int s2 = s1; // copies value, not reference
s1 = 42;
System.out.println(s1 + " " + s2); // prints "1 42"
But, say we had BankAccount objects that hold balances.
BankAccount b1 = new BankAccount(500); // 500 is initial balance parameter
BankAccount b2 = b1; // reference to the same object
b1.setBalance(0);
System.out.println(b1.getBalance() + " " + s2.getBalance()); // prints "0 0"
I'm not sure which is the case with Strings. They are technically Objects, but my compiler seems to treat them like primitive types when setting variables to each other.
If Java passes String variables like primitive type, the answer is "abc ab". However, if Java treats String variables like references to any other Object, the answer would be "abc abc"
Which do you think is the correct answer?

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.
String s1 = "ab";
String s2 = s1;
s1 = s1 + "c";
System.out.println(s1 + " " + s2);
on line 2, s1 == s2 AND s1.equals(s2). After your concatenation on line 3, s1 now references a different instance with the immutable value of "abc", so neither s1==s2 nor s1.equals(s2).

The difference between your BankAccount and a String is that a String is immutable. There is no such thing as 'setValue()' or 'setContent()'. The equivalent example with your bank account would be :
BankAccount b1 = new BankAccount(500); // 500 is initial balance parameter
BankAccount b2 = b1; // reference to the same object
b1 = new BankAccount(0);
System.out.println(b1.getBalance() + " " + s2.getBalance()); // prints "0 500"
So if you think of it this way (not actually what the compiler does, but functionally equivalent) the String concatenation scenario is:
String s1 = "ab";
String s2 = s1;
s1 = new String("abc");
System.out.println(s1 + " " + s2); //prints "abc ab"

It is not relevant whether String is treated like a primitive or like an object!
In the String example, the concatenation of two strings produces a new String instance, which is then assigned to s1. The variable s2 still references the unchanged(!) old String instance.
Assuming the BankAccount had a method to set the balance, which returns a new BankAccount, your example could look like this:
BankAccount b1 = new BankAccount(500); // 500 is initial balance parameter
BankAccount b2 = b1; // reference to the same object
b1 = b1.createNewAccountWithBalance(0); // create and reference a new object
System.out.println(b1.getBalance() + " " + b2.getBalance()); // prints "0 500"

Indeed, String is a class and it's assigned / passed by reference.
But what's confusing is the statement:
String s = "abc";
Which suggests that String is a primitve ( like 'int x = 10;' );
But that's only a shortcut, the statement 'String s = "abc";' is actually compiled as 'String s = new String( "abc" );'
Just like 'Integer x = 10;' is compiled as 'Integer x = new Integer( 10 );'
This mechanism is called 'boxing'.
And more confusing is: there's a class 'Integer' and a primitive 'int',
but String doesn't have a primitive equivalent (allthough char[] comes close)
Sije de Haan

In Java, String objects are assigned and passed around by reference; it this respect they behave exactly like any other object.
However, Strings are immutable: there isn't an operation that modifies the value of an existing string in place, without creating a new object. For example, this means that s1 = s1 + "c" creates a new object and replaces the reference stored in s1 with a reference to this new object.

String is a class, so a String variable is a reference. But it's a language intrinsic, in the sense that Java has special handling and syntax for it, which is why you can do things like your example.
See e.g. http://download.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html.

java.lang.String is an object, not a primitive.
What the code did in the first example is:
Define s1 as "ab"
Set s2 equal to the same underlying object as s1
Set s1 equal to a new string that is the combination of s1's old value and "c"
But to answer your question about reference or value, it's by reference.

The assertion
if Java treats String variables like references to any other Object, the answer would be "abc abc"
is incorrect. Java does treat String variables like references to any other Object. Strings are Objects but the answer is "abc ab" none the less.
The issue is not what the assignment operator does. The assignment operator assigns a reference to a String object in every case in your example.
The issue is what the concatenation operator ('+') does. It creates a new String object. As other have said, this is necessary because a String object is immutable but it is an issue of operator behaviour and not merely because String is immutable. The concatenation operator could return a new Object even if a String object were mutable.
In contrast, in your second example, b1.setBalance(0) does not create a new object, it modifies the existing object.

int s1 = 1;
int s2 = s1; // copies value, not reference
s1 = 42;
System.out.println(s1 + " " + s2); // prints "1 42"
Doesn't print "1 42" but "42 1".Take each discrete line into consideration.First s1 assigns 1, then s2 assigns s1, which is 1 up until now(suppose java didn't see the third line yet.) Then java sees the third line and immediately changes s1 into 42.After that java was told to print what it knows so far, and that is s1 is 42 and s2 is 1(the old s1).
As for the String the same thing happens.
String s1 = "ab";
String s2 = s1;
s1 = s1 + "c";
System.out.println(s1 + " " + s2);// prints "abc ab".
Fort String it doesn't necessarily changes s1 but rather s1 now refers to a new String object in the heap memory, but the old "ab" object is still there, with a new reference of s2!

Related

String + String vs String + String returned from method

Today while working with String's i have encountered a behavior i don't know before. I'm not able to understand what's happening internally.
public String returnVal(){
return "5";
}
String s1 = "abcd5";
String s2 = "abcd"+"5";
String s3 = "abcd5";
String s4 = "abcd"+returnVal();
System.out.println(s1 == s2);
System.out.println(s1.equals(s2));
System.out.println(s3 == s4);
System.out.println(s3.equals(s4));
My expectation is printing "true" from all s.o.p's but s3 == s4 is false, why?
My expectation is printing "true" from all s.o.p's but s3 == s4 is false, why?
The compiler can do constant expression inlining. This means that
String s1 = "abcd5";
String s2 = "abcd"+"5";
final String five = "5"; // final reference
String sa = "abcd" + five;
are all the same (except five) and the compiler can simplify all these expressions to "abcd5"
However, if the compiler cannot optimise the expression, the operation is performed at runtime and a new String is created. This new String is not a constant which is places in the String literal pool (as it is not a literal in byte code)
String s4 = "abcd" + returnVal(); // not inlined by the compiler.
String f5 = "5"; // not a final reference.
String sb = "abcd" + f5; // evaluated at runtime
These create new strings every time they are run (as well as new StringBuilder and char[]s)
You have stumbled across the intricacies of how the Java compiler optimizes String.
Suppose I have this program:
String a = "abc";
String b = "abc";
Here the compiler can initialize a and b to the same String instance. This entails that a == b and a.equals(b).
Here we also get the same behaviour:
String a = "abc";
String b = "ab" + "c";
This is because "ab" + "c" can be evaluated at compile-time to just "abc", which in turn can share an instance with a.
This technique is not possible with expressions that call functions:
String a = "abc";
String b = "ab" + functionThatReturnsC();
This is because functionThatReturnsC could have side-effects which cannot be resolved at compile-time.
Your case of returnVal is interesting. Since it is constant, it could be inlined, in which case the compile-time instance sharing could be applied. It seems the compiler implementer decided not to support this.
This issue exposes a weakness of Java. Since we cannot override =, programmers cannot implement custom value-types. Therefore, you should always use equals or Objects.equals to ensure consistent behaviour.
Note that these optimizations may differ between compilers.

What happens when a string literal is changed? [duplicate]

This question already has answers here:
Is Java "pass-by-reference" or "pass-by-value"?
(93 answers)
toUpperCase in Java does not work [duplicate]
(5 answers)
Closed 6 years ago.
I want to change a string literal into all-uppercase one. Here is my code:
// a and b are the same literal
String a = "Test";
String b = "Test";
// now I want to change all b's letter
// into uppercases, but fail.
b.toUpperCase();
System.out.println("a = " + a + ", " + "b = " + b);
// print: a = Test, b = Test
String c = "Test1";
System.out.println("c = " + c + " , c.toUpperCase() = "
+ c.toUpperCase());
// print: c = Test1 , c.toUpperCase() = TEST1
// change letters of "Test" literal
// into uppercase and success
System.out.println("Test".toUpperCase());
// print: TEST
My question is:
1. b can't be changed into uppercase one, but c and "Test" can. Why?
What I know is:
1. a and b reference to the same object in string pool.
2. String is immutable, but it seems not relevant to this question.
Strings are immutable. So for change b:
b = b.toUpperCase();
Every time then you do something that changes a String, a new String object is created. So you need change the reference on object.
String is immutable, but it seems not relevant to this question
Actually, it's very relevant to the question
b can't be changed into uppercase one
Because toUpperCase() returns a new string by acting on the invoking string , use
b = b.toUpperCase();
c and "Test" can. Why?
c has not been changed it's result has been added to the string in System.out.println()
Let's take your code line by line and please read my comments :
// a and b are the same literal
/* FIRST POINT :
Here you assigned two times the same value "Test",
BUT IT'S 2 DIFFERENT OBJECTS IN MEMORY */
String a = "Test";
String b = "Test";
// now I want to change all b's letter
// into uppercases, but fail.
/* SECOND POINT :
Here you just apply a function (toUpperCase()) on "b" object.
This function returns a string object but
YOU ARE NOT DOING ANYTHING WITH IT
i.e. displaying it or reassigning it to another variable!
*/
b.toUpperCase();
System.out.println("a = " + a + ", " + "b = " + b);
// THAT'S WHY IT STILLS PRINT
// print: a = Test, b = Test
String c = "Test1";
System.out.println("c = " + c + " , c.toUpperCase() = "
+ c.toUpperCase());
/* THIRD POINT :
Here you apply a function (toUpperCase()) on "c" object but this time
YOU ARE REUSING THE RETURN STRING :)
i.e. you are displaying it!
*/
// print: c = Test1 , c.toUpperCase() = TEST1
// change letters of "Test" literal
// into uppercase and success
/* LAST POINT :
Here you do the same as you did before on "c" object
YOU ARE REUSING THE RETURN STRING AGAIN :)
i.e. you are displaying it!
*/
System.out.println("Test".toUpperCase());
// print: TEST
Last but not least calling toUpperCase()/toLowerCase() functions on string objects will never reassign the object's value. These functions only RETURN a string.
The way to reassign the string value is the usual way :
String a = "Test";
a = a.toUpperCase();
Please note, as many said, that this will create another object in memory "TEST" and assign it to "a" and your old string "Test" will then become a candidate to the garbage collector.
I hope it makes more sense now.
Cheers,
What happens when a string literal is changed?
Nothing. That is the string literal object cannot change, because as you point out you already know, it is immutable. References to it (variables like a,b,c) can be made to reference other strings, but that string instance will not change from "Test".
But to explain your code:
This is the difference between b and c:
b.toUpperCase(); //there's a result from this function you are not using
System.out.println("b = " + b);
System.out.println("c = " + c.toUpperCase()); //you're using the result here.
String is immutable, but it seems not relevant to this question
It is relevant, if you know that it is immutable, it is obvious that b cannot change to upper case and that a new string must be created as a result of toUpperCase and you must therefore use that. However b can be made to reference the new string, and this wont affect a or anything else which still references the old string:
b = b.toUpperCase(); //b now is set to the new upper case string
System.out.println("b = " + b);
My question is: 1. b can't be changed into uppercase one, but c and "Test" can. Why?
My answer is when you print c.toUpperCase(), variable c is not changed at all.
You merely returned the another String which was built to uppercase based on the content of c.
The same applies to String "test" as well.
Even if you do this, you are only pointing c to a new String:
String c = "Test1";
c = c.toUpperCase();
This is what happened:
//String c = "Test1";
+-------+
|"Test1"| <--- c
+-------+
//c = c.toUpperCase();
+-------+
|"TEST1"| <--- c
+-------+
+-------+
|"Test1"| <--- waiting to be collected by Garbage collector
+-------+
You need to change like this,because strings are immutable
public static void main(String[] args) {
// a and b are the same literal
String a = "Test";
String b = "Test";
// now I want to change all b's letter
// into uppercases, but fail.
b= b.toUpperCase();
System.out.println("a = " + a + ", " + "b = " + b);
// print: a = Test, b = Test
String c = "Test1";
// c=c.toUpperCase();
System.out.println("c = " + c + " , c.toUpperCase() = "
+ (c=c.toUpperCase()));
// print: c = Test1 , c.toUpperCase() = TEST1
// change letters of "Test" literal
// into uppercase and success
System.out.println("Test".toUpperCase());
// print: TEST
I suggest you to look into the Java API. By using toUpperCase you will get a new Object of String. If you want to print out the variable with new text, you should assign the new object to the variable. In case of c, you're printing out the returned "new" content of the object. The variable c will be lower case anymore.

Confuse about String reference comparison == with intern

I read this when should we use intern method of string on string constants but still not very clear with String == compare also with intern(). I have a couple examples. Can someone help me understand this better.
String s1 = "abc";
String s2 = "abc";
String s3 = "abcabc";
String s4 = s1 + s2;
System.out.println(s3 == s4); // 1. why false ?
System.out.println(s3 == s4.intern()); // 2. why true ?
System.out.println(s4 == s1 + s2); // 3. why false ?
System.out.println(s4 == (s1 + s2).intern()); // 4. why false ?
System.out.println(s4.intern() == (s1 + s2).intern()); // 5. why true ?
There are quite a lot of answers here which exlain that, but let me give you another one.
A string is interned into the String literal pool only in two situations: when a class is loaded and the String was a literal or compile time constant. Otherwise only when you call .intern() on a String. Then a copy of this string is listed in the pool and returned. All other string creations will not be interned. String concatenation (+) is producing new instances as long as it is not a compile time constant expression*.
First of all: never ever use it. If you do not understand it you should not use it. Use .equals(). Interning strings for the sake of comparison might be slower than you think and unnecessarily filling the hashtable. Especially for strings with highly different content.
s3 is a string literal from the constant pool and therefore interned. s4 is a expression not producing an interned constant.
when you intern s4 it has the same content as s3 and is therefore the same instance.
same as s4, expression not a constant
if you intern s1+s2 you get the instance of s3, but s4 is still not s3
if you intern s4 it is the same instance as s3
Some more questions:
System.out.println(s3 == s3.intern()); // is true
System.out.println(s4 == s4.intern()); // is false
System.out.println(s1 == "abc"); // is true
System.out.println(s1 == new String("abc")); // is false
* Compile time constants can be expressions with literals on both sides of the concatenation (like "a" + "bc") but also final String variables initialized from constants or literals:
final String a = "a";
final String b = "b";
final String ab = a + b;
final String ab2 = "a" + b;
final String ab3 = "a" + new String("b");
System.out.println("ab == ab2 should be true: " + (ab == ab2));
System.out.println("a+b == ab should be true: " + (a+b == ab));
System.out.println("ab == ab3 should be false: " + (ab == ab3));
One thing you have to know is, that Strings are Objects in Java. The variables s1 - s4 do not point directly to the text you stored. It is simply a pointer which says where to find the Text within your RAM.
It is false because you compare the Pointers, not the actual text. The text is the same, but these two Strings are 2 completely different Objects which means they have diferent Pointers. Try printing s1 and s2 on the console and you will see.
Its true, because Java does some optimizing concerning Strings. If the JVM detects, that two different Strings share the same text, they will be but in something called "String Literal Pool". Since s3 and s4 share the same text they will also sahe the same slot in the "String Literal Pool". The inter()-Method gets the reference to the String in the Literal Pool.
Same as 1. You compare two pointers. Not the text-content.
As far as I know added values do not get stored in the pool
Same as 2. You they contain the same text so they get stored in the String Literal Pool and therefore share the same slot.
To start off with, s1, s2, and s3 are in the intern pool when they are declared, because they are declared by a literal. s4 is not in the intern pool to start off with. This is what the intern pool might look like to start off with:
"abc" (s1, s2)
"abcabc" (s3)
s4 does not match s3 because s3 is in the intern pool, but s4 is not.
intern() is called on s4, so it looks in the pool for other strings equaling "abcabc" and makes them one object. Therefore, s3 and s4.intern() point to the same object.
Again, intern() is not called when adding two strings, so it does not match from the intern() pool.
s4 is not in the intern pool so it does not match objects with (s1 + s2).intern().
These are both interned, so they both look in the intern pool and find each other.

Literal string creation vs String object creation

How many String object are created
I am studying for the SCJP I cant seem to get my head round this String problem. I seem to see several possible answers depending on how i look at a question.
In the following initialization, how many string objects are created?
String s1 = "A" + "B" + "C" + "D";
System.out.println(s1)
Initially i thought 5 objects, i.e.
"A"
"B"
"C"
"D"
"ABCD"
But then thinking about it i am not really sure because for example will the compiler concatenate "A" + "B" as one object? i.e creating 7 objects?
"A"
"B"
"C"
"D"
"AB"
"ABC"
"ABCD"
Also, how many objects will be created if the code was changed to be
String s1 = new String("A" + "B" + "C" + "D");
System.out.println(s1);
And finally how about:
String s1 = "A";
String s2 = new String("A");
In the above example i think only 2 objects will be created
object 1 - "A"
object 2 - a String object that refers to the "A" object above.
Is this correct or will they not be related? i.e. the object referred to from the constant pool will be different from the one referred to by the s2 reference.
Thanks
Edit
Also, please note i am interested in knowing the total number of objects created including those that are discarded not just those that eventually end up in the constant pool.
Edit
Looking at Jon's answer i might have totally misunderstood the way the objects are created. I know that a String is created only once in the constant pool and it is reused but im not sure of the process that goes through when the 'final' string is constructed. Here is the section from the book i am reading which seems to suggest that temporary objects get created which is a complete opposite to the answers here. (Or maybe the book is wrong or i misunderstood the book)
The code sample was
String s1 = "spring ";
String s2 = s1 + "summer ";
s1.concat("fall ");
s2.concat(s1);
s1 += "winter";
System.out.println(s1 + " " + s2);
The question was
What is the output? For extra credit, how many String objects and how
many reference varibles were created prior to the println statement.
And the answer
The result of this code fragment is spring water spring summer. There
are two reference variables, s1 and s2. There were a total of eight
String objects created as follows "spring", "summer" (lost), "spring
summer", "falls"(lost), "spring fall" (lost), "spring summer spring"
(lost), "winter" (lost), "spring winter" (at this point "spring" is
lost). Only two of the eight String objects are not lost in this
process
Thanks
The compiler will concatenate the whole of "A" + "B" + "C" + "D" into a single constant - so in your first example, only a single string ends up created at all. That same string will be reused if you execute the same code multiple times. The constant is put in the class file, and when the class is loaded the VM checks whether an equal string is already in the string pool - so it will reuse it even if you have the same code in multiple classes.
You can verify that only a single string is in the constant pool within the class with javap:
javap -v Test
Constant pool:
#1 = Methodref #6.#17 // java/lang/Object."<init>":()V
#2 = String #18 // ABCD
#3 = Fieldref #19.#20 // java/lang/System.out:Ljava/io/PrintStream;
However, here:
String s1 = "A";
String s2 = new String("A");
you do end up with two separate string objects. One (the constant) will be reused every time you execute the code (and is shared between the two statements), and a new one will be created due to the constructor call each time.
So for example, this method:
public static void foo() {
for (int i = 0; i < 5; i++) {
String s1 = "A";
String s2 = new String("A");
}
}
... will end up using six string objects - one for the constant, and five new ones created each time you call the method.
How many objects created?
String s1 = "A" + "B" + "C" + "D";
System.out.println(s1)
One or none. This is reduced to one String literal which could be already loaded.
String s1 = new String("A" + "B" + "C" + "D");
System.out.println(s1);
This always creates an extra object.
BTW: A string usually consists of two objects, the String and the char[] it wraps.
String s1 = "A" + "B" + "C" + "D";
The compiler will create only one string literal "ABCD" and put it in the String pool. One object will be created (the one in the String Pool).
String s1 = new String("A" + "B" + "C" + "D");
Same here, except that you are copying it from the String literal. So, 2 object will be created here. One by the new and one in the String Pool.
String s1 = "A";
String s2 = new String("A");
Same here, "A" will be a constant in the string pool. The constructor will copy it. So here two objects will be created.

What's the difference between String s = "something"; and String s = new String("something"); [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
difference between string object and string literal
When initializing a String object there are at least two ways, like such:
String s = "some string";
String s = new String("some string");
What's the difference?
The Java language has special handling for strings; a string literal automatically becomes a String object.
So in the first case, you're initializing the reference s to that String object.
In the second case, you're creating a new String object, passing in a reference to the original String object as a constructor parameter. In other words, you're creating a copy. The reference s is then initialized to refer to that copy.
In first case you can take this string from pool if it exist there.
In second case you explicitly create new string object.
You can check this by these lines:
String s1 = "blahblah";
String s2 = "blahblah";
String s3 = new String("blahblah");
String s4 = s3.intern();
System.out.println(s1 == s2);
System.out.println(s1 == s3);
System.out.println(s2 == s3);
System.out.println(s1 == s4);
Output:
true
false
false
true
String s = "some string"; assigns that value to s from string pool (perm.gen.space) (creates one if it does not exist)
String s = new String("some string"); creates a new string with value given in constructor, memory allocated in heap
The first method is recommended as it will help to reuse the literal from string pool
Semantically, the first one assigns "some string" to s, while the second one assigns a copy of "some string" to s (since "some string" is already a String). I see no practical reasons to do this in 99.9% of cases, thus I would say that in most contexts, the only difference between the two lines is that:
The second line is longer.
The second line might consume more memory than the first one.
As #Anish-Dasappan mentions, the second one will have it's value in heap, whereas the first one will be in the string pool - I'm not sure this has any interest for the programmer, but I might be missing a clever trick there.

Categories