Является ли Java «передачей по ссылке» или «передачей по значению»? - PullRequest
5963 голосов
/ 03 сентября 2008

Я всегда думал, что Java была передача по ссылке .

Однако я видел пару постов в блоге (например, этот блог ), в которых утверждается, что это не так.

Я не думаю, что понимаю разницу, которую они проводят.

Какое объяснение?

Ответы [ 79 ]

45 голосов
/ 03 июля 2014

Я думал, что добавлю этот ответ, чтобы добавить больше деталей из Спецификаций.

Во-первых, В чем разница между передачей по ссылке и передачей по значению?

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

Передача по значению означает, что параметр вызываемых функций будет копией переданный аргумент звонящего.

Или из Википедии, на тему передачи по ссылке

При оценке по ссылке (также именуемой передача по ссылке), функция получает неявную ссылку на переменная, используемая в качестве аргумента, а не копия его значения. это обычно означает, что функция может изменить (т.е. назначить) переменная, используемая в качестве аргумента - то, что увидит ее вызывающий.

И на предмет передачи по значению

В вызове по значению вычисляется выражение аргумента, а Полученное значение связывается с соответствующей переменной в функции [...]. Если функция или процедура может назначать значения своим параметры, назначается только его локальная копия [...].

Во-вторых, нам нужно знать, что Java использует в своих вызовах методов. Спецификация языка Java состояния

Когда вызывается метод или конструктор (§15.12), значения фактические выражения аргумента инициализируют вновь созданный параметр переменные , каждая из объявленного типа, перед выполнением тела метод или конструктор.

Таким образом, он присваивает (или связывает) значение аргумента соответствующей переменной параметра.

Каково значение аргумента?

Давайте рассмотрим ссылочные типы, Спецификация виртуальной машины Java состояния

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

Спецификация языка Java также заявляет

Ссылочные значения (часто просто ссылки) являются указателями на эти объекты и специальной нулевой ссылкой, которая не ссылается на объект.

Значением аргумента (некоторого ссылочного типа) является указатель на объект. Обратите внимание, что переменная, вызов метода с возвращаемым типом ссылочного типа и выражение создания экземпляра (new ...) разрешаются в значение ссылочного типа.

So

public void method (String param) {}
...
String var = new String("ref");
method(var);
method(var.toString());
method(new String("ref"));

все привязывают значение ссылки на экземпляр String к вновь созданному параметру метода, param. Это именно то, что описывает определение передачи по значению. Таким образом, Java передается по значению .

Тот факт, что вы можете следовать по ссылке, чтобы вызвать метод или получить доступ к полю ссылочного объекта, совершенно не имеет отношения к разговору. Определение передачи по ссылке было

Как правило, это означает, что функция может изменять (т.е. назначать) переменная, используемая в качестве аргумента - то, что увидит ее вызывающий.

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


Примитивные значения также определены в Спецификации виртуальной машины Java, здесь . Значением типа является соответствующее целочисленное значение или значение с плавающей запятой, закодированное соответствующим образом (8, 16, 32, 64 и т. Д. Биты).

36 голосов
/ 03 сентября 2008

Различие, или, может быть, просто то, что я помню, как у меня было такое же впечатление, как у оригинального плаката, таково: Java всегда передается по значению. Все объекты (в Java, все, кроме примитивов) в Java являются ссылками. Эти ссылки передаются по значению.

34 голосов
/ 04 сентября 2008

Как уже упоминали многие люди, Java всегда передается по значению

Вот еще один пример, который поможет вам понять разницу ( классический пример обмена ):

public class Test {
  public static void main(String[] args) {
    Integer a = new Integer(2);
    Integer b = new Integer(3);
    System.out.println("Before: a = " + a + ", b = " + b);
    swap(a,b);
    System.out.println("After: a = " + a + ", b = " + b);
  }

  public static swap(Integer iA, Integer iB) {
    Integer tmp = iA;
    iA = iB;
    iB = tmp;
  }
}

Отпечатки:

До: а = 2, б = 3
После: а = 2, б = 3

Это происходит потому, что iA и iB являются новыми локальными ссылочными переменными, которые имеют одинаковое значение переданных ссылок (они указывают на a и b соответственно). Таким образом, попытка изменить ссылки на iA или iB изменится только в локальной области, а не вне этого метода.

32 голосов
/ 24 июля 2016

В Java только ссылки передаются и передаются по значению:

Аргументы Java все переданы по значению (ссылка используется при использовании метода):

