java immutable object question - java

String abc[]={"abc"};
String def[]={};
def=abc;
def[0]=def[0]+"changed";
System.out.println(abc[0]);
by changing "def" object, my abc object is changed as well. Beside String[] array has this characteristic what other java object has similar characteristic? can explain more? in order to prevent abc from changed when i changed def, i will have to do def = abc.clone();

You are confusing object mutability/immutability with copying of reference values.
In these diagrams, [var/index] is a reference variable, and {{an Object}} is an object.
String abc[]={"abc"};
String def[]={};
[abc] ------> {{a String[1]}}
[0] --------------> {{a String "abc"}}
[def] ------> {{a String[0]}}
Now you make def reference variable points to the same object as abc reference variable:
def=abc;
[abc] ------> {{a String[1]}}
/ [0] --------------> {{a String "abc"}}
/
[def] ---/ {{a String[0]}}
At this point, the array of length zero is unreferenced, and should be garbage-collectable. We can narrow our discussion to the array of length one. Note that a String[] is an array of references. With this next line, you changed what the only element in the length one array points to.
def[0]=def[0]+"changed";
[abc] ------> {{a String[1]}}
/ [0] ---------\ {{a String "abc"}}
/ \
[def] ---/ \--> {{a String "abcchanged"}}
Note that {{a String "abc"}} itself was not mutated. [abc] and [def] now points to the same {{a String[1]}}, which is mutable (i.e. you can make the elements of the array, which are references to String objects, to point to anything).
in order to prevent abc from changed when i changed def, i will have to do def = abc.clone();
Actually, that's not quite accurate. Let's see what happens if you clone() an array of references to a mutable type StringBuilder.
StringBuilder[] abc = new StringBuilder[] { new StringBuilder("Hello") };
StringBuilder[] def = abc.clone();
def[0].append(" world!");
System.out.println(abc[0]); // prints "Hello world!"
I won't make the diagrams for you this time, but you can easily draw it out on paper. What's happening here is that even though clone() makes a second {{a StringBuilder[1]}} object with its own element (i.e. def != abc), that element is pointing to the same {{a StringBuilder}} object (i.e. def[0] == abc[0]).
In short:
Immutability means that objects of a certain type can not change in any meaningful way to outside observers
Integer, String, etc are immutable
Generally all value types should be
Array objects are mutable
It may be an array of references to immutable types, but the array itself is mutable
Meaning you can set those references to anything you want
Also true for array of primitives
An immutable array will not be practical
References to objects can be shared
If the object is mutable, mutation will be seen through all these references
If you want more in-depth understanding of the issues, I recommend the following:
Is Java pass by reference? -- NO!
Are Java function parameters always passed-by-value? -- YES!

Immutable objects are objects that cannot be changed once created. String is an obvious example. Arrays are mutable. If you want an immutable collection, use a List instead:
List<String> abc = Collections.unmodifiableList(
Arrays.asList("abc")
);
Mutable objects have mutators. A mutator is any method that modifies the state of the object. Setters are an obvious example. A typical immutable object will look like this:
public class Person {
private final String firstName;
private final String lastName;
private final Date dateOfBirth;
public Person(String firstName, String lastName, Date dateOfBirth) {
this.firstName = firstName;
this.lastName = lastName;
this.dateOfBirth = new Date(dateOfBirth.getTime());
}
public String getFirstName() { return firstName; }
public String getLastname() { return lastName; }
public Date getDateOfBirth() { return new Date(dateOfBirth.getTime()); }
}
Generally speaking, for immutable objects, all members are final and immutable. Date is a good example of the issue above. Date is not immutable, which many (myself included) consider a design mistake. As a result of it being mutable you have to do lots of defensive copying.

just to be pedantic, there's no "abc" object or "def" object. There's the single String[] that abc, and then def happen to refer to. That's why "both objects" changed. They were, in fact, referring to the same object.

