Immutability of string objects in java - java

I'm noob in programming, now learning Java.
I have read that Strings are immutable in Java. But, I have a question regarding it.
public class Immutable {
public static void main (String[ ] args)
{
new Immutable().run();
} // method main
public void run()
{
String s = "yEs";
String p = "Do";
p=p.toUpperCase();
s=s.toLowerCase();
//Here above I'm able to change the content of the string object.
//Why is it said Immutable??
p = new String("DoPe");
System.out.println(p);
// Here I'm taking it as I'm creating new object and I'm assigning it to 'p'
//rather than changing the previously assigned object. so it's immutable
//Is my understanding correct??
flip (s);
System.out.println(s);
//Why does assigning a new object using a method doesn't work
//while if I do it explicitly, it works(as I've done for 'p', see above)
} // method run
public void flip (String t)
{
System.out.println(t);
t = new String ("no");
System.out.println(t);
t = "nope";
System.out.println(t);
} // method flip
} // class Immutable
Please see questions in my comments.
Revision:
public class Immutable {
public static void main (String[] args)
{
new Immutable().run();
} // method main
public void run()
{
String s = "yEs";
String p = "Do";
int [] arr = new int[5];
for (int i = 0; i < 5; i++)
arr[i] = i;
System.out.println("Value in the calling function before the altering is done: "+Arrays.toString(arr));
alter(arr);
System.out.println("Value in the calling function after the altering is done: "+Arrays.toString(arr));
p = p.toUpperCase();
s = s.toLowerCase();
//Here above I'm able to change the content of the string object.
//Why is it said Immutable??
p = new String("DoPe");
System.out.println(p);
// Here I'm taking it as I'm creating new object and I'm assigning it to 'p'
//rather than changing the previously assigned object. so it's immutable
//Is my understanding correct??
System.out.println("Value of string in the calling function before the altering is done: "+s);
flip (s);
System.out.println("Value of string in the calling function after the altering is done: "+s);
//Why does assigning a new object using a method doesn't work
//while if I do it explicitly work(as I've done for 'p', see above)
} // method run
public void flip (String t)
{
System.out.println("Value of string in the called function, before altering: "+t);
t = new String ("no");
System.out.println("Value of string in the called function, after Altering: "+t);
t = "nope";
System.out.println(t);
} // method flip
public void alter(int[] a)
{
System.out.println("Value in the called function, before altering: "+Arrays.toString(a));
a[3] = 50;
System.out.println("Value in the called function, after Altering: "+Arrays.toString(a));
}
} // class Immutable
Modification works for arrays, but not for strings.
Is this the reason strings are called immutable??
Am I missing something?

Here above I'm able to change the content of the string object. Why is it said Immutable??
You don't change the contents of the String, you create a new String object and link it with the variable, the old object will be removed.
Here I'm taking it as I'm creating new object and I'm assigning it to 'p' rather than changing the previously assigned object. so it's immutable. Is my understanding correct??
Yes, and this is actually the same you did one question above, but the String object was created by one of its instance methods.
Why does assigning a new object using a method doesn't work while if I do it explicitly work(as I've done for 'p', see above)
This is because the immutability of String. Java is pass-by-value, but your method needs to be pass-by-pointer. Since you can't change the contents of the String (you only can create a new String object), you'll need to return the String.

You're not changing the content of the string object, you're changing which variable point to which object in memory.
Variables are just pointers to the objects in memory, not the actual objects.

You're not changing the content of the string objects. You're creating new objects and making s and p point to the new objects.

//Here above I'm able to change the content of the string object.
//Why is it said Immutable??
You did not change the content of the string, you simply created a new String and assigned its reference to your String variable. That also answers to your second comment.
//Why does assigning a new object using a method doesn't work
//while if I do it explicitly work(as I've done for 'p', see above)
Because Java methods are not pass-by-reference. See this question for further information.

I believe you are making confusion about things.
Overall, I do understand what you mean.
You believe that you "changed" p by p = new String("DoPe"); which is let's say correct but the same thing doesn't happen with s when you call flip (s);
Well, calling it won't "change" s because your function flip only takes a string and modifies it internally and that's it.
In order to "change" s using a function you have to do this modifications:
1 s= flip (s);
(s receives the returned value of the function)
2. Function flip should return a String and not a void
public String flip (String t) // String instead of void
{
System.out.println(t);
t = new String ("no");
System.out.println(t);
t = "nope";
System.out.println(t);
return t; //returns the String t
}

Related

How can I make two object variables refer to each other?

I am trying to create an alias for a object variable in Java so that I can change one and have it affect the other. For example, in this snippet, I expect "b" to be printed out. Instead I get "a".
String s = new String("a");
String t = s;
t = new String("b");
System.out.println(s); // Prints "a".
I thought that non-primitive types in Java were able to be referenced and "tied" together like this.
No, it never works like that. Variables, fields etc. cannot be "bound" in Java.
Your variables can contain "a link to a value" in a variable, but you cannot have a "link to another variable".
This means that in your case s and t initially hold the link to the same value, but they don't know about each other. When you assign a new string to t, the value of the old string does not change (and s still references the old value). It's just the reference in `t
String is not good example, because this is immutable. You have to look at using object instances and object reference. One object instance could have multiple object references.
class Decorator {
private String str;
public Decorator(String str) {
this.str = str;
}
public void setStr(String str) {
this.str = str;
}
public String getStr() {
return str;
}
}
Decorator s = new Decorator("a"); // s - is a reference to Decorator
System.out.println(s.getStr()); // prints "a"
Decorator t = s; // t - is an another reference to existed Decorator (two references for one instance)
t.setStr("b");
// references s and t both point to one Decorator instance
System.out.println(s.getStr()); // prints "b"
System.out.println(t.getStr()); // prints "b"
You might be looking for something like this:
StringWrapper sw = new StringWrapper(new String("a"));
StringWrapper tw = sw;
tw.setS(new String("b"));
System.out.println(sw.getS()); // Prints b
class StringWrapper {
private String s;
public StringWrapper(String s) {
this.s = s;
}
private String getS() {
return s;
}
private void setS(String s) {
this.s = s;
}
}
(You can replace new StringWrapper(new String("a")) with new StringWrapper("a"). I added new String so it looks more like your example. Same goes for new String("b").)

what's the difference between the two initialization: Object x = new String(); String x = new String();

what's the difference between the two initialization:
Object x = new String();
String x = new String();
in java
thank!
Object x = new String(); // pointing to a String and saying - Hey, Look there! Its an Object
String x = new String();// pointing to a String and saying - Hey, Look there! Its a String
More importantly :
The methods of String that can be accessed depend on the reference. For example :
public static void main(String[] args) {
Object o = new String();
String s = new String();
o.split("\\."); // compile time error
s.split("\\."); // works fine
}
There's no difference in the initializations, only in the declarations and therefore in the way the rest of your code sees the variables type.
Object x = new String();
here, x has access only to the Object's methods and members.
(To use String members, you have to typecast the x to String (using Downcasting in Java))
String x = new String();
here, x has access to all the methods and members of Object as well as String.
The difference is that in the first option x will be considered by the compiler as an Object and in the second it'll be considered as a String. Example:
public static void main (String[] args) throws Exception {
Object x = new String();
test(x);
String y = new String();
test(y);
// and you can also "trick" the compiler by doing
test((String)x);
test((Object)y);
}
public static void test(String s) {
System.out.println("in string");
}
public static void test(Object o) {
System.out.println("in object");
}
will print:
in object
in string
in string
in object
Both are same, X will refer to the string object.
But x variable in Object x = new String(); need to be Type casted to String , using x.toString() or (String)x before making use of it.
As noted by the other answerers, both cases will result in the variable x holding a reference to a String. The only difference is how subsequent code will see the reference: as an Object vs String, i.e. determining which operations the compiler will allow on the reference.
Essentially, the question highlights the difference between a statically typed language (e.g. Java) and a dynamic one (e.g. Python, Javascript, etc.). In the latter you don't have to declare the type of the reference, so the code for this is simply something like:
var x = new String();
In this case, the compiler infers the type at runtime, but you lose some static type checking at compile time.
In everyday Java code you will always use the form:
String x = new String();
since that will allow you to treat x like a String and call, e.g. the method toUpperCase() on it. If x were an Object, the compiler won't allow you to call that method, only Object methods, e.g. equals().
However, the exact opposite is true in the following case:
List list = new ArrayList();
ArrayList list = new ArrayList();
Unless you specifically want to use methods exposed by ArrayList (unlikely), it is always better to declare list as a List, not ArrayList, since that will allow you to change the implementation to another type of List later, without changing the calling code.
Difference is you need to cast it a lot
however you can use the same variable and reinitialize it with another type. It is very usefull when you are working with changing variables..
Example:
Object x = new String();
if(x instanceof String){
System.out.println("String");
}
x = new ArrayList<String>();
if(x instanceof ArrayList){
System.out.println("ArrayList");
}
returns:
String
ArrayList

Passing objects to methods java

I'm using three objects: StringBuilder, Integer, and testobject - that are passed to a method to change its state.
As expected, the StringBuilder and testobject points to the same object and the state is change but it does not work for Integer object.
class testobject{
int x = 1;
}
public class test{
public static void main(String[] args){
StringBuilder s1 = new StringBuilder("String");
go(s1);
System.out.println(s1);
Integer s2 = new Integer("20");
go1(s2);
System.out.println(s2);
testobject s3 = new testobject();
go2(s3);
System.out.println(s3.x);
}
static void go(StringBuilder s1){
s1.append("Builder");
}
static void go1(Integer s2){
s2 = 1;
}
static void go2(testobject s3){
s3.x = 5;
}
Result:
StringBuilder
20
5
ExpectedResult:
StringBuilder
1
5
Look at your three methods:
static void go(StringBuilder s1){
s1.append("Builder");
}
static void go1(Integer s2){
s2 = 1;
}
static void go2(testobject s3){
s3.x = 5;
}
In go and go2, you're making a modification to the object that the parameter value refers to.
In go1, you're changing the value of the parameter variable itself. That's very different, and because Java always uses pass-by-value, that change isn't seen by the caller.
It's important to understand that objects aren't passed to the methods at all. Instead, references are. The value of s1, s2 and s3 are all references. If you think of the variables as like pieces of paper, each piece of paper has a house address on it, which was copied from a piece of paper declared in main.
The method bodies of go and go2 are like visiting the house whose address is on the piece of paper, and painting the front door. If you then visit the houses using the original pieces of paper, you still see the new colours on the front doors.
The method body of go1 is like scribbling out the address written on the piece of paper, and writing a new one on there instead. That doesn't make any change to a house, nor does it change the original piece of paper.
If the argument you want to pass is an object instead a of primitive type, then Java treats pass by object as pass by value, because Java supports a pass-by-value concept.
Here a and p are the same reference:
public class ObjPass {
int value;
public static void main(String[] args) {
ObjPass p = new ObjPass();
p.value = 5;
System.out.println("Before calling: " + p.value);
increment(p);
System.out.println("After calling: " + p.value);
}
public static void increment(ObjPass a){
a.value++;
}
}
Output:
Before calling: 5
After calling: 6

Java String immutable passed by reference/value

Hi Im studying for my scja exam and have a question about string passing by ref/value and how they are immutable. The following code outputs "abc abcfg".
What I want to know is why is this happening? Im not understanding what happens inside of method f. String is passed by value so surely it should change to "abcde" inside the method? Because if b+="fg" appends to the string why doesnt it work inside the method?
Thanks!
public class Test {
public static void main(String[] args){
String a =new String("abc");
String b = a;
f(b);
b+="fg"
System.out.println(a + " " + b);
}
public static void f(String b){
b+="de";
b=null;
}
}
The line b+="de"; in the void f(String b) functions creates a completely new object of String that does not affect the object in the main function.
So when we say String is immutable when mean any change on a String object will result in creating a completely new String object
public class Test {
public static void main(String[] args){
String a =new String("abc");
String b = a; // both a & b points to the same memory address
f(b); // has no effect
// b still has the value of "abc"
b+="fg" // a new String object that points to different location than "a"
System.out.println(a + " " + b); // should print "abc abcfg"
}
public static void f(String b){
b+="de"; // creates a new object that does not affect "b" variable in main
b=null;
}
}
In your method f() you are assigning a new String to the parameter b, but parameters are just like local variables, so assigning something to them has no effect on anything outside the method. That's why the string you passed in is unchanged after the method call.
This line of code:
b += "de";
Roughly translates to:
b = b + "de";
Which creates a new String and stores it in the local variable 'b'. The reference outside the method is not changed in anyway by this. There are a couple of key things to remember when trying to understand how/why this works this way.
In Java, all method arguments are passed by value.
Methods have a local stack where all their data is stored, including the method arguments.
When you assign a new object to a variable that was passed into a method, you are only replacing the address in the local stack, with the address of the new object created. The actual object address outside of the method remains the same.
When you pass the String into the method, it creates a new String object in the method. If you put a println statement in your method, like this:
public static void f(String b){
b+="de";
System.out.println(b);
b=null;
}
You would see the output "abcde".
However, once you come out of the method, you go back to your original object, which has not been altered. Therefore, appending fg to it will only produce a NEW string (b), containing "abcfg".
Your creating a new object of String when you say something like b+="de"
Its like saying b = new String(b+"de");
If you really want to keep the value passed in then use a stringBuilder like this:
StringBuilder a =new StringBuilder("abc");
StringBuilder b = a;
f(b);
b.append("fg");
System.out.println(a + " " + b);
}
public static void f(StringBuilder b){
b.append("de");
b=null;
}
Now your output will be "abcdefg" because stringbuilder object b was passed into f as the value of the object itself. I modified the value without using "new" to point to another object. Hope its clear now.

Passing a String by Reference in Java?

I am used to doing the following in C:
void main() {
String zText = "";
fillString(zText);
printf(zText);
}
void fillString(String zText) {
zText += "foo";
}
And the output is:
foo
However, in Java, this does not seem to work. I assume because the String object is copied instead of passed by referenced. I thought Strings were objects, which are always passed by reference.
What is going on here?
You have three options:
Use a StringBuilder:
StringBuilder zText = new StringBuilder ();
void fillString(StringBuilder zText) { zText.append ("foo"); }
Create a container class and pass an instance of the container to your method:
public class Container { public String data; }
void fillString(Container c) { c.data += "foo"; }
Create an array:
new String[] zText = new String[1];
zText[0] = "";
void fillString(String[] zText) { zText[0] += "foo"; }
From a performance point of view, the StringBuilder is usually the best option.
In Java nothing is passed by reference. Everything is passed by value. Object references are passed by value. Additionally Strings are immutable. So when you append to the passed String you just get a new String. You could use a return value, or pass a StringBuffer instead.
What is happening is that the reference is passed by value, i.e., a copy of the reference is passed. Nothing in java is passed by reference, and since a string is immutable, that assignment creates a new string object that the copy of the reference now points to. The original reference still points to the empty string.
This would be the same for any object, i.e., setting it to a new value in a method. The example below just makes what is going on more obvious, but concatenating a string is really the same thing.
void foo( object o )
{
o = new Object( ); // original reference still points to old value on the heap
}
java.lang.String is immutable.
I hate pasting URLs but https://docs.oracle.com/javase/10/docs/api/java/lang/String.html is essential for you to read and understand if you're in java-land.
All arguments in Java are passed by value. When you pass a String to a function, the value that's passed is a reference to a String object, but you can't modify that reference, and the underlying String object is immutable.
The assignment
zText += foo;
is equivalent to:
zText = new String(zText + "foo");
That is, it (locally) reassigns the parameter zText as a new reference, which points to a new memory location, in which is a new String that contains the original contents of zText with "foo" appended.
The original object is not modified, and the main() method's local variable zText still points to the original (empty) string.
class StringFiller {
static void fillString(String zText) {
zText += "foo";
System.out.println("Local value: " + zText);
}
public static void main(String[] args) {
String zText = "";
System.out.println("Original value: " + zText);
fillString(zText);
System.out.println("Final value: " + zText);
}
}
prints:
Original value:
Local value: foo
Final value:
If you want to modify the string, you can as noted use StringBuilder or else some container (an array or an AtomicReference or a custom container class) that gives you an additional level of pointer indirection. Alternatively, just return the new value and assign it:
class StringFiller2 {
static String fillString(String zText) {
zText += "foo";
System.out.println("Local value: " + zText);
return zText;
}
public static void main(String[] args) {
String zText = "";
System.out.println("Original value: " + zText);
zText = fillString(zText);
System.out.println("Final value: " + zText);
}
}
prints:
Original value:
Local value: foo
Final value: foo
This is probably the most Java-like solution in the general case -- see the Effective Java item "Favor immutability."
As noted, though, StringBuilder will often give you better performance -- if you have a lot of appending to do, particularly inside a loop, use StringBuilder.
But try to pass around immutable Strings rather than mutable StringBuilders if you can -- your code will be easier to read and more maintainable. Consider making your parameters final, and configuring your IDE to warn you when you reassign a method parameter to a new value.
objects are passed by reference, primitives are passed by value.
String is not a primitive, it is an object, and it is a special case of object.
This is for memory-saving purpose. In JVM, there is a string pool. For every string created, JVM will try to see if the same string exist in the string pool, and point to it if there were already one.
public class TestString
{
private static String a = "hello world";
private static String b = "hello world";
private static String c = "hello " + "world";
private static String d = new String("hello world");
private static Object o1 = new Object();
private static Object o2 = new Object();
public static void main(String[] args)
{
System.out.println("a==b:"+(a == b));
System.out.println("a==c:"+(a == c));
System.out.println("a==d:"+(a == d));
System.out.println("a.equals(d):"+(a.equals(d)));
System.out.println("o1==o2:"+(o1 == o2));
passString(a);
passString(d);
}
public static void passString(String s)
{
System.out.println("passString:"+(a == s));
}
}
/* OUTPUT */
a==b:true
a==c:true
a==d:false
a.equals(d):true
o1==o2:false
passString:true
passString:false
the == is checking for memory address (reference), and the .equals is checking for contents (value)
String is an immutable object in Java. You can use the StringBuilder class to do the job you're trying to accomplish, as follows:
public static void main(String[] args)
{
StringBuilder sb = new StringBuilder("hello, world!");
System.out.println(sb);
foo(sb);
System.out.println(sb);
}
public static void foo(StringBuilder str)
{
str.delete(0, str.length());
str.append("String has been modified");
}
Another option is to create a class with a String as a scope variable (highly discouraged) as follows:
class MyString
{
public String value;
}
public static void main(String[] args)
{
MyString ms = new MyString();
ms.value = "Hello, World!";
}
public static void foo(MyString str)
{
str.value = "String has been modified";
}
The answer is simple. In java strings are immutable. Hence its like using 'final' modifier (or 'const' in C/C++). So, once assigned, you cannot change it like the way you did.
You can change which value to which a string points, but you can NOT change the actual value to which this string is currently pointing.
Ie. String s1 = "hey". You can make s1 = "woah", and that's totally ok, but you can't actually change the underlying value of the string (in this case: "hey") to be something else once its assigned using plusEquals, etc. (ie. s1 += " whatup != "hey whatup").
To do that, use the StringBuilder or StringBuffer classes or other mutable containers, then just call .toString() to convert the object back to a string.
note: Strings are often used as hash keys hence that's part of the reason why they are immutable.
String is a special class in Java. It is Thread Safe which means "Once a String instance is created, the content of the String instance will never changed ".
Here is what is going on for
zText += "foo";
First, Java compiler will get the value of zText String instance, then create a new String instance whose value is zText appending "foo". So you know why the instance that zText point to does not changed. It is totally a new instance. In fact, even String "foo" is a new String instance. So, for this statement, Java will create two String instance, one is "foo", another is the value of zText append "foo".
The rule is simple: The value of String instance will never be changed.
For method fillString, you can use a StringBuffer as parameter, or you can change it like this:
String fillString(String zText) {
return zText += "foo";
}
Strings are immutable in Java.
This works use StringBuffer
public class test {
public static void main(String[] args) {
StringBuffer zText = new StringBuffer("");
fillString(zText);
System.out.println(zText.toString());
}
static void fillString(StringBuffer zText) {
zText .append("foo");
}
}
Even better use StringBuilder
public class test {
public static void main(String[] args) {
StringBuilder zText = new StringBuilder("");
fillString(zText);
System.out.println(zText.toString());
}
static void fillString(StringBuilder zText) {
zText .append("foo");
}
}
String is immutable in java. you cannot modify/change, an existing string literal/object.
String s="Hello";
s=s+"hi";
Here the previous reference s is replaced by the new refernce s pointing to value "HelloHi".
However, for bringing mutability we have StringBuilder and StringBuffer.
StringBuilder s=new StringBuilder();
s.append("Hi");
this appends the new value "Hi" to the same refernce s.
//
Aaron Digulla has the best answer so far. A variation of his second option is to use the wrapper or container class MutableObject of the commons lang library version 3+:
void fillString(MutableObject<String> c) { c.setValue(c.getValue() + "foo"); }
you save the declaration of the container class. The drawback is a dependency to the commons lang lib. But the lib has quite a lot of useful function and almost any larger project i have worked on used it.
For passing an object (including String) by reference in java, you might pass it as member of a surrounding adapter. A solution with a generic is here:
import java.io.Serializable;
public class ByRef<T extends Object> implements Serializable
{
private static final long serialVersionUID = 6310102145974374589L;
T v;
public ByRef(T v)
{
this.v = v;
}
public ByRef()
{
v = null;
}
public void set(T nv)
{
v = nv;
}
public T get()
{
return v;
}
// ------------------------------------------------------------------
static void fillString(ByRef<String> zText)
{
zText.set(zText.get() + "foo");
}
public static void main(String args[])
{
final ByRef<String> zText = new ByRef<String>(new String(""));
fillString(zText);
System.out.println(zText.get());
}
}
For someone who are more curious
class Testt {
static void Display(String s , String varname){
System.out.println(varname + " variable data = "+ s + " :: address hashmap = " + s.hashCode());
}
static void changeto(String s , String t){
System.out.println("entered function");
Display(s , "s");
s = t ;
Display(s,"s");
System.out.println("exiting function");
}
public static void main(String args[]){
String s = "hi" ;
Display(s,"s");
changeto(s,"bye");
Display(s,"s");
}
}
Now by running this above code you can see how address hashcodes change with String variable s .
a new object is allocated to variable s in function changeto when s is changed

Categories