简体   繁体   English

java不可变对象问题

[英]java immutable object question

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. 通过更改“def”对象,我的abc对象也会被更改。 Beside String[] array has this characteristic what other java object has similar characteristic? String []数组旁边有哪些其他java对象具有相似特性的特性? can explain more? 可以解释一下吗? in order to prevent abc from changed when i changed def, i will have to do def = abc.clone(); 为了防止在我改变def时改变abc,我将不得不做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. 在这些图中, [var/index]是一个引用变量, {{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引用变量相同的对象:

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. 请注意, String[]是一个引用数组。 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. 请注意{{a String "abc"}}本身未发生变异。 [abc] and [def] now points to the same {{a String[1]}} , which is mutable (ie you can make the elements of the array, which are references to String objects, to point to anything). [abc][def]现在指向相同的{{a String[1]}} ,这是可变的(即你可以使数组的元素,即String对象的引用,指向任何东西)。


in order to prevent abc from changed when i changed def , i will have to do def = abc.clone() ; 为了防止在我改变def时改变abc ,我将不得不做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 . 让我们看看如果clone()一个对可变类型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 (ie def != abc ), that element is pointing to the same {{a StringBuilder}} object (ie def[0] == abc[0] ). 这里发生的事情是,即使clone()使用自己的元素(即def != abc )生成第二个{{a StringBuilder[1]}}对象,该元素也指向同一个{{a StringBuilder}}对象(即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 IntegerString等是不可变的
    • 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: 如果您想更深入地了解这些问题,我建议如下:

Immutable objects are objects that cannot be changed once created. 不可变对象是一旦创建就无法更改的对象。 String is an obvious example. String是一个明显的例子。 Arrays are mutable. 数组是可变的。 If you want an immutable collection, use a List instead: 如果您想要一个不可变的集合,请改用List

List<String> abc = Collections.unmodifiableList(
  Arrays.asList("abc")
);

Mutable objects have mutators . 可变对象有变异器 A mutator is any method that modifies the state of the object. mutator是修改对象状态的任何方法。 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. 一般来说,对于不可变对象,所有成员都是final和immutable。 Date is a good example of the issue above. Date是上述问题的一个很好的例子。 Date is not immutable, which many (myself included) consider a design mistake. Date不是一成不变的,许多人(包括我自己)认为这是设计错误。 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. 只是为了迂腐,没有“abc”对象或“def”对象。 There's the single String[] that abc, and then def happen to refer to. 有单个String [] abc,然后def碰巧引用。 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是一个类,那么,

Sample sam1 = new Sample();

will clearly be explained as sam1 being the reference to the object created. 将清楚地解释为sam1是对创建的对象的引用。 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. 只是声明sam2是Sample类型的引用变量,并且它没有指向Sample类的对象。 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. 所以改变def [0]也会改变abc [0]。

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. 在java中,无论数组的类型如何,您都可以随时更改数组中的元素。 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: 如果要将数据保存在数组结构中,请考虑单独创建数据副本以保护abc的初始值:

String abc[]={"abc"};
String def[];
def = Arrays.copyOf(abc, abc.length);

Alternatively, use cletus solution: 或者,使用cletus解决方案:

List abc = Collections.unmodifiableList(
  Arrays.asList("abc")
);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM