Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Java: Общие вопросы > ссылки...


Автор: Alone 15.12.2003, 13:14
Так как в яве все объекты передаются по ссылке, то подскажите, как с этим правильно работать?
Как правильно использовать clone()? etc...

Автор: Sun 15.12.2003, 14:06
На самом деле утверждение что в Java объекты передаются по ссылке не совсем верно, во всяком случае ссылка в Java это не тоже самое что ссылка в C++. Можно провести простой эксперимент подтверждающий это
Код

class Test {
   public static void main(String args[]) throws Exception {
       Test t = new Test();
       String s = "blah-blah";
       t.ret(s);
       System.out.println("s="+s);
   }
   protected void ret(String s) {
       s = "Test";
   }
}

В этом примере вместо "Test" будет выведено "blah-blah", тогда как в подобном коде на C++ с использованием ссылок будет выведено слово "Test".
Похоже что при передаче объекта в метод происходит его клонирование.

Автор: AntonSaburov 15.12.2003, 14:27
Правильно работать - это значит знать и помнить, что все изменения, которые ты сделаешь будут относится к переданному объекту. Больше тут особо советовать нечего.

А по поводу clone() : Есть Cloneable специальный интерфейс, который дол;ен быть реализован если хочется создавать копию объекта. Если класс реализует Cloneable, то это говорит о том, что при вызове clone() будет создана копия обекта.
Обычное присвоение для объекта оператором присваивания (=) фактически является перенаправлением ссылки, а не копированием полей из одного объекта в другой.

Лучше всего почитать прямо в Java API об интерфейсе Cloneable и методе Object.clone().

Автор: Sun 15.12.2003, 14:48
Вот нашел небольшую статью, утверждающую что "ВСЕ ПАРАМЕТРЫ В JAVA ПЕРЕДАЮТСЯ ПО ЗНАЧЕНИЮ".
http://www.javable.com/columns/robinson/letters/01/
Думаю с автором можно согласиться, правда не стоит говорить такого при тестировании или устройстве на работу smile.gif

Автор: Alone 15.12.2003, 16:46
Спасибо обоим smile.gif

Автор: AntonSaburov 15.12.2003, 17:13
Цитата
Похоже что при передаче объекта в метод происходит его клонирование.


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

Тут важно отметить, что передаваемая ссылка и ссылка как аргумент - это разные ссылки.
Т.е происходит копирование ссылки.

Можно посмотреть на такой код:

Код

public class MyClass
{
 public int ddd = 100;
 public static void main(String[] args)
 {
   MyClass my1 = new MyClass();
   MyClass my2 = new MyClass();
   System.out.println("Before call:"+my2.ddd);
   my1.ShowMe(my2);
   System.out.println("After call:"+my2.ddd);
 }

 public void ShowMe(MyClass mc)
 {
   mc.ddd = 200;
   System.out.println("In method:"+mc.ddd);
   mc = new MyClass();
   System.out.println("In method:"+mc.ddd);
 }
}


Здесь интересен момент когда в ShowMe переменной mc присваивается указатель на новый объект. Т.е. теперь mc указывает уже на совсем другую переменную.
И здесь тогда сразу видно, что переменная my2 в main и переменная mc как аргумент ShowMe - это две разные переменные-ссылки. Просто в первый момент вызова ShowMe они обе указывают на один и тот же объект.

Вот и получаем эффект, который показан в примере Sun

Автор: Alone 16.12.2003, 18:10
Я столкнулся с таким вот :
Создаю новый объект моего типа.
myClass myObj=(myClass)myArray.get(n);
По идее - объект новый, но изменив в нем значения полей, они изменятся и в myArray[n]
sad.gif
То есть копирования не происходит, в myObj передается ссылка на объект из myArray[n]

