簡體   English   中英

java不可變對象問題

[英]java immutable object question

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

def=abc;
def[0]=def[0]+"changed";
System.out.println(abc[0]);

通過更改“def”對象,我的abc對象也會被更改。 String []數組旁邊有哪些其他java對象具有相似特性的特性? 可以解釋一下嗎? 為了防止在我改變def時改變abc,我將不得不做def = abc.clone();

您將對象可變性/不可變性與復制參考值混淆。

在這些圖中, [var/index]是一個引用變量, {{an Object}}是一個對象。

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

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

   [def] ------> {{a String[0]}}

現在,您將def引用變量指向與abc引用變量相同的對象:

def=abc;

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

此時,長度為零的數組未被引用,並且應該是可垃圾收集的。 我們可以將討論范圍縮小到長度為一的數組。 請注意, String[]是一個引用數組。 在下一行中,您更改了一個數組指向的長度中唯一的元素。

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

   [abc] ------> {{a String[1]}}
              /  [0] ---------\      {{a String "abc"}}
             /                 \
   [def] ---/                   \--> {{a String "abcchanged"}}

請注意{{a String "abc"}}本身未發生變異。 [abc][def]現在指向相同的{{a String[1]}} ,這是可變的(即你可以使數組的元素,即String對象的引用,指向任何東西)。


為了防止在我改變def時改變abc ,我將不得不做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!"

這次我不會為你制作圖表,但你可以很容易地在紙上畫出來。 這里發生的事情是,即使clone()使用自己的元素(即def != abc )生成第二個{{a StringBuilder[1]}}對象,該元素也指向同一個{{a StringBuilder}}對象(即def[0] == abc[0] )。


簡而言之:

  • 不變性意味着某種類型的物體不能以任何有意義的方式改變為外部觀察者
    • IntegerString等是不可變的
    • 通常所有的值類型都應該是
  • 數組對象是可變的
    • 它可能是對不可變類型的引用數組,但數組本身是可變的
      • 意思是您可以將這些引用設置為您想要的任何內容
      • 對於基元數組也是如此
    • 不可變數組將不實用
  • 可以共享對象的引用
    • 如果對象是可變的,那么將通過所有這些引用看到變異

如果您想更深入地了解這些問題,我建議如下:

不可變對象是一旦創建就無法更改的對象。 String是一個明顯的例子。 數組是可變的。 如果您想要一個不可變的集合,請改用List

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

可變對象有變異器 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()); }
}

一般來說,對於不可變對象,所有成員都是final和immutable。 Date是上述問題的一個很好的例子。 Date不是一成不變的,許多人(包括我自己)認為這是設計錯誤。 由於它是可變的,你必須進行大量的防御性復制。

只是為了迂腐,沒有“abc”對象或“def”對象。 有單個String [] abc,然后def碰巧引用。 這就是為什么“兩個對象”都改變了。 事實上,他們指的是同一個對象。

簡單來說就是這樣: - 假設Sample是一個類,那么,

Sample sam1 = new Sample();

將清楚地解釋為sam1是對創建的對象的引用。

Sample sam2;

只是聲明sam2是Sample類型的引用變量,並且它沒有指向Sample類的對象。 現在如果我們做這個操作

sam2 = sam1;

那么這意味着兩個引用變量都指向同一個對象,現在可以使用兩個引用中的任何一個來引用該對象。 顯然,可以使用任一參考文獻來使用有效方法來操縱字段。 這也是這里所做的。

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

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

所以改變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.

在java中,無論數組的類型如何,您都可以隨時更改數組中的元素。 如果要將數據保存在數組結構中,請考慮單獨創建數據副本以保護abc的初始值:

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

或者,使用cletus解決方案:

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM