views:

392

answers:

5
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();

+5  A: 

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.

cletus
+1 Note that while an immutable List itself cannot be changed, the individual members do not become immutable and can be modified (unless they are already immutable, such as Strings in this example). E.g. an immutable List of Date objects is not really all that immutable.
Thilo
+1 for defensively copying `Date`, as well as explaining why it's stupid to even have to in the first place. :-)
Chris Jester-Young
+4  A: 

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.

Will Hartung
+5  A: 

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:

polygenelubricants
@polygenelubricants thank you for depth explanation. awesome
cometta
+1  A: 

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.
abson
+1  A: 

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")
);
matsev