Автор: daniel (yerevan| javaxp@ya 16.12.2003, 18:33
yourClass ob1=new yourClass();


yourClas ob2;

ob2=ob1; //ssylka na pamyat ob1
// ob1=null
// ob2 sushestvuet

Автор: Alone 16.12.2003, 18:48
Дело в том, что в данной реализации моего кода я не передаю объект в качестве параметра.
Он у меня объявлен паблик. Может это и некорректно, но мне так проще к нему обращаться.
Поэтому, для работы с ним, мне надо его скопировать(клонировать) для обеспечения возможности отмены изменений объекта. smile.gif

Автор: AntonSaburov 16.12.2003, 19:45
Цитата
Создаю новый объект моего типа.
myClass myObj=(myClass)myArray.get(n);


Так ты его не создаешь. Ты создаешь еще одну ссылку указывающую на один и тот же объект.

А для создания копии надо честно скопировать все поля из одного объекта в другой.
Т.е. если имеем объект
Код

class MyObject
{
   public int field1;
   public int field2;
}


то копирование объекта в другой будет подобно
Код

MYObject my1 = new MyObject();

/// присваиваем значения полям

// и вот реальное копирование
MYObject my2 = new MyObject();
my2.field1 = my1.field1;
my2.field2 = my1.field2;


Причем стоит отметить следующее - если объект внутри себя имеет сылки на объекты (поле типа Object и все, что дальше по наследованию), то опять же простое присваивание таких полей сделает следующее: ссылки во втором объекте будут указывать на тот же объект, что и в первом.

Автор: Alone 18.12.2003, 10:37
И всётаки... Что в этом случае лучше/корректней/проще/вернее сделать?
1. реализовать cloneable?
2. копировать ручками ( а если структура >20полей?, да и код увеличивается...)

Автор: Alone 18.12.2003, 12:45
Вот, добрался до реализации Cloneable... sad.gif
В связи с этим возникли вопросы:
Для решения проблемы клонирования, объект должен реализовать метод clone()
Делаем...
Код

public class test implements Cloneable{
 
 public int i;
 public String s;
 
 public test(){
 }

 public Object clone() {
   test clone=null;
   try {
       clone=(test) super.clone();
   }
   catch (CloneNotSupportedException e) {
       throw new InternalError(e.getMessage());
   }
   return clone;
 }

}


Идем дальше:
Код

public class test2 implements Cloneable{
 
 public String s;
 public List list    //массив элементов типа test
 
 public test(){
 }

 public Object clone() {
   test2 clone=null;
   try {
       clone=(test2) super.clone();
   }
   catch (CloneNotSupportedException e) {
       throw new InternalError(e.getMessage());
   }
   return clone;
 }

}


И наконец в главном коде:
Код

 test2 t2=new test2();
 t2.s="aaa";
 test t1=new test();
 t1.i=10;
 t1.s="aaa";
 t2.list.add(t1);
 // заполнили
 test2 tt2=new test2();
 // теперь если захотим сделать копию - что у нас выйдетconfused.gif
 tt2=(test2)t1.clone();
 // все бы хорошо, ток массив то мы получим как ссылку :(((
 // кто виноват и что делать!!!


Автор: AntonSaburov 18.12.2003, 13:31
Надо копировать список - и ручками. Т.е. создать новый список и скопировать туда содержимое из оригинала. Иначе никак не получится.

super.clone может копировать ТОЛЬКО primitive поля (int, double, long)

Автор: Alone 18.12.2003, 13:52
Хорошо, как тогда объяснить такое:

объект1
Код

public class mailRow implements Cloneable {
   
   /** Creates a new instance of mailRow */
   public mailRow() {
   }
   
   public Object clone() throws CloneNotSupportedException {
       mailRow clone=null;
       try {
           clone=(mailRow) super.clone();
       } catch (CloneNotSupportedException e) {
           throw new InternalError(e.getMessage());
       }
       return clone;
   }
   
   public int id;
   public String s;
}


объект2
Код

public class personRow implements Cloneable {
   
   /** Creates a new instance of personRow */
   public personRow() {
   }
   
   public Object clone() throws CloneNotSupportedException {
       personRow clone=null;
       try {
           clone=(personRow) super.clone();
       } catch (CloneNotSupportedException e) {
           throw new InternalError(e.getMessage());
       }
       return clone;
   }
   
   public int pid;
   public String name;
   public java.util.List eList=new java.util.LinkedList();
}


главный класс
Код

public class test {
   
   /** Creates a new instance of test */
   public test() {
   }
   
   /**
    * @param args the command line arguments
    */
   public static void main(String[] args) {
       personRow pRow=new personRow();
       mailRow mRow=new mailRow();
       mRow.id=1;
       mRow.s="s1";
       
       pRow.pid=1;
       pRow.name="name";
       pRow.eList.add(mRow);
       
       mRow=new mailRow();
       mRow.id=2;
       mRow.s="s2";
       pRow.eList.add(mRow);
       
       mailRow m0=(mailRow)pRow.eList.get(0);
       mailRow m1=(mailRow)pRow.eList.get(1);
       System.out.println("pRow.eList[0].id="+m0.id);
       System.out.println("pRow.eList[1].s="+m1.s);
       System.out.println("Create copy of pRow and change it");
       
       personRow pRow2=null;
       try {
           pRow2=(personRow)pRow.clone();
       }catch (CloneNotSupportedException e){
           
       }
       mailRow mm=new mailRow();
       mm.id=22;
       mm.s="s22";
       pRow2.eList.set(0,mm);
       mailRow mm1=(mailRow)pRow2.eList.get(0);
       System.out.println("pRow2.eList[0].id="+mm1.id);
       System.out.println("pRow2.eList[1].s="+mm1.s);
 System.out.println("check original");
       System.out.println("pRow.eList[0].id="+m0.id);
       System.out.println("pRow.eList[1].s="+m1.s);
       
   }
   
}



результат выполнения:

[root@alone java_tests]# java test
pRow.eList[0].id=1
pRow.eList[1].s=s2
Create copy of pRow and change it
pRow2.eList[0].id=22
pRow2.eList[1].s=s22
check original
pRow.eList[0].id=1
pRow.eList[1].s=s2

Получилась "правильная" копия листа... не ссылки, а новые объекты!
О, как!

Автор: Alone 18.12.2003, 13:59
Извиняюсь, ошибочка вышла... (с)Охота

вот здесь
Код

System.out.println("pRow2.eList[0].id="+mm1.id);
System.out.println("pRow2.eList[1].s="+mm1.s);
System.out.println("check original");
System.out.println("pRow.eList[0].id="+m0.id);
System.out.println("pRow.eList[1].s="+m1.s);


а надо вот так:
Код

System.out.println("pRow2.eList[0].id="+mm1.id);
       System.out.println("pRow2.eList[1].s="+mm1.s);
 System.out.println("check original");
       m0=(mailRow)pRow.eList.get(0);
       m1=(mailRow)pRow.eList.get(1);
       System.out.println("pRow.eList[0].id="+m0.id);
       System.out.println("pRow.eList[1].s="+m1.s);


и результат плачевный sad.gif
[root@alone java_tests]# java test
pRow.eList[0].id=1
pRow.eList[1].s=s2
Create copy of pRow and change it
pRow2.eList[0].id=22
pRow2.eList[1].s=s22
check original
pRow.eList[0].id=22
pRow.eList[1].s=s2

Автор: Alone 18.12.2003, 14:44
УРА! поборол таки!
рассказываю фсем:

в класс, который предполагается клонировать, надо имплементить интерфейс Cloneable и реализовать его метод clone()
Если в классе встречается поле типа массива, то в реализуемом методе clone() надо
1. создать новый массив
2. в цикле клонируем все элементы массива
3. заменяем поле типа массив новым массивом
4. возвращаем клонированный объект

пример:
Код

public class personRow implements Cloneable {
   
   /** Creates a new instance of personRow */
   public personRow() {
   }
   
   public Object clone() throws CloneNotSupportedException {
       personRow clone=null;
       try {
           clone=(personRow) super.clone();
       } catch (CloneNotSupportedException e) {
           throw new InternalError(e.getMessage());
       }
       
       java.util.List L=new java.util.LinkedList();
       
       for(int k=0; k<eList.size(); k++){
           mailRow oldRow=null;
           oldRow=(mailRow)eList.get(k);
           mailRow newRow=null;
           newRow=(mailRow)oldRow.clone();
       }
       clone.eList=L;
       return clone;
   }
   
   public int pid;
   public String name;
   public java.util.List eList=new java.util.LinkedList();
}


а вот класс, который является элементом массива
Код

public class mailRow implements Cloneable {
   
   /** Creates a new instance of mailRow */
   public mailRow() {
   }
   
   public Object clone() throws CloneNotSupportedException {
       mailRow clone=null;
       try {
           clone=(mailRow) super.clone();
       } catch (CloneNotSupportedException e) {
           throw new InternalError(e.getMessage());
       }
       return clone;
   }
   
   public int id;
   public String s;
}


Теперь если у нас есть объект
personRow a=(personRow)AnyArray.get(i);
Мы смело можем его клонировать!
personRow b=(personRow)a.clone();
smile.gif

Автор: AntonSaburov 18.12.2003, 15:10
Так я тебе это и пытался все это время втолковать - надо фактически создать новый объект и честно скопировать все поля. Если поля имеют сложный тип, то надо создавать объект этого типа и опять делать копию. И так далее.

Автор: Alone 18.12.2003, 15:31
Кстати, я опять ошибся sad.gif
класс LinkedList уже имплементит Cloneable, и в нем есть реализация метода clone
Только выполняет он таки "поверхностное" копирование, то бишь лепит ссылку...

Фууух... блин, как тяжко... sad.gif

Автор: Alone 18.12.2003, 15:35
Цитата(AntonSaburov @ 18.12.2003, 15:10)
Если поля имеют сложный тип, то надо создавать объект этого типа и опять делать копию. И так далее.

Насколько я понял, для этого надо "сложный тип" делать клонируемым.

Автор: AntonSaburov 18.12.2003, 17:24
Цитата
Насколько я понял, для этого надо "сложный тип" делать клонируемым.


Можно делать, а можно в лоб копировать все. Тут уже как хочешь. Но как ты сам видел многие стандартные JAVA-классы делают "shallow copy" (поверхностная копия). А в твоем случае потребовалось "deep copy" (глубокая копия) (перевод само собой условный).

Вот ты и нарвался. Впрочем это как раз в описании Object.clone() очень подробно расписано.

Автор: Alone 18.12.2003, 19:30
Да я просто в исходник LinkedList глянул smile.gif

Кстати, по ходу дела вопрос.

почему надо делать именно так:
java.util.List myList = new java.util.LinkedList();
а не так:
java.util.LinkedList myList = new java.util.LinkedList();

У?

Почему обявление - интерфейсом, а нью - классом?

Автор: AntonSaburov 18.12.2003, 20:35
Цитата
почему надо делать именно так:
java.util.List myList = new java.util.LinkedList();
а не так:
java.util.LinkedList myList = new java.util.LinkedList();


new классом - а иначе просто никак.
А вот почему надо делать ?
Я бы сформулировал так - почему так делают ?

Ссылка на интерфейс говорит о том, что мы будем использовать созданный класс только для работы именно с этим интерфейсом. И никак иначе. В таком случае код получается более защищенным и мобильным.

Вдруг ты захочешь использовать для этого интерфейса другой класс ? Просто замени его в операторе new.

И тебе тогда нельзя будет использовать методы созданного объекта, которые он получил от класса. Значит ты получаешь более защищенный код.

Вот вообщем-то основные мысли по этому поводу.

И больше в этом топике вопросы не по теме не надо - против правил не стоит идти

Автор: Alone 19.12.2003, 12:04
За оффтоп понял, за остальное - спасибо.
Тема в принципе исчерпана.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)