In simple terms it is like this :-
Lets assume Sample to be a class then,
Sample sam1 = new Sample();
will clearly be explained as sam1 being the reference to the object created.
but
Sample sam2;
just declares sam2 to be a reference variable of Sample type and has no object of Sample class being pointed by it.
now if we do this operation
sam2 = sam1;
then it means both the reference variables point to the same object and now one can refer to that object using any one of the two references.
Obviously one can manipulate the fields employing the valid methods using either of the references.
And this is what has been done here too.
String abc[]={"abc"};
String def[]={};
def=abc;
def[0]=def[0]+"changed";
and so changing def[0] changes abc[0] too.
Now when you clone you are creating a clone of the existent object.
The clone and the cloned objects independently exist
as 2 different objects and so the result of manipulations on one
is not reflected as you stated.

In java, you can always change the elements in an array, regardless of the type of the array. Consider making a separate copy of the data in order to protect the initial value of abc if you would like to keep the data in an array structure:
String abc[]={"abc"};
String def[];
def = Arrays.copyOf(abc, abc.length);
Alternatively, use cletus solution:
List abc = Collections.unmodifiableList(
Arrays.asList("abc")
);

Related

Pass-by-value (StringBuilder vs String) [duplicate]

This question already has answers here:
Is Java "pass-by-reference" or "pass-by-value"?
(93 answers)
Closed 7 years ago.
I do not understand why System.out.println(name) outputs Sam without being affected by the method's concat function, while System.out.println(names) outputs Sam4 as a result of the method's append method. Why is StringBuilder affected and not String? Normally, calling methods on a reference to an object affects the caller, so I do not understand why the String result remains unchanged. Thanks in advance
public static String speak(String name) {
name = name.concat("4");
return name;
}
public static StringBuilder test(StringBuilder names) {
names = names.append("4");
return names;
}
public static void main(String[] args) {
String name = "Sam";
speak(name);
System.out.println(name); //Sam
StringBuilder names = new StringBuilder("Sam");
test(names);
System.out.println(names); //Sam4
}
Because when you call speak(name);, inside speak when you do
name = name.concat("4");
it creates a new object because Strings are immutable. When you change the original string it creates a new object,I agree that you are returning it but you are not catching it.
So essentially what you are doing is :
name(new) = name(original) + '4'; // but you should notice that both the names are different objects.
try
String name = "Sam";
name = speak(name);
Of course now I think there is no need to explain why it's working with StringBuilder unless if you don't know that StringBuilder is mutable.
Looking at the Javadoc for String, one will read that
[...] String objects are immutable [...].
This means concat(String) does not change the String itself, but constructs a new String.
StringBuilders, on the other hand, are mutable. By calling append(CharSequence), the object itself is mutated.
Because String is immutable and hence String#concat does not modify the original String instance, it only returns a new String while the original is left unmodified, while StringBuilder is mutable and the change is reflected in the StringBuilder instance passed as parameter.
Okay, what is speak method doing?
First of all,
name.concat("4");
creates new object, which is equal to name, concatenated with "4".
So, the line
name = name.concat(4);
redefines local (for speak method) variable name.
Then you return the reference to this new value with
return name;
So, the original variable, passed within method is not modified, but the method returns modified value.
In the test method you actually modify variable without modifying the reference (the StringBuilder class is mutable, so variable if this type can be modified).
Then we can see another question arising: why StringBuilder.append returns value, where it can seem redundant. The answer to this question lies in the description of "builder" pattern, for which it is the usual way of implementing modification methods. See wikipedia on Builder pattern.
String is immutable in java. As soon as you invoke concat method on name. A new string is created and while you are playing with the old reference in System.out.println(name).If you want to use the modified string you should explicitly return the reference.
While StringBuilder is mutable and it returns the same reference always.
When you invoke speak(name) it computes the new value, but discards it.
If you replace it with
name = speak(name);
the result will be the one you expect.
With the StringBuilder, the object you pass is mutable: so
names.append(names);
changes the state of the current object (it also returns a reference to the same object, which is just a convenience to allow you to write code like names.append(...).append(...) etc.). So in the case of the StringBuilder, the object you are referencing when you call the method has actually changed, hence you see the changes.
In your method speak, the concat method returns a new String, the original object it was called on is unchanged (strings are immutable). As documented:
If the length of the argument string is 0, then this String object is returned. Otherwise, a String object is returned that represents 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.
Calling name.concat("4") is the equivalent of name + "4".
In your test method the append method modifies the content of the StringBuilder. As documented:
The principal operations on a StringBuilder are the append and insert methods, which are overloaded so as to accept data of any type. Each effectively converts a given datum to a string and then appends or inserts the characters of that string to the string builder. The append method always adds these characters at the end of the builder; the insert method adds the characters at a specified point.
In your main method both name and names are still the same object as before the method call, but the content of name is unchanged as strings are immutable, while the content of names has been changed.
If instead you had used the return values of both methods, then you would have the result you were expecting.
First of all, String is an immutable class in Java. An immutable class is simply a class whose instances cannot be modified. All information in an instance is initialized when the instance is created and the information can not be modified.
Second, in java parameters are sent by values and not by reference.
In your method 'test' you don't need names = names.append("4"), instead names.append("4") will be enough .
If you check java docs for String object, you will see that most of the methods there, including concat, will generate a new String.
So to have on output Sam4 also for the String, you will need in main method to have this name = speak(name).
String
String is immutable ( once created can not be changed )object . The
object created as a String is stored in the Constant String Pool .
Every immutable object in Java is thread safe ,that implies String is
also thread safe . String can not be used by two threads
simultaneously. String once assigned can not be changed.
String demo = " hello " ; // The above object is stored in constant
string pool and its value can not be modified.
demo="Bye" ; //new "Bye" string is created in constant pool and
referenced by the demo variable // "hello" string still
exists in string constant pool and its value is not overrided but we
lost reference to the "hello"string
StringBuilder
StringBuilder is same as the StringBuffer , that is it stores the
object in heap and it can also be modified . The main difference
between the StringBuffer and StringBuilder is that StringBuilder is
also not thread safe. StringBuilder is fast as it is not thread safe
.
For more details check this
Conclusion:
You don't need to re-assign the value again to StringBuilder as it is already a reference
test method should be
public static void test(StringBuilder names) {
names.append("4");
}
but speak should be
String name = "Sam";
name = speak(name);