В случае примитивных типов поведение Java простое: Значение копируется в другой экземпляр типа примитива.

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

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

«Строковые» объекты кажутся совершенным контрпримером к городской легенде о том, что «Объекты передаются по ссылке»:

По сути, внутри метода вы никогда не сможете обновить значение строки, переданной в качестве аргумента:

Объект String, содержит символы в массиве, объявленном final , который нельзя изменить. Только адрес объекта может быть заменен другим, используя «новый». Использование «new» для обновления переменной не позволит получить доступ к объекту извне, поскольку переменная изначально была передана по значению и скопирована.

32 голосов
/ 10 июля 2013

Java имеет только проход по значению. Очень простой пример для подтверждения этого.

public void test() {
    MyClass obj = null;
    init(obj);
    //After calling init method, obj still points to null
    //this is because obj is passed as value and not as reference.
}
private void init(MyClass objVar) {
    objVar = new MyClass();
}
30 голосов
/ 08 сентября 2008

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

public class PassByCopy{
    public static void changeName(Dog d){
        d.name = "Fido";
    }
    public static void main(String[] args){
        Dog d = new Dog("Maxx");
        System.out.println("name= "+ d.name);
        changeName(d);
        System.out.println("name= "+ d.name);
    }
}
class Dog{
    public String name;
    public Dog(String s){
        this.name = s;
    }
}

вывод java PassByCopy:

name = Maxx
имя = Фидо

Примитивные классы-обертки и строки неизменны, поэтому любой пример, использующий эти типы, не будет работать так же, как другие типы / объекты.

25 голосов
/ 08 сентября 2008

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

Java также упоминается . Вот краткое резюме:

  • Java передает его параметры по значению
  • «по значению» - единственный способ в java передать параметр методу
  • использование методов из объекта, указанного в качестве параметра, изменит объект как ссылки указывают на оригинальные предметы. (если это сам метод изменяет некоторые значения)
24 голосов
/ 03 сентября 2008

Короче говоря, Java объекты имеют некоторые очень специфические свойства.

Как правило, Java имеет примитивные типы (int, bool, char, double и т. Д.), Которые передаются непосредственно по значению. Тогда у Java есть объекты (все, что происходит от java.lang.Object). Объекты на самом деле всегда обрабатываются с помощью ссылки (ссылка - это указатель, который вы не можете коснуться). Это означает, что, по сути, объекты передаются по ссылке, так как ссылки обычно не интересны. Это, однако, означает, что вы не можете изменить объект, на который указывает объект, так как сама ссылка передается по значению.

Звучит странно и сбивает с толку? Давайте рассмотрим, как C реализует передачу по ссылке и передачу по значению. В С соглашением по умолчанию является передача по значению. void foo(int x) передает int по значению. void foo(int *x) - это функция, которая не хочет int a, но указывает на int: foo(&a). Можно использовать это с оператором & для передачи адреса переменной.

Отнесите это на C ++, и у нас есть ссылки. Ссылки в основном (в этом контексте) синтаксический сахар, который скрывает указатель части уравнения: void foo(int &x) вызывается foo(a), где сам компилятор знает, что это ссылка и адрес не-ссылки a должен быть пройден. В Java все переменные, ссылающиеся на объекты, на самом деле относятся к ссылочному типу, фактически вызывая вызов по ссылке для большинства целей и задач без детального контроля (и сложности), предоставляемого, например, C ++.

24 голосов
/ 26 декабря 2009

Несколько исправлений к некоторым постам.

C НЕ поддерживает передачу по ссылке. ВСЕГДА передается по значению. C ++ поддерживает передачу по ссылке, но не используется по умолчанию и довольно опасен.

Неважно, какое значение в Java: примитив или адрес (примерно) объекта, оно ВСЕГДА передается по значению.

Если объект Java "ведет себя" так, как его передают по ссылке, это свойство изменчивости и не имеет абсолютно никакого отношения к механизмам передачи.

Я не уверен, почему это так запутанно, возможно, потому, что так много "программистов" на Java не обучены формально и поэтому не понимают, что в действительности происходит в памяти?

22 голосов
/ 19 марта 2015

Java передает параметры по VALUE и по значению ONLY .

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

Для тех, кто прибывает из C #: НЕТ параметра "out".

Для тех, кто прибывает из PASCAL: НЕТ параметра "var" .

Это означает, что вы не можете изменить ссылку на сам объект, но вы всегда можете изменить свойства объекта.

Обходной путь должен использовать параметр StringBuilder вместо String. И вы всегда можете использовать массивы!

...