вопрос неизменяемого объекта Java - PullRequest
2 голосов
/ 29 марта 2010
String abc[]={"abc"};
String def[]={};

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

изменяя объект "def", мой объект abc также изменяется. У массива String [] есть такая характеристика, какой другой Java-объект имеет аналогичную характеристику? можете объяснить больше? чтобы предотвратить изменение abc при изменении def, мне нужно будет выполнить def = abc.clone ();

Ответы [ 5 ]

13 голосов
/ 29 марта 2010

Вы путаете изменчивость / неизменность объекта с копированием эталонных значений.

На этих диаграммах [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 объекты, указывать на что угодно).


, чтобы предотвратить изменение 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!"

В этот раз я не буду делать для вас диаграммы, но вы легко можете нарисовать их на бумаге. Здесь происходит то, что, хотя clone() создает второй {{a StringBuilder[1]}} объект со своим собственным элементом (то есть def != abc), этот элемент указывает на тот же объект {{a StringBuilder}} (т.е. def[0] == abc[0]).


Короче говоря:

  • Неизменяемость означает, что объекты определенного типа не могут изменяться каким-либо значимым образом для внешних наблюдателей.
    • Integer, String и т. Д. Являются неизменными
    • Обычно все типы значений должны быть
  • Объекты массива изменчивы
    • Это может быть массив ссылок на неизменяемые типы, но сам массив является изменяемым
      • То есть вы можете установить эти ссылки на что угодно
      • Также верно для массива примитивов
    • Неизменяемый массив не будет практичным
  • Ссылками на объекты можно делиться
    • Если объект изменчив, мутация будет видна через все эти ссылки

Если вам нужно более глубокое понимание проблем, я рекомендую следующее:

5 голосов
/ 29 марта 2010

просто чтобы быть педантичным, нет объекта "abc" или объекта "def". Есть одна строка [], которую abc, и затем def, на которую ссылаются. Вот почему «оба объекта» изменились. На самом деле они ссылались на один и тот же объект.

5 голосов
/ 29 марта 2010

Неизменяемые объекты - это объекты, которые нельзя изменить после создания. String является очевидным примером. Массивы изменчивы. Если вы хотите неизменную коллекцию, используйте List вместо:

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

Изменчивые объекты имеют мутаторы . Мутатор - это любой метод, который изменяет состояние объекта. Сеттеры являются очевидным примером. Типичный неизменный объект будет выглядеть так:

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 и неизменны. Date является хорошим примером вышеуказанной проблемы. Date не является неизменным, что многие (включая меня) считают ошибкой проектирования. Из-за того, что оно изменчиво, вам приходится много защищаться.

1 голос
/ 29 марта 2010

В Java вы всегда можете изменить элементы в массиве, независимо от типа массива. Попробуйте сохранить отдельную копию данных, чтобы защитить начальное значение abc, если вы хотите сохранить данные в структуре массива:

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

В качестве альтернативы, используйте раствор Клетуса:

List abc = Collections.unmodifiableList(
  Arrays.asList("abc")
);
1 голос
/ 29 марта 2010

Проще говоря, это так: Предположим, что 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.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...