java不可变对象问题

com*_*tta 2 java

String abc[]={"abc"};
String def[]={};

def=abc;
def[0]=def[0]+"changed";
System.out.println(abc[0]);
Run Code Online (Sandbox Code Playgroud)

通过更改"def"对象,我的abc对象也会被更改.String []数组旁边有哪些其他java对象具有相似特性的特性?可以解释一下吗?为了防止在我改变def时改变abc,我将不得不做def = abc.clone();

pol*_*nts 13

您将对象可变性/不可变性与复制参考值混淆.

在这些图中,[var/index]是一个引用变量,并且{{an Object}}是一个对象.

String abc[]={"abc"};
String def[]={};

   [abc] ------> {{a String[1]}}
                 [0] --------------> {{a String "abc"}}

   [def] ------> {{a String[0]}}
Run Code Online (Sandbox Code Playgroud)

现在,您将def引用变量指向与引用变量相同的对象abc:

def=abc;

   [abc] ------> {{a String[1]}}
              /  [0] --------------> {{a String "abc"}}
             /
   [def] ---/    {{a String[0]}}
Run Code Online (Sandbox Code Playgroud)

此时,长度为零的数组未被引用,并且应该是可垃圾收集的.我们可以将讨论范围缩小到长度为1的数组.请注意,a String[]是引用数组.在下一行中,您更改了一个数组指向的长度中唯一的元素.

def[0]=def[0]+"changed";

   [abc] ------> {{a String[1]}}
              /  [0] ---------\      {{a String "abc"}}
             /                 \
   [def] ---/                   \--> {{a String "abcchanged"}}
Run Code Online (Sandbox Code Playgroud)

请注意,{{a String "abc"}}它本身没有变异.[abc][def]现在指向同一个{{a String[1]}},这是可变的(例如,你可以让数组的元素,这是引用String的对象,指向任何东西).


为了防止abc在我改变时改变def,我将不得不这样做def = abc.clone();

实际上,这不太准确.让我们看看如果您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!"
Run Code Online (Sandbox Code Playgroud)

这次我不会为你制作图表,但你可以很容易地在纸上画出来.这里发生的是,即使使用自己的元素(即)clone()创建第二个{{a StringBuilder[1]}}对象def != abc,该元素也指向同一个{{a StringBuilder}}对象(即def[0] == abc[0]).


简而言之:

  • 不变性意味着某种类型的物体不能以任何有意义的方式改变为外部观察者
    • Integer,String等是不可变的
    • 通常所有的值类型都应该是
  • 数组对象是可变的
    • 它可能是对不可变类型的引用数组,但数组本身是可变的
      • 意思是您可以将这些引用设置为您想要的任何内容
      • 对于基元数组也是如此
    • 不可变数组将不实用
  • 可以共享对象的引用
    • 如果对象是可变的,那么将通过所有这些引用看到变异

如果您想更深入地了解这些问题,我建议如下:


cle*_*tus 5

不可变对象是一旦创建就无法更改的对象.String是一个明显的例子.数组是可变的.如果您想要一个不可变的集合,请使用List:

List<String> abc = Collections.unmodifiableList(
  Arrays.asList("abc")
);
Run Code Online (Sandbox Code Playgroud)

可变对象有变异器.mutator是修改对象状态的任何方法.塞特斯是一个明显的例子.典型的不可变对象将如下所示:

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()); }
}
Run Code Online (Sandbox Code Playgroud)

一般来说,对于不可变对象,所有成员都是final不可变的.Date是上述问题的一个很好的例子.Date不是一成不变的,许多人(包括我自己)认为这是设计错误.由于它是可变的,你必须进行大量的防御性复制.


Wil*_*ung 5

只是为了迂腐,没有"abc"对象或"def"对象.有单个String [] abc,然后def碰巧引用.这就是为什么"两个对象"都改变了.事实上,他们指的是同一个对象.