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

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.

Related

How + internally works on Strings in JAVA

I read from the blogs that internally java use StringBuilder to concat the String when we use + operator. I was just checking it and found some strange outputs.
public class StringDemo {
public static void main(String[] args) {
String a = "Hello World";
String b = "Hello World";
String c = "Hello";
String d = c + " World".intern();
String e = new StringBuilder().append(String.valueOf(c)).append(" World").toString().intern() ;
String f = new StringBuilder(String.valueOf(c)).append(" World").toString().intern();
System.out.println(a == b); // Line 1 Expected output true
System.out.println(a == d); // Line 2 Output is false
System.out.println(a == e); // Line 3 Output is true
System.out.println(a == f); // Line 4 Output is true
}
}
So i am using + operator to concat two strings c & " World" and then use intern() method to move String in the pool for String d.
As per my understanding java use StringBuilder, so now I use StringBuilder to concat the String and use intern() method for Strings e and f.
So if both the equivalent then address of both the String must be same but the output of Line 2 not matching with Line 4 & 5.
Thanks in advance for your valuable feedback.
How + internally works in JAVA
Here is my post on the same, give a read Compiler version : How String concatenation works in java.
And coming to your code inside
System.out.println(a == d);
That should be false only.
As per your understanding you are expecting true. No. Your understanding is wrong. There is a clear difference between
String d = c + " World".intern();
And
String d = (c + " World").intern();
In first line only "World" got interned and the second line "Hello World" got interned
When you do (c + " World").intern(), you'll see the output true.

How are chained assignments in java defined? Is there a difference between value and reference types?

How are chained assignments in java defined, considering following points:
Is there a difference between chained assignment and chained declaration?
Is there a way for reference types to repeat the statement instead of passing the reference?
E.g.
Integer a, b = new Integer(4);
In JLS 15.26 Assignment Operators it says
At run time, the result of the assignment expression is the value of the variable after the assignment has occurred. The result of an assignment expression is not itself a variable.
So a == b should be true.
Is there a way to achieve
Integer a = new Integer(4)
Integer b = new Integer(4)
in one line so that
a != b, since a and b are different objects.
Additional Info
The question is already answered, but I felt it was not clear enough, so here some code
to clarify it.
Integer a = null, b = null, c = null;
System.out.println(a + " " + b + " " + c); // null null null
a = b = c = new Integer(5); // <-- chained assignment
System.out.println(a + " " + b + " " + c); // 5 5 5
System.out.println(a.equals(b)); // true
System.out.println(b.equals(c)); // true
System.out.println(a == b); // true
System.out.println(b == c); // true
Sure:
Integer a = new Integer(4), b = new Integer(4);
Personally I think that's less readable than using two separate declarations though, and there's no way of doing it without either repeating the new Integer(4) or extracting that to some other method which you then call twice.

Java: println with char array gives gibberish

Here's the problem. This code:
String a = "0000";
System.out.println(a);
char[] b = a.toCharArray();
System.out.println(b);
returns
0000
0000
But this code:
String a = "0000";
System.out.println("String a: " + a);
char[] b = a.toCharArray();
System.out.println("char[] b: " + b);
returns
String a: 0000
char[] b: [C#56e5b723
What in the world is going on? Seems there should be a simple enough solution, but I can't seem to figure it out.
When you say
System.out.println(b);
It results in a call to print(char[] s) then println()
The JavaDoc for print(char[] s) says:
Print an array of characters. The characters are converted into bytes
according to the platform's default character encoding, and these
bytes are written in exactly the manner of the write(int) method.
So it performs a byte-by-byte print out.
When you say
System.out.println("char[] b: " + b);
It results in a call to print(String), and so what you're actually doing is appending to a String an Object which invokes toString() on the Object -- this, as with all Object by default, and in the case of an Array, prints the value of the reference (the memory address).
You could do:
System.out.println("char[] b: " + new String(b));
Note that this is "wrong" in the sense that you're not paying any mind to encoding and are using the system default. Learn about encoding sooner rather than later.
Use
System.out.println("char[] b: " + Arrays.toString(b));
The gibrish you get is the Class name followed by the memory address of the object. Problem occurs when you try to append b with a string char[] b: in this case the char array b.toString() method is called thus [C#56e5b723 is printed.
[ indicates that it is an array
C indicates the class in this case char
#56e5b723 indicates the memory location
System.out.println("char[] b: " + b);
This is just like
System.out.println(("char[] b: " + b.toString());
You can look up "Object.toString()"
An array's toString() method (which is what's called when you do "..." + b) is only meant to give debugging output. There isn't a special case where a char[]'s toString() will give you the original string - arrays of all types have the same toString() implementation.
If you want to get the original string from the char array, use:
String a2 = new String(b);
Use
3:e row!
Scanner input = new Scanner(System.in);
char[] txt = input.next().toCharArray();
System.out.println((char[])txt);
private void print(char[] arr) {
try {
PrintStream stream
= new PrintStream(System.out);
stream.println(arr);
stream.flush();
} catch (Exception e) {
e.printStackTrace();
}
}

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.

Java String variable setting - reference or value?

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!

Categories