ArrayList modifying value returned by "get" method [duplicate]

This question already has answers here:
Is Java "pass-by-reference" or "pass-by-value"?
(93 answers)
Closed 6 years ago.
I have below two situations related to ArrayList get method, one with custom class and one with String class:
1. Below is the example of modifying Custom class ArrayList element:
ArrayList<MyClass> mTmpArray1 = new ArrayList<MyClass>();
MyClass myObj1 = new MyClass(10);
mTmpArray1.add(myObj1);
MyClass myObj2 = mTmpArray1.get(0);
myObj2.myInt = 20;
MyClass myObj3 = mTmpArray1.get(0);
Log.d(TAG, "Int Value:"+myObj3.myInt); // Prints "20"
2. And below is the example of modifying String ArrayList element:
ArrayList<String> mTmpArray2 = new ArrayList<String>();
mTmpArray2.add("Test_10");
String myStr1 = mTmpArray2.get(0);
myStr1 = "Test_20";
String myStr2 = mTmpArray2.get(0);
Log.d(TAG, "Str Value:"+myStr2); // Prints "Test_10"
So in case of MyClass ArrayList, when I call get and modify the value, then I see change is reflecting when I do get again.
But same way when I modify String ArrayList, then changes are not reflecting.
What is the different in of the get method in both the scenarios?
Is it that in case of String, String class creating deep copy and returns new object, and in case of Custom class shallow copy is created?
In the first scenario applicable to "LinkedHashMap", "HashMap" and "List"?
Your are not doing the same thing in the two cases.
Here you update the state of an object, so the change affects the object stored in the list :
myObj2.myInt = 20;
Here you are assigning a new object to a local variable, so the list is not affected :
myStr1 = "Test_20";
If String was mutable, you could have modified the String by calling some method, and the change would have been reflected in the object stored in the list :
myStr1.setSomething(...);
On the other hand, if in the first case you would have changed the value of the local variable, the object stored in the list wouldn't have been affected :
myObj2 = new MyClass (...);
Strings are immutable. You're not inserting the new string into the array list.
When you do String myStr2 = mTmpArray2.get(0);, even tho you are pointing to a reference in the ArrayList, any attempt to change the value, will (because of String immutability) create a new String (myStr2) that will not reference the ArrayList anymore.
When you do myStr1 = "xxx", you're not actually changing the ArrayList reference, you're changing a new (copy) (now called myStr1) that was grabbed from the ArrayList and it has a local scope.
Read some more about Strings: Immutability of Strings in Java
Now in the first example, you are pointing to a mutable object (your custom class) so you're literally changing the direct value, through the reference. Welcome to Java. ;)
Unrelated: This code: MyClass myObj1 = new MyClass(10); is (arguably) considered bad. It's better to use a factory pattern that is a lot easier to read. In other words, public constructors with parameters are hard to read (for example, I have no idea what I am constructing when I read your code).
A (perhaps) better approach would be: MyClass myObj = MyClass.createInstanceWithCapacity(10); // i've invented the name because I don't know what your 10 is, but look at both, which one do you think is easier to understand upon first glance?
Disclaimer: The above unrelated comment is my personal opinion and not all developers will agree. ;)
Strings have very nice property called "Immutablity"
This means that String cannot be mutable (changed), when we create/
try to refer to old string, a new instance string is created. And any
changes we do are saved in new instance and it do not affect the old
string
For example,
String s = "Old String";
System.out.println("Old String : "+s); // output : Old String
String s2 = s;
s2 = s2.concat(" made New");
System.out.println("New String : "+s2); // output : Old String made New
System.out.println("Old String is not changed : "+s); // output : Old String
These is no difference between the two "get" calls. The difference is between the types that the ArrayList is holding, and what you're doing the references the "get" method returns.
In your first example, you do this:
MyClass myObj2 = mTmpArray1.get(0);
myObj2.myInt = 20;
Here, you're getting a reference to the MyClass instance in the ArrayList in position 0, and you are modifying a field within this instance.
In your second example, you do this:
String myStr1 = mTmpArray2.get(0);
myStr1 = "Test_20";
Here, you're getting a reference to the String instance in the array list, and then you're giving myStr1 a reference to a different string which you create ("Test_20"). It's as if you did wrote myObj2 = new MyClass(20); in the 2nd line in the 1st example.
So, in short, in the 1st example, you access a field within the object by altering it from the reference you grabbed. In the 2nd example, you simply altered your reference to point at a different String.
I should also mention that in Java, Strings are immutable, meaning once they have been created, they cannot be changed.
String is an immutable class. A line like
myStr1 = "Test_20";
does not change the value of the String object myStr1 is pointing to. Instead a new String is created and myStr1 is modified to point to the new String. The original String is unchanged and can be retrieved from the ArrayList.
Your MyClass object is clearly mutable. Only one instance is created and its state is changed by the assignment
myObj2.myInt = 20;
Hence when this object is retrieved from the ArrayList, its new state is seen.
You simply do NOT change the list in your 2nd example.
In the first example, you are doing this:
Get the first object from the list and store it in the variable called 'myObj2'
Modify the object stored in variable 'myObj2' by setting the int value of this object
But your second example is completely different:
String myStr1 = mTmpArray2.get(0);
myStr1 = "Test_20";
Let me translate that:
Get the first element from the list and store it in the variable called 'myStr1'.
Set the value of the variable 'myStr1' to "Test_20"
So, in case one you are modifying a variable of the object stored in the list. In case two you are retrieving the object stored in the list - and then re-use the variable you stored that retrieved object in and use it for something new - but that does not change the original list, of course.
To modify your list for a type like string, you would need to use set(x, "Test_20").

Returning a reference to a mutable object value stored in one of the object's fields exposes the internal representation of the object

I'm getting this error when running checkstyle on my code for the following lines:
#Override
public String[] getDescriptions() {
return DESCRIPTIONS;
}
but DESCRIPTIONS IS NOT mutable. It's defined as:
private static final String[] DESCRIPTIONS = new String[NUM_COLUMNS];
static {
// In a loop assign values to the array.
for (int i = 0; i < NUM_COLUMNS; ++i) {
DESCRIPTIONS[i] = "Some value";
}
}
This is the complete error message:
"Returning a reference to a mutable object value stored in one
of the object's fields exposes the internal representation of
the object. If instances are accessed by untrusted code, and
unchecked changes to the mutable object would compromise security
or other important properties, you will need to do something
different. Returning a new copy of the object is better approach
in many situations."
Related Question: Link
Arrays and some collections are not immutable in the sense that their content still remains mutable.
Immutability in Java only concerns object's reference assignment, not its deep content.
Try this:
#Override
public String[] getDescriptions() {
return Arrays.copyOf(DESCRIPTIONS, DESCRIPTIONS.length);
}
BTW, caution to java naming convention.. : descriptions, not DESCRIPTIONS
The reference variable is final so you cannot assign another array to DESCRIPTIONS. However, the object itself is mutable (arrays are always mutable), final or not. If you return the reference, then you lose control over the contents your variable, violating encapsulation.
You would need either to return a copy of the array, or don't return the array at all, providing a method to get a specific element of the array instead, if that's good enough.

