В Java все параметры передаются по значению, хотя некоторые говорят, что, мол, объекты передаются по ссылке. Но это не так — сейчас объясню тонкость.
Работать будем с классом Cat:
public class Cat { private String name; public Cat(String name) { this.name = name; } //setter/getter }
Пример №1
Для начала простой пример. Пусть у нас есть метод m(), который меняет имя кошки на Vasya:
void m(Cat cat) { cat.setName("Vasya"); }
Создадим кошку Petya, запомним ее в переменную oldCat и прогоним через метод:
Cat cat = new Cat("Petya"); Cat oldCat=cat; this.pass.m(cat); assertEquals("Vasya", cat.getName()); assertEquals(oldCat, cat);
Убедились, что теперь кошка стала Васей, при этом значение ссылки cat осталось прежним (впрочем, мы и не пытались его поменять внутри метода). Но имя то поменялось, то есть действия в методе мы проделали со внешней кошкой (созданной снаружи метода), а не с копией объекта кошки внутри метода. И это правда.
Но это не значит, что передача параметра произошла по ссылке. Она произошла по значению. И этим значением является ссылка.
То есть, oldCat и cat как до вызова метода располагалась по адресу 10 (к примеру), так и после вызова будут располагаться по адресу 10. Даже если бы внутри метода этот адрес попробовали поменять. А давайте «попробуем».
На следующем примере поясню, что такое настоящая передача по ссылке, и как это происходило бы к примеру в в языке C++ (в котором передача параметров и вправду может происходить по ссылке).
Пример № 2
Теперь у нас метод m1(), в котором мы переменной cat присвоим новую кошку Fifa:
void m1(Cat cat) { cat.setName("Vasya"); //(1) cat=new Cat("Fifa"); //(2) здесь в С++ можно поменять адрес внешней cat, а у нас меняется адрес внутренней cat cat.setName("Kitty"); //(3) System.out.println(cat.getName()); //Kitty }
Повторим вышеприведенный тест, и результат будет таким же. Внешняя кошка снова Vasya, и ссылка осталась прежней:
Cat cat = new Cat("Petya"); Cat oldCat=cat; this.pass.m1(cat); assertEquals("Vasya", cat.getName()); assertEquals(oldCat, cat);
Рассмотрим по шагам, что происходит.
- На строке (1) мы поменяли имя внешней кошки (поскольку cat здесь — все еще ссылка на внешнюю кошку) на имя Vasya, как и в первом примере.
- Но на строке (2) cat становится уже ссылкой на новую кошку, и ее адрес уже не 10, а какой-нибудь 42. Но этот адрес относится к внутренней ссылке cat, а никак не ко внешней. Внешняя ссылка cat не поменялась.
- То есть на строке (2) и далее все, что мы делаем с кошкой уже касается новой кошки Fifi, расположенной по новому адресу. Внешнюю кошку это уже не касается, имя меняется не у нее. Она остается по старому адресу, который хранится во внешних переменных cat и oldCat.
Вот в C++ мы могли бы передать ссылку — так, что на строке (2) не внутренняя cat встала на новый адрес, а адрес внешней cat встал бы на новую кошку. Вот в чем разница.
У нас же после прогона метода m1() ссылка cat по прежнему равна oldCat, значение ее не поменялось. В Java такого синтаксиса, как в С++ для передачи по ссылке нет.
Код примера тут.
Уже который раз натыкаюсь на это утверждение. «в Java все передается по значению!»
Почему такая путаница? Зачем людям вообще сранивать с С++?
Ссылка в с++ может хранить адрес объекта в куче. Можно передать объект по ссылке в качестве параметра в метод и это будет аналогично тому что происходит в Java — две ссылки на один и тот же объект. Просто разница лишь в том как вы это называете, а работа будет аналогичная!
В Java просто нету возможности создать объект на стэке или разыменовать указатель чтобы получить копию, зачем это придумали вообще?) Людей путать)
чуть проще. схематично
Obj obj1 = new Obj(«Вася»);
m(obj1); // передается значение ссылки, т.е. адрес на объект
assertEquals(«Вася», obj1.name); // оригинал ссылки obj1, объект не изменился
void m(Obj obj2){ // копия ссылки obj1
obj2 = new Obj(«Петя»); // переписали копию ссылки, но не объект по ней
}