2013-06-04 3 views
2

Я не совсем понимаю, когда Java передает копию/значение и когда она передает «ссылку» (указатель).Получение копии объекта

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

У меня есть это:

static ArrayList<MyObject> myObjects; 

Я хочу получить копию myObjects, так что я могу играть с ценностями, не затрагивая оригинал. Передаёт ли он ссылку или копию/значение, когда я использую такой газопоглотитель:

public static ArrayList<MyObject> getMyObject() 
{ 
    return ThisClass.myObjects; 
} 

Что это такое? Если это ссылка, как я могу получить копию?

Я видел это:

(How do I copy an object in Java?)

(Java: getter method vs. public instance variable: performance and memory)

(Is Java "pass-by-reference" or "pass-by-value"?)

(How can Java assignment be made to point to an object instead of making a copy?)

Но я до сих пор не совсем понимаю, я вернусь.

+0

Вам понадобится глубокая копия списка, если вы беспокоитесь о том, что вы не изменяете 'MyObject' в исходном списке. –

ответ

5

Java всегда будет возвращать ссылку, а не копию, пока это не примитивный тип (так называемый long, int, short и т.д., или один из примитивных оберток Long, Integer, Short.

Чтобы получить вам понадобится скопировать данные, использовать конструктор копирования или использовать метод clone, который создаст новый объект с соответствующими значениями.

Пример конструктора копирования со списком, по умолчанию это " мелкая копия "означает, что объекты внутри одинаковы.

List<MyObject> myNewCopiedList = new ArrayList<MyObject>(oldList); 

Для «глубокой копии», то есть объекты внутри может мутировать, не влияя на оригиналы, нужно будет сделать новый список затем добавить копий/клоны объекта и добавить.

Пример, предполагая, что MyObject имеет конструктор копирования или метод clone.

List<MyObject> myNewCopiedList = new ArrayList<MyObject>(); 
for (MyObject myo : oldList){ 
    myNewCopiedList.add(new MyObject(myo)); // if there is a copy constructor 
    myNewCopiedList.add(myo.clone()); // if there is clone method 
} 
+0

добавлена ​​информация о мелкой и глубокой копии – greedybuddha

+0

Отлично. Я забыл ВСЕ о глубоких копиях и почти имел проблему с этим. Спасибо за всю информацию! Это всегда хлопот, чтобы переключаться между языками, но это намного больше похоже на C, чем я позволял. Опять же, это потрясающе, так что спасибо! – RileyE

1

Технически Java всегда передается по значению. Тем не менее, для мышления новичка легче думать об этом так:

Если это примитивный тип, это пропускная стоимость.

Если это объект, это пересылка по ссылке.

Итак, в вашем примере вы возвращаете ссылку на тот же объект static в ThisClass.Причина, по которой я говорю, технически передается по значению, потому что ваша переменная myObjects фактически сохраняет адрес памяти ArrayList<MyObject>, который вы объявили, и это передается.

+0

Я думаю, что гораздо лучше думать о переменных типа класса, как о «объектных идентификаторах». Все параметры передаются по значению; вызов 'PaintBooth.Spray (myCar)', когда 'myCar' держит' Car @ 14912C', будет аналогичен тому, кто находится в ремонтной мастерской, записывая идентификационный номер автомобиля, хранящийся в 'myCar', в верхней части рабочего заказа и отправляет его в лакировка, которая затем находит автомобиль и рисует его. В ларьке не дана машина; скорее, ему дается * копия * VIN, и она использует эту копию VIN для извлечения автомобиля. – supercat

+0

@supercat Мне нравится ваша аналогия, но я не понимаю, как это сильно отличается от того, что я сказал. :) Если вы называете это «адресом памяти» или «идентификатором объекта», концепция остается прежней. – asteri

+0

Когда переменная передается по ссылке, метод, получающий эту переменную, не может продолжать ссылаться на нее. Все, что он собирается делать с этой переменной, должно быть выполнено до того, как оно вернется. Напротив, когда передается идентификатор объекта, получатель может свободно копировать этот идентификатор любым способом, который он считает нужным, и заставить его использовать для любой произвольной цели в любое произвольное время, даже после того, как метод вернется к вызывающему. – supercat

1

Для того, чтобы правильно сделать копию объекта, необходимо знать, какие непримитивные поля инкапсулировать

  • Мутабельном аспекты состояния объекта, а не его личность

  • Идентичность объект и другие непреложные аспекты, но не изменяемые аспекты.

  • аспекты объекта, которые, как ожидается, не будут подвергаться какой-либо код, который может мутировать их (а не личность)

  • Mutable аспекты состояния объекта, а также его личность

Основываясь на том, что инкапсулирует поле, правильную копию Foo

  • Если один из Foo, которое инкапсулирует изменяемое состояние, соответствующее поле в копии Foo должно содержать ссылку на другой объект с тем же состоянием.

  • Если поле инкапсулирует идентификатор объекта, что поле в копии должны содержать ссылку на же объекта как в Foo - не копия.

  • Если поле инкапсулирует только неизменные аспекты , кроме идентичности, то копия Foo может либо содержать ссылку на тот же объект, как и в Foo, или любой объект, который имеет один и тот же неизменное состояние, так удобно.

  • Если поле инкапсулирует как изменяемое состояние, так и идентичность, поскольку первые два требования будут конфликтовать, невозможно будет изолировать объект отдельно.

В некоторых случаях может быть возможно скопировать набор изменяемых объектов, которые используют ссылки друг для друга для инкапсуляции как состояния, так и идентичности. Такая копия должна выполняться на множестве в целом; для каждого объекта в наборе любое поле, которое в исходном объекте инкапсулирует как изменяемое состояние, так и идентификатор другого объекта в исходном наборе, должно в копии ссылаться на соответствующий объект в скопированном наборе.

+0

Это отличная информация. Спасибо! – RileyE

2

Думайте об этом таким образом. Java всегда проходит по значению.

Для примитивов это пропуск по значению (фактическое значение). Для объектов это пропуск по значению ссылки.

public int square(int a) { //The parameter a is copy of actual int itself. 
//So now there are 2 ints 
a=a*a; //Only local copy a is actually modified. 
     //The integer variable passed(in caller function) is not modified. 
return a; 
} 

Если вы звоните DoSomething (d), где d является объектом, копия эталонного указывающего к этому объекту присваивается параметр a, но есть только один объект.

public void doSomething(Object a) { 
// Here the parameter is a reference which points to an 
// object, not the object itself 
a.doMore(); //But doMore() does things using a different ref but on the same object. 
      //The object can be modified! 

Object b = new Object(); 
a = b; //Object referenced by passed parameter does not change but 
     //copy of reference now points to different object. 
     // Now there is no reference of original object passed in this method. 
} 
+0

Хорошо. Итак, на Java действительно существуют указатели, но они кажутся «невидимыми»/немаркированными. Теперь это имеет больше смысла. Прохождение всех этих ответов помогло мне задуматься о перераспределении памяти, а не думать о «копировании». – RileyE

Смежные вопросы