Does it make a copy before adding Strings into a Collection?

List<String> list = new ArrayList<String>();
String string = null;
string = "123";
list.add(string);
string = "456";
list.add(string);
for (String s : list)
{
System.out.println(s);
}
This program outputs:
123
456
which is pretty natural.
However, I'm thinking in another way. "string" is the reference(pointer) to the actual String object. When executing add(), it just stores the reference. When "string" refers to another String object, why the list still keeps the original one? Does it make a copy before add()?
The "value" of a String variable is a reference to the (immutable) object that is the string.
So there is no copy of the String but a copy of the reference. Having this reference doesn't allow you to change the original variable (you have no link to it) and doesn't allow you to change the string as it is immutable.
What you have here, inside the array contained by the arrayList after the two calls to add, is two different references. They could point to the same string but changing one reference doesn't change the other one. If you wanted to change the first reference in this case to point to the same string as the second one, the simplest would have been to do list.set(0, list.get(1));
When "string" refers to another String object, why the list still keeps the original one?
What you've added to the list is the reference to the string. Later, when you do
string = "456";
you're not changing the existing string, you're assigning a reference to a different string to the string variable. The original string is unchanged (in fact, strings in Java are immutable).
because the variable 'string' is nothing but a pointer to a address in the memory. when you add 'string' in list the memory address of the 'string' is entered in the list. when you again assign a value , it justs change the address in the memory where the new variable is pointing

Pass by Value in Java

import java.util.Arrays;
public class Test {
public static void main(String... args) {
String[] strings = new String[] { "foo", "bar" };
changeReference(strings);
System.out.println(Arrays.toString(strings)); // still [foo, bar]
changeValue(strings);
System.out.println(Arrays.toString(strings)); // [foo, foo]
}
public static void changeReference(String[] strings) {
strings = new String[] { "foo", "foo" };
}
public static void changeValue(String[] strings) {
strings[1] = "foo";
}
}
Can anyone explain these questions?
What is Strings[]. Is it a String Object or String Object containing array of Objects.
What does the changeReference() and changeValue() functions do and return?
Does Java support Pass by Reference?
strings is an array of Strings. Arrays are objects for our purposes here, which means they are a reference type.
changeReference does nothing useful. It receives a reference to strings, but it receives that reference by value. Reassigning strings within the function has no effect on the strings being passed in -- it just replaces the local copy of the reference, with a reference to a new array. changeValue, on the other hand, modifies the array object referred to by strings. Since it's a reference type, the variable refers to the same object.
No, "pass by reference" is not supported. Java can pass references around, but it passes them by value. Summary being, you can change the object being passed in, but you can't replace the object in such a way that the caller will see it.
What is Strings[]. Is it a String Object or String Object containing array of Objects.
It’s neither. It’s an object (well, actually it’s a type) that references an array of strings.
What does the chanfeReference and changeValue function do and return?
Please try it yourself to see the effect.
Does Java support Pass by Reference?
No. Java is always pass by value.
What is String[]. Is it a String
Object or String Object containing
array of Objects.
String[] is an array of String (and String is an Object).
What does the changeReference and
changeValue function do and return?
In changeReference() java changes the reference of strings to an new string array. In changeValue(), java changes the value of the first element of strings array.
Does Java support Pass by Reference?
Java supports Pass by Value. As stated on JavaWorld:
Java does manipulate objects by
reference, and all object variables
are references. However, Java doesn't
pass method arguments by reference; it
passes them by value.
String[] is an array of String objects
changeReference changes the refence to the array strings to a new refence to a new array which, in this case, contains the same thing, but the reference in the memory is in another place.
Pass by Reference is not supported in Java

Categories