Является ли Java «передачей по ссылке» или «передачей по значению»?

5703

Я всегда считал, что Java является сквозной ссылкой.

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

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

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

  • 608
    Я считаю, что большая часть путаницы в этом вопросе связана с тем, что разные люди имеют разные определения термина «ссылка». Люди из C ++ полагают, что «ссылка» должна означать то же, что и в C ++, люди из C полагают, что «ссылка» должна быть такой же, как «указатель» в их языке, и так далее. Правильно ли говорить, что Java проходит по ссылке, на самом деле зависит от того, что подразумевается под «ссылкой».
  • 103
    Я стараюсь последовательно использовать терминологию, найденную в статье « Стратегия оценки» . Следует отметить, что, хотя в статье указывается, что термины сильно различаются в зависимости от сообщества, в ней подчеркивается, что семантика для call-by-value и call-by-reference отличается очень важным образом . (Лично я предпочитаю использовать совместное использование call-by-object-sharing время по call-by-value[-of-the-reference] , так как это описывает семантику на высоком уровне и не создает конфликта с call-by-value , которое является базовой реализацией.)
Показать ещё 18 комментариев
Теги:
methods
parameter-passing
pass-by-value
pass-by-reference

92 ответа

5321

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

Это выглядит так:

public static void main(String[] args) {
    Dog aDog = new Dog("Max");
    Dog oldDog = aDog;

    // we pass the object to foo
    foo(aDog);
    // aDog variable is still pointing to the "Max" dog when foo(...) returns
    aDog.getName().equals("Max"); // true
    aDog.getName().equals("Fifi"); // false
    aDog == oldDog; // true
}

public static void foo(Dog d) {
    d.getName().equals("Max"); // true
    // change d inside of foo() to point to a new Dog instance "Fifi"
    d = new Dog("Fifi");
    d.getName().equals("Fifi"); // true
}

В приведенном выше примере aDog.getName() будет по-прежнему возвращать "Max". Значение aDog в main не изменяется в функции foo с помощью Dog "Fifi" как ссылка на объект передается по значению. Если бы он был передан по ссылке, то aDog.getName() в main вернул бы "Fifi" после вызова foo.

Точно так же:

public static void main(String[] args) {
    Dog aDog = new Dog("Max");
    Dog oldDog = aDog;

    foo(aDog);
    // when foo(...) returns, the name of the dog has been changed to "Fifi"
    aDog.getName().equals("Fifi"); // true
    // but it is still the same dog:
    aDog == oldDog; // true
}

public static void foo(Dog d) {
    d.getName().equals("Max"); // true
    // this changes the name of d to be "Fifi"
    d.setName("Fifi");
}

В приведенном выше примере Fifi - это имя собаки после вызова foo(aDog) потому что имя объекта было установлено внутри foo(...). Любые операции, которые foo выполняет на d таковы, что для всех практических целей, они выполняются на aDog, но это не возможно, чтобы изменить значение переменной aDog самой.

  • 232
    Разве это не немного путает проблему с внутренними деталями? Нет никакой концептуальной разницы между «передачей ссылки» и «передачей значения ссылки», при условии, что вы имеете в виду «значение внутреннего указателя на объект».
  • 352
    Но есть небольшая разница. Посмотрите на первый пример. Если бы он был просто передан по ссылке, aDog.name был бы "Fifi". Это не так - ссылка, которую вы получаете, является ссылкой на значение, которая при перезаписи будет восстановлена при выходе из функции.
Показать ещё 77 комментариев
2630

Я просто заметил, что вы ссылались на мою статью.

В Java Spec сказано, что все в Java является передачей по значению. В Java нет такой вещи, как "pass-by-reference".

Ключом к пониманию этого является то, что что-то вроде

Dog myDog;

не является Собакой; это на самом деле указатель на Собака.

Что это значит, когда у вас

Dog myDog = new Dog("Rover");
foo(myDog);

вы по существу передаете адрес созданного объекта Dog методу foo.

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

Предположим, что объект Dog находится по адресу 42 памяти. Это означает, что мы передаем метод 42.

если метод был определен как

public void foo(Dog someDog) {
    someDog.setName("Max");     // AAA
    someDog = new Dog("Fifi");  // BBB
    someDog.setName("Rowlf");   // CCC
}

посмотрим, что происходит.

  • параметр someDog установлен в значение 42
  • в строке "AAA"
    • someDog применяется к Dog, который указывает (объект Dog по адресу 42)
    • чтобы Dog (тот, на адрес 42) попросил изменить его имя на Max
  • в строке "BBB"
    • создается новый Dog. Скажем, он по адресу 74
    • мы назначаем параметр someDog равным 74
  • в строке "CCC"
    • someDog следует за Dog, который указывает (объект Dog по адресу 74)
    • чтобы Dog (тот, на адрес 74), попросил изменить его имя на Rowlf
  • тогда мы возвращаем

Теперь подумайте о том, что происходит вне метода:

Изменил ли myDog?

Вот ключ.

Имея в виду, что myDog является указателем, а не фактическим Dog, ответ НЕТ. myDog все еще имеет значение 42; он по-прежнему указывает на оригинал Dog (но обратите внимание, что из-за строки "AAA" его имя теперь "Макс" - все тот же символ Dog; myDog не изменился.)

Совершенно верно, чтобы следить за адресом и изменять то, что в конце его; который не изменяет переменную, однако.

Java работает точно так же, как C. Вы можете назначить указатель, передать указатель методу, следовать указателю в методе и изменить данные, на которые указали. Тем не менее, вы не можете изменить, куда указывает этот указатель.

В С++, Ada, Pascal и других языках, поддерживающих pass-by-reference, вы действительно можете изменить переданную переменную.

Если Java имеет семантику сквозной ссылки, метод foo, который мы определили выше, изменился бы там, где myDog указывал, когда он назначал someDog в строке BBB.

Подумайте о ссылочных параметрах как псевдонимах для переданной переменной. Когда этот псевдоним назначается, так же как и переменная, которая была передана.

  • 136
    Вот почему распространенный рефрен «У Java нет указателей» вводит в заблуждение.
  • 129
    Вы не правы, имхо. «Помня, что myDog - это указатель, а не настоящий Dog, ответ НЕТ. MyDog по-прежнему имеет значение 42; он по-прежнему указывает на исходный Dog». myDog имеет значение 42, но его аргумент name теперь содержит «Max», а не «Rover» в строке // AAA.
Показать ещё 32 комментария
1472

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


Позвольте мне объяснить это на примере:

public class Main{
     public static void main(String[] args){
          Foo f = new Foo("f");
          changeReference(f); // It won't change the reference!
          modifyReference(f); // It will modify the object that the reference variable "f" refers to!
     }
     public static void changeReference(Foo a){
          Foo b = new Foo("b");
          a = b;
     }
     public static void modifyReference(Foo c){
          c.setAttribute("c");
     }
}

Я объясню это по шагам:

  1. Объявление ссылки с именем f типа Foo и присвоение ей нового объекта типа Foo с атрибутом "f".

    Foo f = new Foo("f");
    

    Изображение 1270

  2. Со стороны метода объявляется ссылка типа Foo с именем a которому изначально присваивается значение null.

    public static void changeReference(Foo a)
    

    Изображение 1271

  3. Когда вы вызываете метод changeReference, ссылке a будет присвоен объект, который передается в качестве аргумента.

    changeReference(f);
    

    Изображение 1272

  4. Объявить ссылку с именем b типа Foo и назначить ей новый объект типа Foo с атрибутом "b".

    Foo b = new Foo("b");
    

    Изображение 1273

  5. a = b делает новое назначение для ссылки a, а не f, объекта, его атрибутом которого является "b".

    Изображение 1274


  6. Когда вы вызываете modifyReference(Foo c), создается ссылка c и назначается объект с атрибутом "f".

    Изображение 1275

  7. c.setAttribute("c"); изменит атрибут объекта, на который указывает ссылка c, и того же объекта, на который указывает ссылка f.

    Изображение 1276

Надеюсь, теперь вы понимаете, как передача объектов в качестве аргументов работает в Java :)

  • 71
    +1 Хороший материал. хорошие диаграммы. Я также нашел хорошую лаконичную страницу здесь adp-gmbh.ch/php/pass_by_reference.html Хорошо, я признаю, что она написана на PHP, но это - основа понимания разницы, которую я считаю важной (и как манипулировать этой разницей твои нужды).
  • 4
    @ Eng.Fouad Это хорошее объяснение , но если a указывает на тот же объект, f (и никогда не получает свою собственную копию объекта f указывает на), любые изменения объекта с использованием следует изменять a f Aswell (так как они оба работать с тем же объектом), поэтому в какой-то момент a должен получить свою собственную копию объекта, на который указывает f .
Показать ещё 11 комментариев
714

Это даст вам представление о том, как Java действительно работает до такой степени, что в следующем обсуждении о передаче Java по ссылке или передаче по значению вы просто будете улыбаться: -)

Шаг один, пожалуйста, удалите из своего разума это слово, которое начинается с "p" "_ _ _ _ _ _ _", особенно если вы исходите из других языков программирования. Java и "p" не могут быть записаны в одной книге, форуме или даже в txt.

Шаг 2 помните, что при передаче объекта в метод вы передаете ссылку Object, а не сам объект.

  • Студент: Мастер, означает ли это, что Java является передачей по ссылке?
  • Мастер: Кузнечик, Нет.

Теперь подумайте о том, какая ссылка/переменная Object имеет /:

  • Переменная содержит биты, которые сообщают JVM, как перейти к указанному объекту в памяти (кучи).
  • При передаче аргументов методу вы НЕ передаете ссылочную переменную, а копию битов в ссылочной переменной. Что-то вроде этого: 3bad086a. 3bad086a представляет способ доступа к переданному объекту.
  • Итак, вы просто передаете 3bad086a, что это значение ссылки.
  • Вы передаете значение ссылки, а не ссылку (а не объект).
  • Это значение фактически КОПИРОВАНО и дано методу.

В следующем (пожалуйста, не пытайтесь скомпилировать/выполнить это...):

1. Person person;
2. person = new Person("Tom");
3. changeName(person);
4.
5. //I didn't use Person person below as an argument to be nice
6. static void changeName(Person anotherReferenceToTheSamePersonObject) {
7.     anotherReferenceToTheSamePersonObject.setName("Jerry");
8. }

Что происходит?

  • Пользователь переменной создается в строке # 1, а в начале - null.
  • Новый объект Person создается в строке # 2, хранящейся в памяти, а человеку переменной присваивается ссылка на объект Person. То есть, его адрес. Скажем 3bad086a.
  • Лицо переменной, содержащее адрес объекта, передается функции в строке # 3.
  • В строке # 4 вы можете слушать звук тишины
  • Отметьте комментарий на строке # 5
  • Создается локальная переменная метода -anotherReferenceToTheSamePersonObject-, а затем появляется волшебство в строке # 6:
    • Пользователь переменной/ссылки копируется по-бит и передается в другойReferenceToTheSamePersonObject внутри функции.
    • Нет новых экземпляров Person.
    • Оба "person" и "anotherReferenceToTheSamePersonObject" сохраняют одно и то же значение 3bad086a.
    • Не пытайтесь это, но человек == anotherReferenceToTheSamePersonObject будет правдой.
    • Обе переменные имеют ИДЕНТИФИКАЦИОННЫЕ КОПИИ ссылки, и оба они относятся к одному объекту Person, SAME Object on the Heap и NOT A COPY.

Изображение стоит тысячи слов:

Изображение 1277

Обратите внимание, что стрелки anotherReferenceToTheSamePersonObject направлены к объекту, а не к переменной!

Если вы этого не поняли, просто доверьтесь мне и помните, что лучше сказать, что Java передается по значению. Ну, передайте ссылочное значение. О, хорошо, еще лучше pass-by-copy-of-the-variable-value!;)

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

Вы всегда передаете копию бит значения ссылки!

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

Java - это пропускная способность, потому что внутри метода вы можете изменять ссылочный объект столько, сколько хотите, но как бы вы ни старались, вы никогда не сможете изменить переданную переменную, которая будет продолжать ссылаться (не p _ _ _ _ _ _ _) тот же объект независимо от того, что!


Вышеуказанная функция changeName никогда не сможет изменить фактическое содержимое (битовые значения) переданной ссылки. В другом слове changeName не может заставить Person лицо ссылаться на другой объект.


Конечно, вы можете сократить его и просто сказать, что Java - это пропускная способность!

  • 5
    Вы имеете в виду указатели? .. Если я правильно понял, в public void foo(Car car){ ... } , car является локальным по отношению к foo и содержит местоположение кучи объекта? Так что, если я меняю car значение «s на car = new Car() , он будет указывать на другой объект в куче? и если я изменю значение свойства car на car.Color = "Red" , объект в куче, указанной car будет изменен. Кроме того, то же самое в C #? Ответьте, пожалуйста! Спасибо!
  • 10
    @ domanokz Ты убиваешь меня, пожалуйста, не говори это слово снова! ;) Обратите внимание, что я мог бы ответить на этот вопрос, не сказав при этом также и «ссылку». Это проблема терминологии, и с ней дела обстоят хуже. У меня и Скотта разные взгляды на это, к сожалению. Я думаю, что вы поняли, как это работает в Java, теперь вы можете называть это передачей по значению, разделением по объектам, копированием переменных, или можете свободно предлагать что-то еще! Мне все равно, пока вы понимаете, как это работает и что находится в переменной типа Object: просто адрес почтового ящика! ;)
Показать ещё 13 комментариев
570

Java всегда проходит по значению, без исключений, когда-либо.

Итак, как же все это можно смутить, и считают, что Java проходит по ссылке, или думает, что у них есть пример Java, действующий как pass by reference? Ключевым моментом является то, что Java никогда обеспечивает прямой доступ к значениям самих объектов при любых обстоятельствах. Единственный доступ к объектам - это ссылка на этот объект. Поскольку объекты Java всегда обращаются через ссылку, а не напрямую, обычно говорят о том, что поля, переменные и аргументы метода являются объектами, когда педантично они являются только ссылками на объекты. Путаница проистекает из этого (строго говоря, неправильного) изменения в номенклатуре.

Итак, при вызове метода

  • Для примитивных аргументов (int, long и т.д.) пропуск по значению является фактическим значением примитива (например, 3).
  • Для объектов передается по значению значение ссылки на объект.

Итак, если у вас есть doSomething(foo) и public void doSomething(Foo foo) { .. }, два Foos скопировали ссылки, которые указывают на одни и те же объекты.

Естественно, что передача по значению ссылки на объект очень похожа на (и на практике неотличима от), передавая объект по ссылке.

  • 7
    Поскольку значения примитивов являются неизменяемыми (например, String), разница между этими двумя случаями на самом деле не имеет значения.
  • 4
    Именно так. Для всего, что вы можете сказать через наблюдаемое поведение JVM, примитивы могут передаваться по ссылке и могут жить в куче. Они не делают, но это на самом деле не наблюдается в любом случае.
Показать ещё 8 комментариев
304

Java передает ссылки по значению.

Таким образом, вы не можете изменить ссылку, которая будет передана.

  • 26
    Но то, что постоянно повторяется: «Вы не можете изменить значение объектов, передаваемых в аргументах», явно ложно. Возможно, вы не сможете заставить их ссылаться на другой объект, но вы можете изменить их содержимое, вызвав их методы. ИМО это означает, что вы теряете все преимущества ссылок и не получаете никаких дополнительных гарантий.
  • 32
    Я никогда не говорил: «Вы не можете изменить значение объектов, переданных в аргументах». Я скажу «Вы не можете изменить значение ссылки на объект, переданной в качестве аргумента метода», что является истинным утверждением о языке Java. Очевидно, что вы можете изменить состояние объекта (если оно не является неизменным).
Показать ещё 8 комментариев
210

Мне кажется, что спорить о "pass-by-reference vs pass-by-value" не очень полезно.

Если вы скажете: "Java - это pass-by-whatever (reference/value)", в любом случае вы не предоставляете полный ответ. Вот некоторая дополнительная информация, которая, надеюсь, поможет понять, что происходит в памяти.

Курс Crash в стеке/куче прежде чем мы перейдем к реализации Java: Ценности идут в стопку и складываются аккуратно, как стопка тарелок в столовой. Память в куче (также известная как динамическая память) беспорядочна и дезорганизована. JVM просто находит место, где только может, и освобождает его, поскольку переменные, которые его используют, больше не нужны.

Хорошо. Во-первых, локальные примитивы идут в стек. Итак, этот код:

int x = 3;
float y = 101.1f;
boolean amIAwesome = true;

приводит к следующему:

Изображение 1278

Когда вы объявляете и создаете экземпляр объекта. Фактический объект переходит в кучу. Что происходит в стеке? Адрес объекта в куче. Программисты на C++ назвали бы это указателем, но некоторые разработчики Java против слова "указатель". Без разницы. Просто знайте, что адрес объекта идет в стек.

Так же:

int problems = 99;
String name = "Jay-Z";

Изображение 1279

Массив - это объект, поэтому он также находится в куче. А как насчет объектов в массиве? Они получают свое собственное пространство кучи, и адрес каждого объекта входит в массив.

JButton[] marxBros = new JButton[3];
marxBros[0] = new JButton("Groucho");
marxBros[1] = new JButton("Zeppo");
marxBros[2] = new JButton("Harpo");

Изображение 1280

Итак, что передается, когда вы вызываете метод? Если вы передаете объект, то, что вы фактически передаете, является адресом объекта. Некоторые могут сказать "значение" адреса, а некоторые говорят, что это просто ссылка на объект. Это генезис священной войны между "ссылочными" и "ценностными" сторонниками. То, что вы называете, не так важно, как вы понимаете, что то, что передается, - это адрес объекта.

private static void shout(String name){
    System.out.println("There goes " + name + "!");
}

public static void main(String[] args){
    String hisName = "John J. Jingleheimerschmitz";
    String myName = hisName;
    shout(myName);
}

Создается одна строка, а пространство для нее выделяется в куче, а адрес строки сохраняется в стеке и присваивается идентификатор hisName, так как адрес второй строки такой же, как и первый, не создается новая String и не выделяется новое место кучи, но в стек создается новый идентификатор. Затем мы вызываем shout(): создается новый стек стека и создается новый идентификатор name и назначается адрес уже существующей строки.

Изображение 1281

Итак, значение, ссылка? Вы говорите "картофель".

  • 6
    Однако вам следовало бы привести более сложный пример, где появляется функция для изменения переменной, на адрес которой она имеет ссылку.
  • 33
    Люди не "танцуют вокруг реальной проблемы" стека против кучи, потому что это не настоящая проблема. В лучшем случае это деталь реализации, а в худшем - совершенно неверная. (Для объектов вполне возможно жить в стеке; Google "escape-анализ". И огромное количество объектов содержит примитивы, которые, вероятно , не живут в стеке.) Реальная проблема заключается именно в разнице между ссылочными типами и типами значений - в частности, что значение переменной ссылочного типа является ссылкой, а не объектом, на который она ссылается.
Показать ещё 13 комментариев
180

Чтобы показать контраст, сравните следующие C++ и Java фрагменты:

В С++: Примечание. Плохой код - утечка памяти! Но это демонстрирует точку.

void cppMethod(int val, int &ref, Dog obj, Dog &objRef, Dog *objPtr, Dog *&objPtrRef)
{
    val = 7; // Modifies the copy
    ref = 7; // Modifies the original variable
    obj.SetName("obj"); // Modifies the copy of Dog passed
    objRef.SetName("objRef"); // Modifies the original Dog passed
    objPtr->SetName("objPtr"); // Modifies the original Dog pointed to 
                               // by the copy of the pointer passed.
    objPtr = new Dog("newObjPtr");  // Modifies the copy of the pointer, 
                                   // leaving the original object alone.
    objPtrRef->SetName("objRefPtr"); // Modifies the original Dog pointed to 
                                    // by the original pointer passed. 
    objPtrRef = new Dog("newObjPtrRef"); // Modifies the original pointer passed
}

int main()
{
    int a = 0;
    int b = 0;
    Dog d0 = Dog("d0");
    Dog d1 = Dog("d1");
    Dog *d2 = new Dog("d2");
    Dog *d3 = new Dog("d3");
    cppMethod(a, b, d0, d1, d2, d3);
    // a is still set to 0
    // b is now set to 7
    // d0 still have name "d0"
    // d1 now has name "objRef"
    // d2 now has name "objPtr"
    // d3 now has name "newObjPtrRef"
}

В Java,

public static void javaMethod(int val, Dog objPtr)
{
   val = 7; // Modifies the copy
   objPtr.SetName("objPtr") // Modifies the original Dog pointed to 
                            // by the copy of the pointer passed.
   objPtr = new Dog("newObjPtr");  // Modifies the copy of the pointer, 
                                  // leaving the original object alone.
}

public static void main()
{
    int a = 0;
    Dog d0 = new Dog("d0");
    javaMethod(a, d0);
    // a is still set to 0
    // d0 now has name "objPtr"
}

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

  • 7
    +1 Я бы также добавил Dog **objPtrPtr в пример C ++, чтобы мы могли изменить то, на что «указывает» указатель.
  • 0
    Это один из лучших ответов, которые я видел до сих пор. Он в основном избегает нерелевантного семантического аргумента «ссылка на значение ссылки», представленного в другом месте, и вместо этого обсуждает механизм того, что на самом деле происходит. Я должен был дать большинство других «проходных значений» ответов -1 из-за их противоречивого или фактически ложного содержания, но вместо этого этот получает +1.
Показать ещё 2 комментария
159

Java передает ссылки на объекты по значению.

145

В принципе, переназначение параметров объекта не влияет на аргумент, например,

private void foo(Object bar) {
    bar = null;
}

public static void main(String[] args) {
    String baz = "Hah!";
    foo(baz);
    System.out.println(baz);
}

распечатает "Hah!" вместо null. Причина, по которой это работает, заключается в том, что bar является копией ценности baz, которая является просто ссылкой на "Hah!" , Если бы это была фактическая ссылка, то foo переопределил бы baz на null.

  • 8
    Я бы скорее сказал, что bar является копией ссылочной базы baz (или псевдонима baz), которая изначально указывает на один и тот же объект.
  • 0
    не немного ли отличается между классом String и всеми другими классами?
136

Не могу поверить, что никто еще не упомянул Барбару Лисков. Когда она разработала CLU в 1974 году, она столкнулась с этой же проблемой терминологии, и она придумала термин "вызов" путем совместного использования (также называемого вызовом путем совместного использования объектов и вызова по объекту) для этого конкретного случая "вызов по значению, где значение ссылку".

  • 2
    Мне нравится это различие в номенклатуре. К сожалению, Java поддерживает вызовы с помощью общего доступа для объектов, но не вызов по значению (как это делает C ++). Java поддерживает вызов по значению только для примитивных типов данных, а не для составных типов данных.
  • 2
    Я действительно не думаю, что нам нужен был дополнительный термин - это просто передача по значению для определенного типа значения. Будет ли добавление «вызов по примитиву» добавить какие-либо разъяснения?
Показать ещё 2 комментария
98

Суть в том, что ссылка слова в выражении "пройти по ссылке" означает нечто совершенно отличное от обычного значения ссылки на слова в Java.

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

  • 10
    В разговорной речи называется указатель.
  • 7
    @Gevorg - Тогда что такое «NullPointerException»?
Показать ещё 3 комментария
79

В java все ссылается, поэтому, когда у вас есть что-то вроде:    Point pnt1 = new Point(0,0); Java выполняет следующие действия:

  • Создает новый объект Point
  • Создает новую ссылку на точку и инициализирует эту ссылку на точку (см.) на ранее созданном объекте Point.
  • Отсюда, через жизнь объекта Point, вы получите доступ к этому объекту через pnt1  Справка. Поэтому мы можем сказать, что в Java вы манипулируете объектом через его ссылку.

Изображение 1282

Java не передает аргументы метода по ссылке; он передает их по значению. Я буду использовать пример из этого сайта:

public static void tricky(Point arg1, Point arg2) {
  arg1.x = 100;
  arg1.y = 100;
  Point temp = arg1;
  arg1 = arg2;
  arg2 = temp;
}
public static void main(String [] args) {
  Point pnt1 = new Point(0,0);
  Point pnt2 = new Point(0,0);
  System.out.println("X1: " + pnt1.x + " Y1: " +pnt1.y); 
  System.out.println("X2: " + pnt2.x + " Y2: " +pnt2.y);
  System.out.println(" ");
  tricky(pnt1,pnt2);
  System.out.println("X1: " + pnt1.x + " Y1:" + pnt1.y); 
  System.out.println("X2: " + pnt2.x + " Y2: " +pnt2.y);  
}

Поток программы:

Point pnt1 = new Point(0,0);
Point pnt2 = new Point(0,0);

Создание двух разных объектов Point с двумя связанными ссылками. Изображение 1283

System.out.println("X1: " + pnt1.x + " Y1: " +pnt1.y); 
System.out.println("X2: " + pnt2.x + " Y2: " +pnt2.y);
System.out.println(" ");

Как ожидалось, выход будет:

X1: 0     Y1: 0
X2: 0     Y2: 0

В этой строке "pass-by-value" переходит в игру...

tricky(pnt1,pnt2);           public void tricky(Point arg1, Point arg2);

Ссылки pnt1 и pnt2 являются переданы значением для сложного метода, что означает, что теперь ваши ссылки pnt1 и pnt2 имеют свои copies с именем arg1 и arg2.So pnt1 и arg1 указывает на тот же объект. (То же самое для pnt2 и arg2) Изображение 1284

В методе tricky:

 arg1.x = 100;
 arg1.y = 100;

Изображение 1285

Далее в методе tricky

Point temp = arg1;
arg1 = arg2;
arg2 = temp;

Здесь вы сначала создаете новую ссылку temp Point, которая укажет на то же место, что и ссылка arg1. Затем вы перемещаете ссылку arg1, чтобы указать на то же место, что и ссылка arg2. Наконец arg2 будет указывать на то же место, что и temp.

Изображение 1286

Отсюда область применения метода tricky исчезла, и вы больше не имеете доступа к ссылкам: arg1, arg2, temp. Но важно отметить, что все, что вы делаете с этими ссылками, когда они находятся в жизни, будет постоянно влиять на объект, на который они указывают.

Итак, после выполнения метода tricky, когда вы вернетесь в main, у вас возникнет такая ситуация: Изображение 1287

Итак, теперь полное выполнение программы будет:

X1: 0         Y1: 0
X2: 0         Y2: 0
X1: 100       Y1: 100
X2: 0         Y2: 0
  • 7
    В Java, когда вы говорите «В Java все ссылки», вы подразумеваете, что все объекты передаются по ссылке. Примитивные типы данных не передаются по ссылке.
  • 0
    Я могу напечатать замененное значение в основном методе. в методе trickey добавьте следующий оператор arg1.x = 1; arg1.y = 1; arg2.x = 2; arg2.y = 2; Итак, поскольку arg1 теперь содержит ссылку на pnt2, а arg2 держит теперь ссылку на pnt1, значит, его печать X1: 2 Y1: 2 X2: 1 Y2: 1
68

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

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

Данные в памяти имеют Местоположение, и в этом месте есть значение (байт, слово, что угодно). В Assembly мы имеем удобное решение для предоставления Name для определенного Location (aka variable), но при компиляции кода ассемблер просто заменяет Name > с назначенным местоположением так же, как ваш браузер заменяет имена доменов IP-адресами.

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

Предположим, что у нас есть переменная Foo, ее Местоположение находится в 47-м байте в памяти, а ее Значение равно 5. У нас есть другая переменная Ref2Foo strong > , который находится в памяти 223 байта, а его значение будет 47. Этот Ref2Foo может быть технической переменной, явно не созданной программой. Если вы просто посмотрите на 5 и 47 без какой-либо другой информации, вы увидите только две Значения. Если вы используете их в качестве ссылок, то для достижения 5 нам нужно путешествовать:

(Name)[Location] -> [Value at the Location]
---------------------
(Ref2Foo)[223]  -> 47
(Foo)[47]       -> 5

Вот как работают прыжки-таблицы.

Если мы хотим вызвать метод/функцию/процедуру с помощью значения Foo, существует несколько возможных способов передать переменную методу в зависимости от режима language и нескольких способов его вызова:

  • 5 копируется в один из регистров CPU (например, EAX).
  • 5 получает PUSHd в стек.
  • 47 копируется в один из регистров CPU
  • 47 PUSHd в стек.
  • 223 копируется в один из регистров CPU.
  • 223 получает PUSHd в стек.

В каждом случае выше значение - копия существующего значения - было создано, теперь до метода приема обрабатывается. Когда вы пишете "Foo" внутри метода, он либо считывается из EAX, либо автоматически разыменовывается или дважды разыменован, процесс зависит от того, как работает язык и/или какой тип Foo диктует, Это скрыто от разработчика, пока она не оборвет процесс разыменования. Таким образом, ссылка представляет собой значение, когда оно представлено, потому что ссылка - это значение, которое необходимо обработать (на уровне языка).

Теперь мы прошли Foo к методу:

  • в случае 1 и 2. если вы меняете Foo (Foo = 9), это влияет только на локальную область, поскольку у вас есть копия значения. Изнутри метода мы даже не можем определить, где находится память оригинального Foo.
  • в случае 3. и 4. если вы используете конструкторы языка по умолчанию и меняете Foo (Foo = 11), это может изменить Foo глобально (зависит от языка, то есть Java или как Pascal procedure findMin(x, y, z: integer; var m : integer);). Однако, если язык позволяет обойти процесс разыменования, вы можете изменить 47, скажем, на 49. В этот момент Foo, похоже, был изменен, если вы его прочитали, потому что вы изменили на него локальный указатель. И если вы должны изменить этот Foo внутри метода (Foo = 12), вы, вероятно, будете FUBAR для выполнения программы (aka. Segfault), потому что вы будете писать в другую память, чем ожидалось, вы даже можете изменить область, которая предназначена для выполнения исполняемой программы и записи на нее будет изменен код запуска (Foo теперь не в 47). BUT Значение Foo 47 не изменилось глобально, а только один внутри метода, потому что 47 также был копией метода.
  • в случае 5 и 6. если вы изменяете 223 внутри метода, он создает тот же хаос, что и в 3. или 4. (указатель, указывающий на теперь плохое значение, которое снова используется как указатель) но это все еще локальная проблема, так как 223 был скопирован. Однако, если вы можете разыменовать Ref2Foo (то есть 223), достигните и измените указанное значение 47, скажем, на 49, это повлияет на Foo глобально, потому что в этом случае методы получили копию 223, но ссылочный 47 существует только один раз, и изменение этого значения на 49 приведет к неправильному значению каждого двойного разыменования Ref2Foo.

Отбрасывая незначительные детали, даже языки, которые передают по ссылке, передают значения функциям, но эти функции знают, что они должны использовать его для разыменования. Этот параметр pass-the-reference-as просто скрыт от программиста, потому что он практически бесполезен, а терминология - только по ссылке.

Строгое значение pass-by-value также бесполезно, это означало бы, что массив 100 Мбайт должен быть скопирован каждый раз, когда мы вызываем метод с массивом в качестве аргумента, поэтому Java не может быть строго переданным по значению. Каждый язык передавал бы ссылку на этот огромный массив (как значение) и использовал механизм copy-on-write, если этот массив может быть локально изменен внутри метода или позволяет использовать метод (как Java) для изменения массива глобально (от представление вызывающего абонента), а несколько языков позволяют изменять значение самой ссылки.

Итак, вкратце и в собственной терминологии Java, Java имеет значение pass-by-value, где значение может быть: либо реальное значение, либо значение, которое представляет собой представление a ссылка.

  • 1
    В языке с передачей по ссылке вещь, которая передается (ссылка), эфемерна; получатель не должен «копировать» его. В Java передача массива передается является «идентификатором объекта» - эквивалентно листку бумаги, на котором написано «Объект № 24601», когда созданный 24601-й объект был массивом. Получатель может скопировать «Объект № 24601» куда угодно, и любой, у кого есть бумажка с надписью «Объект №24601», может сделать все, что захочет, с любым из элементов массива. Шаблон битов, который передается, на самом деле не скажет «Объект № 24601», но ...
  • 0
    ... ключевой момент заключается в том, что получатель идентификатора объекта, который идентифицирует массив, может хранить этот идентификатор объекта в любом месте и передавать его тому, кто хочет, и любой получатель сможет получить доступ к массиву или изменить его всякий раз, когда хочет. Напротив, если массив передавался по ссылке в языке, подобном Pascal, который поддерживает такие вещи, вызываемый метод мог делать с массивом все, что ему нужно, но не мог сохранять ссылку таким образом, чтобы позволить коду изменять массив после того как он вернулся.
Показать ещё 3 комментария
66

Java всегда проходит по значению, а не по ссылке

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

Передача по значению означает, что вы делаете копию в памяти фактического значения параметра, которое передается. Это копия содержимого фактического параметра.

Передача по ссылке (также называемая pass by address) означает, что сохраняется копия адреса фактического параметра.

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

public class PassByValue {
    public static void main(String[] args) {
        Test t = new Test();
        t.name = "initialvalue";
        new PassByValue().changeValue(t);
        System.out.println(t.name);
    }

    public void changeValue(Test f) {
        f.name = "changevalue";
    }
}

class Test {
    String name;
}

Результатом этой программы является:

changevalue

Дайте понять шаг за шагом:

Test t = new Test();

Как мы все знаем, он создаст объект в куче и вернет исходное значение обратно в t. Например, предположим, что значение t равно 0x100234 (мы не знаем фактического внутреннего значения JVM, это просто пример).

Изображение 1288

new PassByValue().changeValue(t);

При прохождении ссылочного т к функции не будет непосредственно передавать фактическое значение задания теста объекта, но это создаст копию т, а затем передать его в функцию. Поскольку он передается по значению, он передает копию переменной, а не фактическую ссылку на нее. Поскольку мы сказали, что значение t 0x100234, оба t и f будут иметь одинаковое значение и, следовательно, они будут указывать на один и тот же объект.

Изображение 1289

Если вы измените что-либо в функции с помощью ссылки f, оно изменит существующее содержимое объекта. Вот почему мы получили значение changevalue, которое обновляется в функции.

Чтобы понять это более четко, рассмотрим следующий пример:

public class PassByValue {
    public static void main(String[] args) {
        Test t = new Test();
        t.name = "initialvalue";
        new PassByValue().changeRefence(t);
        System.out.println(t.name);
    }

    public void changeRefence(Test f) {
        f = null;
    }
}

class Test {
    String name;
}

Это NullPointerException? Нет, потому что он только передает копию справки. В случае прохождения по ссылке он мог бы вызвать NullPointerException, как показано ниже:

Изображение 1290

Надеюсь, это поможет.

52

Нет, он не проходит по ссылке.

Java передается по значению в соответствии со спецификацией Java Language:

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

48

Java - это вызов по значению.

Как это работает

  • Вы всегда передаете копию бит значения ссылки!

  • Если это примитивный тип данных, эти биты содержат значение самого примитивного типа данных. Поэтому, если мы изменим значение заголовка внутри метода, то оно не отражает изменения вне.

  • Если это тип данных объекта, например Foo foo = new Foo(), то в этом случае копия адреса объекта проходит как ярлык файла, предположим, что у нас есть текстовый файл abc.txt на C:\desktop и предположим, что мы делаем ярлык тот же файл и поместите это внутри C:\desktop\abc-shortcut, поэтому, когда вы получаете доступ к файлу из C:\desktop\abc.txt и пишите 'Stack Overflow' и закрываете файл, и снова вы открываете файл из ярлыка, тогда вы write 'является крупнейшим онлайн-сообществом для программистов, чтобы узнать, что тогда полное изменение файла будет "Qaru - это самое большое онлайн-сообщество для программистов, чтобы учиться", что означает, что не имеет значения, откуда вы открываете файл, каждый раз, когда мы обращались к нему тот же файл, здесь мы можем считать Foo в качестве файла и предположим, что foo хранится на адресе 123hd7h (исходный адрес, например C:\desktop\abc.txt) и 234jdid (скопированный адрес, такой как C:\desktop\abc-shortcut, который фактически содержит исходный адрес файла внутри). Поэтому для лучшего понимания создайте ярлык и почувствуйте...

48

Насколько я знаю, Java знает только вызов по значению. Это означает, что для примитивных типов данных вы будете работать с копией и для объектов, которые будут работать с копией ссылки на объекты. Однако я думаю, что есть некоторые подводные камни; например, это не сработает:

public static void swap(StringBuffer s1, StringBuffer s2) {
    StringBuffer temp = s1;
    s1 = s2;
    s2 = temp;
}


public static void main(String[] args) {
    StringBuffer s1 = new StringBuffer("Hello");
    StringBuffer s2 = new StringBuffer("World");
    swap(s1, s2);
    System.out.println(s1);
    System.out.println(s2);
}

Это заполнит Hello World, а не World Hello, потому что в функции swap вы используете копии, которые не влияют на ссылки в main. Но если ваши объекты не являются неизменными, вы можете его изменить, например:

public static void appendWorld(StringBuffer s1) {
    s1.append(" World");
}

public static void main(String[] args) {
    StringBuffer s = new StringBuffer("Hello");
    appendWorld(s);
    System.out.println(s);
}

Это запустит Hello World в командной строке. Если вы измените StringBuffer на String, он произведет только Hello, потому что String неизменен. Например:

public static void appendWorld(String s){
    s = s+" World";
}

public static void main(String[] args) {
    String s = new String("Hello");
    appendWorld(s);
    System.out.println(s);
}

Однако вы можете сделать оболочку для String, подобную этой, которая позволила бы ей использовать ее со строками:

class StringWrapper {
    public String value;

    public StringWrapper(String value) {
        this.value = value;
    }
}

public static void appendWorld(StringWrapper s){
    s.value = s.value +" World";
}

public static void main(String[] args) {
    StringWrapper s = new StringWrapper("Hello");
    appendWorld(s);
    System.out.println(s.value);
}

edit: я считаю, что это также причина использования StringBuffer, когда дело доходит до "добавления" двух строк, потому что вы можете модифицировать исходный объект, который не может быть с неизменяемыми объектами, такими как String.

  • 0
    +1 для теста подкачки - возможно, самый простой и удобный способ различить передачу по ссылке и передачу ссылки по значению . Если вы можете легко написать функцию swap(a, b) которая (1) меняет местами a и b из POV вызывающей стороны, (2) не зависит от типа в той степени, в которой позволяет статическая типизация (то есть использование ее с другим типом больше ничего не требует чем изменение объявленных типов a и b ), и (3) не требует, чтобы вызывающая сторона явно передавала указатель или имя, тогда язык поддерживает передачу по ссылке.
  • 0
    «... для примитивных типов данных вы будете работать с копией, а для объектов вы будете работать с копией ссылки на объекты» - отлично написано!
46

Позвольте мне попытаться объяснить свое понимание с помощью четырех примеров. Java передается по значению, а не по ссылке

/**

Передача по значению

В Java все параметры передаются по значению, т.е. назначение аргумента метода не отображается вызывающему.

*/

Пример 1:

public class PassByValueString {
    public static void main(String[] args) {
        new PassByValueString().caller();
    }

    public void caller() {
        String value = "Nikhil";
        boolean valueflag = false;
        String output = method(value, valueflag);
        /*
         * 'output' is insignificant in this example. we are more interested in
         * 'value' and 'valueflag'
         */
        System.out.println("output : " + output);
        System.out.println("value : " + value);
        System.out.println("valueflag : " + valueflag);

    }

    public String method(String value, boolean valueflag) {
        value = "Anand";
        valueflag = true;
        return "output";
    }
}

Результат

output : output
value : Nikhil
valueflag : false

Пример 2:

/**  *  * Пройти по значению  *  */

public class PassByValueNewString {
    public static void main(String[] args) {
        new PassByValueNewString().caller();
    }

    public void caller() {
        String value = new String("Nikhil");
        boolean valueflag = false;
        String output = method(value, valueflag);
        /*
         * 'output' is insignificant in this example. we are more interested in
         * 'value' and 'valueflag'
         */
        System.out.println("output : " + output);
        System.out.println("value : " + value);
        System.out.println("valueflag : " + valueflag);

    }

    public String method(String value, boolean valueflag) {
        value = "Anand";
        valueflag = true;
        return "output";
    }
}

Результат

output : output
value : Nikhil
valueflag : false

Пример 3:

/** У этого "Pass By Value" есть ощущение "Pass By Reference"

Некоторые люди говорят, что примитивные типы и "String" являются "pass by value" и объекты "проходят по ссылке".

Но из этого примера мы можем понять, что это infact pass только по значению, имея в виду, что здесь мы передаем ссылку как значение. т.е.: ссылка передается по значению. Вот почему они могут меняться, и все же это верно после локального масштаба. Но мы не можем изменить фактическую ссылку за пределами исходной области. что это означает, демонстрируется в следующем примере PassByValueObjectCase2.

*/

public class PassByValueObjectCase1 {

    private class Student {
        int id;
        String name;
        public Student() {
        }
        public Student(int id, String name) {
            super();
            this.id = id;
            this.name = name;
        }
        public int getId() {
            return id;
        }
        public void setId(int id) {
            this.id = id;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        @Override
        public String toString() {
            return "Student [id=" + id + ", name=" + name + "]";
        }
    }

    public static void main(String[] args) {
        new PassByValueObjectCase1().caller();
    }

    public void caller() {
        Student student = new Student(10, "Nikhil");
        String output = method(student);
        /*
         * 'output' is insignificant in this example. we are more interested in
         * 'student'
         */
        System.out.println("output : " + output);
        System.out.println("student : " + student);
    }

    public String method(Student student) {
        student.setName("Anand");
        return "output";
    }
}

Результат

output : output
student : Student [id=10, name=Anand]

Пример 4:

/**

В дополнение к тому, что упоминалось в примере 3 (PassByValueObjectCase1.java), мы не можем изменить фактическую ссылку за пределами исходной области. "

Примечание. Я не вставляю код для private class Student. Определение класса для Student такое же, как в примере 3.

*/

public class PassByValueObjectCase2 {

    public static void main(String[] args) {
        new PassByValueObjectCase2().caller();
    }

    public void caller() {
        // student has the actual reference to a Student object created
        // can we change this actual reference outside the local scope? Let see
        Student student = new Student(10, "Nikhil");
        String output = method(student);
        /*
         * 'output' is insignificant in this example. we are more interested in
         * 'student'
         */
        System.out.println("output : " + output);
        System.out.println("student : " + student); // Will it print Nikhil or Anand?
    }

    public String method(Student student) {
        student = new Student(20, "Anand");
        return "output";
    }

}

Результат

output : output
student : Student [id=10, name=Nikhil]
44

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

void getValues(int& arg1, int& arg2) {
    arg1 = 1;
    arg2 = 2;
}
void caller() {
    int x;
    int y;
    getValues(x, y);
    cout << "Result: " << x << " " << y << endl;
}

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

void getValues(int[] arg1, int[] arg2) {
    arg1[0] = 1;
    arg2[0] = 2;
}
void caller() {
    int[] x = new int[1];
    int[] y = new int[1];
    getValues(x, y);
    System.out.println("Result: " + x[0] + " " + y[0]);
}

Как было объяснено в предыдущих ответах, в Java вы передаете указатель на массив как значение в getValues. Этого достаточно, потому что метод затем модифицирует элемент массива, и по соглашению вы ожидаете, что элемент 0 будет содержать возвращаемое значение. Очевидно, вы можете сделать это другими способами, такими как структурирование вашего кода, чтобы это не было необходимо, или создание класса, который может содержать возвращаемое значение или разрешить его установку. Но простой шаблон, доступный вам в С++ выше, недоступен в Java.

41

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

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

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

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

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

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

И по вопросу пропущенного значения

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

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

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

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

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

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

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

В Спецификация языка Java также указано

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

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

Итак,

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

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

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

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

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


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

  • 0
    Спасибо, что начали с обсуждения определений терминов. Это отличает ваш ответ от толпы и необходимо для правильного понимания.
33

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

  • 2
    Я нахожу ваше предпоследнее предложение очень вводящим в заблуждение. Это не правда, что «все объекты в Java являются ссылками». Это только ссылки на те объекты, которые являются ссылками.
31

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

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

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

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

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

"String" Объекты кажутся идеальным встречным примером для городской легенды, говорящей, что "Объекты передаются по ссылке":

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

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

  • 0
    Таким образом, это byRef в отношении объектов и byVal в отношении примитивов?
  • 0
    @mox, пожалуйста, прочитайте: Объекты не передаются по ссылке, это уступка: String a = new String ("неизмененный");
Показать ещё 6 комментариев
31

Как уже упоминалось ранее, Java всегда имеет значение pass-by-value

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

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;
  }
}

Отпечатки:

До: a = 2, b = 3
После: a = 2, b = 3

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

  • 0
    я понял с этим ответом
30

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();
}
  • 4
    Это самый простой и простой способ увидеть, что Java проходит по значению. Значение obj ( null ) было передано init , а не ссылка на obj .
30

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

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
name= Fido

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

  • 3
    «передача по копии» - это то, что означает передача по значению.
24

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

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

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

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

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

  • 1
    +1. Что C делает поддержку, обрабатывают ссылки (которые C вызывает указатели) в качестве значений первого класса, а затем передать их по значению.
  • 1
    Да, в C вы можете создавать указатели не только на объекты, но и на произвольные переменные - так что вы можете легко смоделировать «вызов по ссылке» любой переменной. В Java это работает только для переменных, которые имеют окружающий объект, который вы можете дать.
23

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

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

  • Java передает свои параметры по значению
  • "по значению" - единственный способ в java передать параметр методу
  • использование методов из объекта, заданного как параметр, изменит объект в качестве точки ссылки на исходные объекты. (если это сам метод изменяет некоторые значения)
22

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

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

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

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

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

Java передает параметры по VALUE и по значению ТОЛЬКО.

Сокращение длинного рассказа:

Для тех, кто приходит из С#: НЕТ параметра "out".

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

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

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

  • 0
    Я думаю, что вы прибили это, сказав, что you can't change the reference from the object itself
  • 0
    Таким образом, объект может быть изменен с помощью его открытых интерфейсов, но не может быть заменен на что-либо другое, то есть почтение, и благодаря этому Java достигает лучшего из обоих миров
20

В течение всех ответов мы видим, что Java передается по значению или, скорее, как @Gevorg писал: "pass-by-copy-of-the-variable-value", и это идея, которую мы должны иметь в виду все время.

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

Из [1] В Java вы всегда передаете аргументы копией; то есть вы всегда создаете новый экземпляр значения внутри функции. Но есть определенные виды поведения, которые могут заставить вас думать, что вы проходите по ссылке.

  • Передача по копии: когда переменная передается методу/функции, создается копия (иногда мы слышим, что при передаче примитивов вы делаете копии).

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

Примеры передачи по копии/по значению

Пример из [ref 1]

void incrementValue(int inFunction){
  inFunction ++;
  System.out.println("In function: " + inFunction);
}

int original = 10;
System.out.print("Original before: " + original);
incrementValue(original);
System.out.println("Original after: " + original);

We see in the console:
 > Original before: 10
 > In Function: 11
 > Original after: 10 (NO CHANGE)

Пример из [ref 2]

хорошо показывает механизм смотреть максимум 5 минут

(Передача по ссылке) pass-by-copy-of-the-variable-value

Пример из [ref 1] (помните, что массив является объектом)

void incrementValu(int[] inFuncion){
  inFunction[0]++;
  System.out.println("In Function: " + inFunction[0]);
}

int[] arOriginal = {10, 20, 30};
System.out.println("Original before: " + arOriginal[0]);
incrementValue(arOriginal[]);
System.out.println("Original before: " + arOriginal[0]);

We see in the console:
  >Original before: 10
  >In Function: 11
  >Original before: 11 (CHANGE)

Сложные объекты сами копируются, но внутренние ссылки сохраняются.

Пример из [ref 3]

package com.pritesh.programs;

class Rectangle {
  int length;
  int width;

  Rectangle(int l, int b) {
    length = l;
    width = b;
  }

  void area(Rectangle r1) {
    int areaOfRectangle = r1.length * r1.width;
    System.out.println("Area of Rectangle : " 
                            + areaOfRectangle);
  }
}

class RectangleDemo {
  public static void main(String args[]) {
    Rectangle r1 = new Rectangle(10, 20);
    r1.area(r1);
  }
}

Площадь прямоугольника равна 200, а длина = 10 и ширина = 20

Последняя вещь. Я хотел бы поделиться этим моментом лекции: Распределение памяти который я нашел очень полезным в понимании передачи Java по значению или, скорее, "pass-by-copy-of-the-variable-value", как написал @Gevorg.

  • 0
    Говорят, что Java по умолчанию передается по ссылке. обычно, когда изменяемый объект передается некоторым методам, любые изменения, сделанные с этой ссылкой на объект, будут отражаться и на других частях, используя ту же ссылку на объект. Но в случае неизменного объекта, если ссылка переназначена на некоторые другие объекты, то она не будет отражена на других частях, будут созданы совершенно новые объекты.
  • 1
    @ShailendraSingh Java никогда не проходит по ссылке, точка. См. Ответ Гаурава для ясного доказательства этого.
Показать ещё 1 комментарий
18

Это действительно довольно, довольно просто:

Для переменной примитивного типа (например, int, boolean, char и т.д.)), когда вы используете его имя для аргумента метода, вы передаете значение, содержащееся в нем (5, true или 'c'). Это значение получает "скопировано", и переменная сохраняет свое значение даже после вызова метода.

Для переменной ссылочного типа (например, String, Object и т.д.)), когда вы используете свое имя для аргумента метода, вы передаете значение, содержащееся в нем (ссылка значение, которое "указывает" на объект). Это опорное значение получает "скопированы", а переменная сохраняет свое значение даже после вызова метода. Контрольная переменная сохраняет "указание" на тот же объект.

В любом случае, вы всегда передаете материал по значению.


Сравните это, чтобы сказать С++, где вы можете иметь метод для принятия int& или на С#, где вы могли бы взять ref int (хотя в этом случае вам также необходимо использовать модификатор ref при передаче имени переменной в метод.)

  • 0
    Также следует помнить, что String S неизменны в Java.
  • 0
    Я считаю полезным думать о переменных типа класса как о «идентификаторах объектов». Строковое представление объектов Java по умолчанию хорошо работает с таким описанием. Если установить Foo в «Car # 1234» и скопировать Foo в Bar , то и Foo и Bar будут содержать «Car # 1234». Foo.SetColor(Colors.Blue) раскрасит «Автомобиль № 1234» в синий цвет. Другое описание, которое я использую для таких мест хранения, - «разнородные ссылки на объекты», поскольку код, который передает такую ссылку на объект, не может контролировать, как получатель может поделиться им.
Показать ещё 1 комментарий
18

Java копирует ссылку по значению. Поэтому, если вы измените его на что-то еще (например, используя new), ссылка не изменится вне метода. Для нативных типов он всегда передается по значению.

17

Это лучший способ ответить на вопрос...

Во-первых, мы должны понимать, что в Java поведение передачи параметров...

public void foo(Object param)
{
  // some code in foo...
}

public void bar()
{
  Object obj = new Object();

  foo(obj);
}

точно так же, как...

public void bar()
{
  Object obj = new Object();

  Object param = obj;

  // some code in foo...
}

не учитывая расположение стеков, которые не имеют отношения к этому обсуждению.

Итак, на самом деле, то, что мы ищем в Java, это как работает назначение переменных. Я нашел это в документах:

Одним из наиболее распространенных операторов, с которыми вы столкнетесь, является простой оператор присваивания "=" [...], который присваивает значение справа операнду слева:

int cadence = 0;
int speed = 0;
int gear = 1;

Этот оператор может также использоваться на объектах для назначения ссылок на объекты [...]

Понятно, как этот оператор действует двумя различными способами: назначать значения и назначать ссылки. Последнее, когда это объект... первое, когда это не объект, то есть когда это примитив. Но так, можем ли мы понять, что параметры функций Java могут быть по значению и по ссылке?

Истина в коде. Давай попробуем это:

public class AssignmentEvaluation
{
  static public class MyInteger
  {
    public int value = 0;
  }

  static public void main(String[] args)
  {
    System.out.println("Assignment operator evaluation using two MyInteger objects named height and width\n");

    MyInteger height = new MyInteger();
    MyInteger width  = new MyInteger();

    System.out.println("[1] Assign distinct integers to height and width values");

    height.value = 9;
    width.value  = 1;

    System.out.println("->  height is " + height.value + " and width is " + width.value + ", we are different things! \n");

    System.out.println("[2] Assign to height value the width value");

    height.value = width.value;

    System.out.println("->  height is " + height.value + " and width is " + width.value + ", are we the same thing now? \n");

    System.out.println("[3] Assign to height value an integer other than width value");

    height.value = 9;

    System.out.println("->  height is " + height.value + " and width is " + width.value + ", we are different things yet! \n");

    System.out.println("[4] Assign to height the width object");

    height = width;

    System.out.println("->  height is " + height.value + " and width is " + width.value + ", are we the same thing now? \n");

    System.out.println("[5] Assign to height value an integer other than width value");

    height.value = 9;

    System.out.println("->  height is " + height.value + " and width is " + width.value + ", we are the same thing now! \n");

    System.out.println("[6] Assign to height a new MyInteger and an integer other than width value");

    height = new MyInteger();
    height.value = 1;

    System.out.println("->  height is " + height.value + " and width is " + width.value + ", we are different things again! \n");
  }
}

Это результат моего бега:

Assignment operator evaluation using two MyInteger objects named height and width

[1] Assign distinct integers to height and width values
->  height is 9 and width is 1, we are different things! 

[2] Assign to height value the width value
->  height is 1 and width is 1, are we the same thing now? 

[3] Assign to height value an integer other than width value
->  height is 9 and width is 1, we are different things yet! 

[4] Assign to height the width object
->  height is 1 and width is 1, are we the same thing now? 

[5] Assign to height value an integer other than width value
->  height is 9 and width is 9, we are the same thing now! 

[6] Assign to height a new MyInteger and an integer other than width value
->  height is 1 and width is 9, we are different things again! 

В [2] мы имеем разные объекты и присваиваем одно значение переменной другому. Но после присвоения нового значения в [3] объекты имели разные значения, что означает, что в [2] назначенное значение было копией примитивной переменной, обычно называемой передачей по значению, в противном случае значения выводились в [3]. ] должно быть таким же.

В [4] у нас все еще есть отдельные объекты и мы присваиваем один объект другому. И после присвоения нового значения в [5] объекты имели те же значения, что означает, что в [4] назначенный объект не был копией другого, который следует называть передачей по ссылке. Но, если мы посмотрим внимательно в [6], мы не можем быть уверены, что ни одна копия не была сделана...?????

Мы не можем быть так уверены, потому что в [6] объекты были одинаковыми, тогда мы присвоили новый объект одному из них, и после этого объекты имели разные значения! Как они могут различаться сейчас, если они были одинаковыми? Они должны быть одинаковыми и здесь! ?????

Нам нужно запомнить документы, чтобы понять, что происходит:

Этот оператор также может использоваться на объектах для назначения ссылок на объекты

Таким образом, наши две переменные хранили ссылки... наши переменные имели одну и ту же ссылку после [4] и разные ссылки после [6]... если такая возможность возможна, это означает, что присвоение объектов выполняется путем копирования объекта ссылка, в противном случае, если это не копия ссылки, напечатанное значение переменных в [6] должно быть одинаковым. Таким образом, объекты (ссылки), как и примитивы, копируются в переменные посредством присваивания, что люди обычно называют передачей по значению. Это единственный обход Java.

17

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

  • 1
    В Java нет такого понятия, как «постоянная ссылка», если программист не укажет «наконец».
  • 0
    Под константной ссылкой я имел в виду, что нет способа изменить саму ссылку, сказав в функции новый MyClass (). Если я правильно выразился, там ссылки на объекты передаются по значению, что означает, что копия ссылки передается, поэтому вы можете изменить данные, на которые ссылается эта ссылка, но вы не можете изменить их с помощью нового оператора и выделить новый объект.
Показать ещё 1 комментарий
16

Java, несомненно, без сомнения, является "pass by value". Кроме того, поскольку Java (в основном) объектно-ориентированная и объекты работают со ссылками, легко запутаться и подумать о том, что она "проходит по ссылке"

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

В Java, как правило, когда мы передаем объект методу, мы в основном передаем ссылку объекта как -значение, потому что это работает Java; он работает со ссылками и адресами, поскольку объект находится в куче.

Но чтобы проверить, действительно ли он передается по значению или передается по ссылке, вы можете использовать примитивный тип и ссылки:

@Test
public void sampleTest(){
    int i = 5;
    incrementBy100(i);
    System.out.println("passed ==> "+ i);
    Integer j = new Integer(5);
    incrementBy100(j);
    System.out.println("passed ==> "+ j);
}
/**
 * @param i
 */
private void incrementBy100(int i) {
    i += 100;
    System.out.println("incremented = "+ i);
}

Вывод:

incremented = 105
passed ==> 5
incremented = 105
passed ==> 5

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

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

@Test
public void sampleTest2(){
    Person person = new Person(24, "John");
    System.out.println(person);
    alterPerson(person);
    System.out.println(person);
}

/**
 * @param person
 */
private void alterPerson(Person person) {
    person.setAge(45);
    Person altered = person;
    altered.setName("Tom");
}

private static class Person{
    private int age;
    private String name; 

    public Person(int age, String name) {
        this.age=age;
        this.name =name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append("Person [age=");
        builder.append(age);
        builder.append(", name=");
        builder.append(name);
        builder.append("]");
        return builder.toString();
    }

}

В этом случае вывод:

Person [age=24, name=John]
Person [age=45, name=Tom]
16

Java всегда использует вызов по значению. Это означает, что метод получает копию всех значений параметров.

Рассмотрим следующие 3 ситуации:

1) Попытка изменить примитивную переменную

public static void increment(int x) { x++; }

int a = 3;
increment(a);

x скопирует значение a и будет увеличивать x, a остается тем же самым

2) Попытка изменить примитивное поле объекта

public static void increment(Person p) { p.age++; }

Person pers = new Person(20); // age = 20
increment(pers);

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

3) Попытка изменить опорное значение эталонных переменных

public static void swap(Person p1, Person p2) {
    Person temp = p1;
    p1 = p2;
    p2 = temp;
}

Person pers1 = new Person(10);
Person pers2 = new Person(20);
swap(pers1, pers2);

после вызова swap p1, p2 копирует опорные значения из pers1 и pers2, меняются со значениями, поэтому pers1 и pers2 остаются теми же

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

15

Одной из самых больших путаниц в языке программирования Java является ли Java Pass by Value или Pass by Reference.

Прежде всего, мы должны понимать, что понимается под передачей по значению или по ссылке.

Pass by Value: Значения параметров метода копируются в другую переменную, а затем скопированный объект передается, поэтому его вызов передается по значению.

Передача по ссылке: Алиас или ссылка на фактический параметр передаются методу, поэтому его вызов передается по ссылке.

Допустим, у нас есть класс Balloon, как показано ниже.

public class Balloon {

    private String color;

    public Balloon(){}

    public Balloon(String c){
        this.color=c;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }
}

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

public class Test {

    public static void main(String[] args) {

        Balloon red = new Balloon("Red"); //memory reference 50
        Balloon blue = new Balloon("Blue"); //memory reference 100

        swap(red, blue);
        System.out.println("red color="+red.getColor());
        System.out.println("blue color="+blue.getColor());

        foo(blue);
        System.out.println("blue color="+blue.getColor());

    }

    private static void foo(Balloon balloon) { //baloon=100
        balloon.setColor("Red"); //baloon=100
        balloon = new Balloon("Green"); //baloon=200
        balloon.setColor("Blue"); //baloon = 200
    }

    //Generic swap method
    public static void swap(Object o1, Object o2){
        Object temp = o1;
        o1=o2;
        o2=temp;
    }
}

Когда мы выполняем вышеуказанную программу, получаем следующий вывод.

red color=Red
blue color=Blue
blue color=Red

Если вы посмотрите на первые две строки вывода, ясно, что метод swap не работал. Это связано с тем, что Java передается по значению, этот метод swap() может использоваться с любым языком программирования, чтобы проверить, прошел ли его пропуск по значению или передается по ссылке.

Позволяет анализировать выполнение программы шаг за шагом.

Balloon red = new Balloon("Red");
Balloon blue = new Balloon("Blue");

Когда мы используем новый оператор для создания экземпляра класса, экземпляр создается, а переменная содержит ссылочное расположение памяти, в которой сохраняется объект. В нашем примере предположим, что "красный" указывает на 50, а "синий" указывает на 100, и это расположение памяти обоих объектов Balloon.

Теперь, когда мы вызываем метод swap(), создаются две новые переменные o1 и o2, указывающие соответственно на 50 и 100.

Итак, ниже фрагмент кода объясняет, что произошло при выполнении метода swap().

public static void swap(Object o1, Object o2){ //o1=50, o2=100
    Object temp = o1; //temp=50, o1=50, o2=100
    o1=o2; //temp=50, o1=100, o2=100
    o2=temp; //temp=50, o1=100, o2=50
} //method terminated

Обратите внимание, что мы меняем значения o1 и o2, но они являются копиями "красных" и "синих" опорных мест, поэтому на самом деле нет изменений в значениях "красный" и "синий" и, следовательно, выход.

Если вы это поняли, вы можете легко понять причину путаницы. Поскольку переменные являются только ссылкой на объекты, мы путаем, что мы передаем ссылку, поэтому Java передается по ссылке. Однако мы передаем копию ссылки и, следовательно, ее пропуск по значению. Надеюсь, теперь он очистит все сомнения.

Теперь проанализируйте выполнение foo().

private static void foo(Balloon balloon) { //baloon=100
    balloon.setColor("Red"); //baloon=100
    balloon = new Balloon("Green"); //baloon=200
    balloon.setColor("Blue"); //baloon = 200
}

Первая строка является важной, когда мы вызываем метод, который метод вызывается в Object в ссылочном местоположении. В этот момент баллон указывает на 100, и, следовательно, его цвет меняется на красный.

В следующей строке ссылка баллона изменяется на 200, и любые другие выполненные методы происходят на объекте в ячейке 200 памяти и не оказывают никакого влияния на объект в ячейке памяти 100. Это объясняет третью строку нашего выхода программы печать синего цвета = красный.

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

15

Сколько долгих ответов. Позвольте мне дать простой:

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

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

13

В отличие от некоторых других языков, Java не позволяет вам выбирать pass-by-value или передавать by-reference-все аргументы передаются по значению. Вызов метода может передавать два типа значений в метод-копии примитивных значений (например, значения int и double) и копии ссылок на объекты.

Когда метод изменяет параметр примитивного типа, изменения в параметре не влияют на исходное значение аргумента в вызывающем методе.

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

Как Java создает и хранит объекты. Когда мы создаем объект, мы сохраняем адрес объекта в ссылочной переменной. "Ввод сканера" - это тип и ссылочная переменная, "=" - оператор присваивания, "новый" запрашивает требуемое количество пространства из системы. Конструктор справа от ключевого слова new, который создает объект, называется неявным ключевым словом new. Адрес созданного объекта (результат правой переменной, который является выражением) присваивается левой переменной (которая является ссылочной переменной с именем и указанным типом) с использованием оператора присваивания. "new Account()" называется выражением создания экземпляра класса.

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

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

На изображении ниже вы можете увидеть, что у нас есть две контрольные переменные (они называются указателями в C/C++. И я думаю, что этот термин упрощает понимание этой функции.) В основном методе. Примитивные и опорные переменные хранятся в памяти стека (слева на рисунках ниже). Эти ссылочные переменные "точка" (как это называют программисты C/C++) или ссылки на массивы a и b, которые являются объектами (значения, которые эти ссылочные переменные содержат, являются адресами объектов) в кучевой памяти (справа на рисунках ниже).

Изображение 1291

Если мы передадим значение ссылочной переменной array1 в качестве аргумента методу reverseArray, в методе создается эталонная переменная, и эта ссылочная переменная начинает указывать на тот же массив (a).

reverseArray(array1);

...

public void reverseArray(Int[] array1)
{
   ...
}

Изображение 1292

Итак, если мы скажем

array1[0] = 5;

в методе reverseArray он внесет изменения в массив a.

У нас есть другая ссылочная переменная в методе reverseArray (array2), которая указывает на массив c. Если бы мы сказали

array1 = array2;

в методе reverseArray, тогда эталонная переменная array1 в методе reverseArray перестанет указывать на массив a и начнет указывать на массив c (пунктирная линия на втором изображении).

Если мы вернем значение ссылочной переменной array2 в качестве возвращаемого значения метода reverseArray и присвоим это значение ссылочной переменной array1 в основном методе, array1 в main начнет указывать на массив c.

Изображение 1293

Вы можете назначить значение array2 в main также для array1. array1 начнет указывать на b.

12

Java строго передается значением

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

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

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

В Java нет понятия указателей (т.е. ничего не называется переменной указателя), хотя мы можем рассматривать ссылочную переменную как указатель технически в java, которую мы называем ее как дескриптор. Причина, по которой мы вызываем указатель на адрес в качестве дескриптора в java, состоит в том, что переменная указателя способна выполнять не только разглашение, но и множественное разыменование например: int *p; в P означает p указывает на целое число и int **p; в C означает, что p является указателем на указатель на целое число у нас нет этого средства на Java, поэтому его абсолютно корректно и технически законно сказать это как дескриптор, также существуют правила для арифметики указателей в C. Это позволяет выполнять арифметическую операцию с указателями с ограничениями на нее.

В C мы называем такой механизм передачи адреса и получения его с переменными-указателями как pass by reference, поскольку мы передаем их адреса и получаем их как переменную-указатель в формальном параметре, но на уровне компилятора этот адрес копируется в переменную-указатель ( поскольку данные здесь являются адресом, а затем его данными), поэтому мы можем быть на 100% уверены, что C строго передается по значению (поскольку мы передаем только данные)

(и если мы передаем данные непосредственно в C, мы будем называть это как пропуск по значению.)

В java, когда мы делаем то же самое, мы делаем это с помощью ручек; так как они не называются переменными указателя, как в (как обсуждалось выше), хотя мы передаем ссылки, которые мы не можем сказать, его пропуск по ссылке, поскольку мы не собираем это с переменной указателя в Java.

Следовательно, Java строго использует механизм передачи по значению

12

Знание основных краеугольных камней должно быть процитированным,

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

Java: руководство для новичков, шестое издание, Герберт Шильдт

12

Посмотрите на этот код. Этот код не будет бросать NullPointerException... Он напечатает "Vinay"

public class Main {
    public static void main(String[] args) {
        String temp = "Vinay";
        print(temp);
        System.err.println(temp);
    }

    private static void print(String temp) {
        temp = null;
    }
}

Если Java передается по ссылке, тогда он должен был сбросить NullPointerException, поскольку для ссылки задано значение Null.

  • 0
    Статика отбрасывает это. открытый класс Main {public static void main (String [] args) {Main m = new Main (); m.aaa (); } public void aaa () {String temp = "Vinay"; печать (температура); System.err.println (температура); } private void print (String temp) {temp = null; }} приведенный выше код не является NPE.
  • 0
    Не получил то, что вы пытаетесь сказать ..?
Показать ещё 1 комментарий
11

Простой тест, чтобы проверить, поддерживает ли язык pass-by-reference, просто написать традиционный обмен. Можете ли вы написать традиционный метод/функцию swap (a, b) в Java?

Традиционный метод или функция свопинга принимает два аргумента и свопирует их так, что переменные, переданные в функцию, изменяются вне функции. Его основная структура выглядит как

(Non-Java) Основная структура функции свопинга

swap(Type arg1, Type arg2) {
    Type temp = arg1;
    arg1 = arg2;
    arg2 = temp;
}

Если вы можете написать такой метод/функцию на своем языке, чтобы вызов

Type var1 = ...;
Type var2 = ...;
swap(var1,var2);

фактически переключает значения переменных var1 и var2, язык поддерживает pass-by-reference. Но Java не позволяет такую ​​вещь, поскольку она поддерживает передачу только значений, а не указателей или ссылок.

  • 0
    Хороший ответ. Красиво объяснил.
  • 0
    Вы можете уточнить ваше последнее предложение. Моя первая реакция на «передачу только значений, а не указателей ...» заключается в том, что ваша реализация Java, вероятно, делает именно это , передает указатель. Тот факт, что вы не можете разыменовать этот указатель, не имеет значения.
9

Я понимаю, что это на самом деле очень просто:

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

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

Однако изменение свойства объекта, на которое ссылается переменная, изменяет базовый объект.

8

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

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

В приведенном ниже println(sb_actual) вызов println(sb_actual) печатает "Foo", а не "Bar", потому что присвоение sb_formal в addFoo() не изменяет переменную вызывающего абонента sb_actual.

void addFoo(StringBuffer sb_formal) {
    sb_formal.append("Foo");           // Modifies the object created by the caller.
    sb_formal = new StringBuffer();    // Creates a new object.
    sb_formal.append("Bar");           // Modifies the new object.
}

void caller() {
    StringBuffer sb_actual = new StringBuffer();
    addFoo(sb_actual);
    System.out.println(sb_actual);  // sb_actual still refers to the original object here.
}
  • 0
    private void test1 (String s) {s = "h"; // Значение никогда не используется, так как это то же самое, что и изменение исходного объекта} private void test2 (String [] s) {s [0] = "h"; // содержимое исходного объекта изменяется s = new String [] {"k"}; // s теперь просто локальный объект s [0] = "l"; // так как ссылка на объект s передается по значению, т.е. исходная ссылка на объект («указатель») или сам объект или структура не могут быть изменены}
8

Java передает примитивные типы по значениям и типам типов по ссылке

Теперь люди любят бесконечно препираться о том, является ли "пройти по ссылке" правильным способом описать, что такое Java et al. действительно делаю. Дело в том, что:

  1. Передача объекта не копирует объект.
  2. Объект, переданный функции, может иметь свои члены, модифицированные функцией.
  3. Первичное значение, переданное функции, не может быть изменено функцией. Сделана копия.

В моей книге, которая называется прохождение по ссылке.

- Брайан Би - Какие языки программирования проходят по ссылке?

  • 0
    Я не уверен, что технически правильно упомянуть, что копии сделаны для примитивных типов. Примитивные типы являются неизменяемыми, поэтому их нельзя изменить внутри метода, которому они передаются. Разница незначительна для таких вещей, как числа, но есть важное различие для потенциально больших строк.
  • 10
    Этот ответ совершенно неверен и только создает путаницу. Java - это чистый язык передачи значений. Что вас смущает, так это то, что значение может быть указателем на объект. Передача по ссылке означает, что можно было бы изменить идентичность объекта на стороне вызывающего. Например, назначение нового объекта параметру метода также повлияет на указатель, который был передан в коде, вызвавшем метод.
Показать ещё 2 комментария
8

В попытке добавить еще больше к этому, я подумал, что я бы включил раздел "Учебное руководство SCJP" по этой теме. Это из руководства, которое сделано для прохождения теста Sun/Oracle по поведению Java, поэтому он является хорошим источником для обсуждения.

Передача переменных в методы (Цель 7.3)

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

Способы могут быть объявлены для принятия примитивов и/или ссылок на объекты. Вы должны знать, как (или если) на вызывающую переменную может влиять вызываемый метод. Разница между ссылкой объекта и примитивными переменными, когда они передаются в методы, огромна и важна. Чтобы понять этот раздел, вам нужно быть удобным в разделе присвоений, описанном в первой части этой главы.

Передача ссылочных переменных объекта

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

В этом примере мы будем использовать класс Dimension из пакета java.awt:

1. import java.awt.Dimension;
2. class ReferenceTest {
3.     public static void main (String [] args) {
4.         Dimension d = new Dimension(5,10);
5.         ReferenceTest rt = new ReferenceTest();
6.         System.out.println("Before modify() d.height = " + d.height);
7.         rt.modify(d);
8.         System.out.println("After modify() d.height = "
9.     }
10.
11.
12.
13.   }
14. }

Когда мы запускаем этот класс, мы можем видеть, что метод modify() действительно смог изменить оригинальный (и только) размерный объект, созданный в строке 4.

C:\Java Projects\Reference>java ReferenceTest
Before modify() d.height = 10
dim = 11
After modify() d.height = 11

Обратите внимание, когда объект Dimension в строке 4 передается методу modify(), любые изменения объекта, которые происходят внутри метода, производятся объекту, ссылка на который была передана. В предыдущем примере ссылочные переменные d и dim оба указывают на один и тот же объект.

Использует ли Java семантику Pass-By-Value?

Если Java передает объекты, передавая вместо них ссылочную переменную, означает ли это, что Java использует pass-by-reference для объектов? Не совсем, хотя вы часто слышите и читаете, что он делает. Java фактически передается по значению для всех переменных, работающих в одной виртуальной машине. Пошаговое значение означает pass-by-variable-value. И это означает, что вы переходите к переменной! (Там это слово копируется снова!)

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

И если вы передаете ссылочную переменную объекта, вы передаете копию битов, представляющих ссылку на объект. Вызываемый метод затем получает свою собственную копию ссылочной переменной, чтобы делать с ней то, что ей нравится. Но поскольку две идентичные ссылочные переменные относятся к одному и тому же объекту, если вызываемый метод изменяет объект (например, вызывая методы setter), вызывающий может увидеть, что объект, к которому ссылается исходная переменная вызывающего, также был изменен. В следующем разделе мы рассмотрим, как меняется изображение, когда мы говорим о примитивах.

Нижняя строка по значению pass-by-value: вызываемый метод не может изменить переменную вызывающего, хотя для ссылочных переменных объекта вызываемый метод может изменить объект, на который ссылается переменная. Какая разница между изменением переменной и изменением объекта? Для ссылок на объекты это означает, что вызываемый метод не может переназначить исходную ссылочную переменную вызывающего и сделать ее ссылкой на другой объект или null. Например, в следующем фрагменте кода

        void bar() {
           Foo f = new Foo();
           doStuff(f);
        }
        void doStuff(Foo g) {
           g.setName("Boo");
           g = new Foo();
        }

переназначение g не переназначает f! В конце метода bar() были созданы два объекта Foo, один из которых ссылается на локальную переменную f и на которую ссылаются локальная (аргументная) переменная g. Поскольку метод doStuff() имеет копию ссылочной переменной, у него есть способ добраться до исходного объекта Foo, например, для вызова метода setName(). Но метод doStuff() не имеет возможности добраться до ссылочной переменной f. Таким образом, doStuff() может изменять значения внутри объекта f, но doStuff() не может изменить фактическое содержимое (битовая диаграмма) f. Другими словами, doStuff() может изменять состояние объекта, к которому относится f, но он не может заставить f ссылаться на другой объект!

Передача примитивных переменных

Посмотрим, что происходит, когда примитивная переменная передается методу:

class ReferenceTest {
    public static void main (String [] args) {
      int a = 1;
      ReferenceTest rt = new ReferenceTest();
      System.out.println("Before modify() a = " + a);
      rt.modify(a);
      System.out.println("After modify() a = " + a);
    }
    void modify(int number) {
      number = number + 1;
      System.out.println("number = " + number);
    }
}

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

  Before modify() a = 1
  number = 2
  After modify() a = 1

Обратите внимание, что a не изменилось после его передачи методу. Помните, что это была копия того, что было передано методу. Когда примитивная переменная передается методу, она передается по значению, что означает "передача по кодам-of-the-bits-in-the-variable".

  • 0
    С передачей по значению, как в Java, «переменная вызывающего» не имеет смысла. Фактическим аргументом является оценка выражения; переменная не должна быть задействована вообще.
7

В java аргументы всегда передаются по значению независимо от исходного типа переменной. Каждый раз, когда вызывается метод, происходит следующее:

  • В памяти стека создается копия для каждого аргумента, а копия версия передается методу.
  • Если исходный тип переменной примитивен, то просто, копия переменная создается внутри памяти стека, а затем передается метод.
  • Если исходный тип не является примитивным, то новая ссылка созданный внутри памяти стека, который указывает на фактический объект данные и новая ссылка затем передаются методу (при этом этап, 2 ссылки указывают на одни и те же данные объекта).

Внутри метода вы можете изменять данные объекта, на который ссылаетесь, но вы не можете изменить саму ссылку.

Этот учебник объясняет, почему Java всегда передается по значению.

7

Существует очень простой способ понять это. Позволяет передавать С++ по ссылке.

#include <iostream>
using namespace std;

class Foo {
    private:
        int x;
    public:
        Foo(int val) {x = val;}
        void foo()
        {
            cout<<x<<endl;
        }
};

void bar(Foo& ref)
{
    ref.foo();
    ref = *(new Foo(99));
    ref.foo();
}

int main()
{
   Foo f = Foo(1);
   f.foo();
   bar(f);
   f.foo();

   return 0;
}

Каков результат?

1
1
99
99

Итак, после того, как bar() присвоил новое значение переданной "ссылке", он фактически изменил ту, которая была передана из самой основной, объясняя последний вызов f.foo() из основной печати 99.

Теперь давайте посмотрим, что говорит java.

public class Ref {

    private static class Foo {
        private int x;

        private Foo(int x) {
            this.x = x;
        }

        private void foo() {
            System.out.println(x);
        }
    }

    private static void bar(Foo f) {
        f.foo();
        f = new Foo(99);
        f.foo();
    }

    public static void main(String[] args) {
        Foo f = new Foo(1);
        System.out.println(f.x);
        bar(f);
        System.out.println(f.x);
    }

}

В нем говорится:

1
1
99
1

Voilà, ссылка Foo в main, которая была передана в bar, по-прежнему не изменилась!

В этом примере ясно показано, что java не совпадает с С++, когда мы говорим "pass by reference". По сути, java передает "ссылки" как "значения" в функции, то есть java передается по значению.

  • 0
    Есть ли проблема в вашей версии на С ++, когда вы рискуете получить ошибку, когда Foo(99) выходит из области видимости, но вы ссылаетесь на нее в своем основном методе?
  • 0
    В самом деле. Ах приходит от использования Java в течение 10 лет. Но идея все еще держится. И я исправил это сейчас.
Показать ещё 2 комментария
7

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

6

PT 1: Список недвижимости

Существует голубой, 120sq-ft "Крошечный дом", который в настоящее время припаркован в 1234 Main St с красивым ухоженным газоном и цветником.

Арендодатель с местной фирмой нанимается и велел сохранить листинг для этого дома.

Позвольте назвать этого риэлтора "Боб". Привет, Боб.

Боб держит свой Листинг, который он называет tinyHouseAt1234Main, обновляется с помощью веб-камеры, которая позволяет ему замечать любые изменения в реальном доме в реальном времени. Он также подсчитывает, сколько людей спрашивали о листинге. Боб целое число viewTally для дома составляет 42 сегодня.

Всякий раз, когда кто-то хочет получить информацию о синем Крошечном Доме в 1234 Main St, они спрашивают Боба.

Боб просматривает свой листинг tinyHouseAt1234Main и рассказывает им об этом - цвет, красивый газон, кровать для лофта и туалет для компостирования и т.д. Затем он добавляет свое расследование к своему viewTally. Он не говорит им настоящего физического адреса, потому что фирма Боба специализируется на крошечных домах, которые могут быть перемещены в любое время. Талли теперь 43.

В другой фирме риэлторы могут прямо заявить, что их листинг "указывает" на дом в 1234 Main St, обозначая это небольшим * рядом с ним, поскольку они в основном касаются домов, которые редко когда-либо перемещаются (хотя, вероятно, там причины для этого). Боб фирма не беспокоится об этом.

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

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

В некоторых других, более старых фирмах, расположенных ближе к морю, риэлтор, такой как Боб, может даже не существовать, чтобы управлять Листами. Клиенты могут вместо этого обратиться к Rolodex "Annie" (& для краткости) для прямого адреса дома. Вместо того, чтобы читать данные, указанные в ссылке, из списка, такого как Боб, клиенты вместо этого получают домашний адрес от Энни (&) и переходят непосредственно к 1234 Main St, иногда даже не представляя, что они могут там найти.

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

Ну, человек с этой информацией - Боб, поэтому у клиента есть Боб, который вызывает эту услугу и отправляет ей копию списка.

jobKillingAutomatedListingService(Listing tinyHouseAt1234Main, int viewTally) Боб отправляет...

Служба в конце вызывает этот листинг houseToLookAt, но на самом деле то, что он получает, является точной копией списка Боба, с теми же самыми VALUE в ней, которые относятся к дому в 1234 Main St.

У этой новой службы также есть своя внутренняя оценка того, сколько людей просмотрели листинг. Служба принимает Боба из профессиональной любезности, но это все равно, и все равно перезаписывает его со своей собственной локальной копией. Это на сегодняшний день составляет 1, а Боб все еще 43.

Фирмы-риэлторы называют это "пропуском", поскольку Боб передает текущее значение своего viewTally и его листинга tinyHouseAt1234Main. Он фактически не проходит по всему физическому дому, потому что это непрактично. И он не передаст реальный физический адрес, как Энни (&).

Но он передает копию значения ссылки, которую он имеет в доме. Кажется, это глупое педантичное различие в чем-то, но то, как работает его фирма... ..............

PT II: где вещи становятся запутанными и опасными...

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

После предоставления объекта листинга он позволяет клиентам фактически перекрасить дом REAL на 1234 Main St, используя удаленный флот робота-поворота! Он позволяет клиентам управлять бульдозером-роботом, чтобы НАПРЯЖАТЬ копать клумбу! Это безумие!!!

Услуга также позволяет клиентам полностью перенаправлять houseToLookAt в другой дом по другому адресу без привлечения Боба или его листинга. Внезапно они могли смотреть на 4321 Elm St. вместо этого, у которого нет никакой связи с записью Боба (к счастью, они не могут больше наносить урон).

Боб наблюдает за всем этим на своей веб-камере в реальном времени. Смирившись с тяжелым трудом своей единственной ответственной работы, он рассказывает клиентам о новой уродливой работе с краской и о внезапном отсутствии ажиотажа. В конце концов, его листинг все еще для главной улицы 1234 года. Новая услуга houseToLookAt не может изменить это. Боб докладывает подробности своего tinyHouseAt1234Main точно и послушно, как всегда, до тех пор, пока его не уволят, или дом полностью разрушен Ничем.

Действительно, единственное, что служба НЕ МОЖЕТ сделать со своей копией houseToLookAt первоначального листинга Боба, - это изменить адрес от 1234 Main St. на какой-то другой адрес или на пустоту ничтожества или на какой-то случайный тип объекта, подобного утконосу. Боб Листинг все еще всегда указывает на 1234 Main St, что бы он ни стоил. Он передает текущее значение, как всегда.

Этот странный побочный эффект передачи списка в новую автоматизированную услугу запутан для людей, которые спрашивают о том, как это работает. Действительно, какая разница между способностью дистанционно управлять роботами, которые изменяют состояние дома в 1234 Main, по сравнению с тем, что физически происходит там и разрушает хаос, потому что Энни дала вам адрес?

Кажется, что это какой-то странный семантический аргумент, если вы вообще заботитесь о том, что состояние дома в листинге копируется и передается, верно?

Я имею в виду, если бы вы занимались тем, что собирали дома и физически перемещали их по другим адресам (не таким, как мобильные или крошечные дома, где такая ожидаемая функция платформы), или вы обращались, переименовывали, и перетасовать целые кварталы, как какой-то сумасшедший безумный босс низкого уровня, ТОГДА, возможно, вам будет больше нужно пропустить вокруг этих конкретных адресных ссылок, а не просто копии последнего значения деталей дома...

6
  • передано по ссылке: вызывающий и вызываемый используют одну и ту же переменную для параметра.

  • передано по значению: вызывающий и вызываемый имеют две независимые переменные с одинаковым значением.

  • Java использует pass by value
    • При передаче примитивных данных он копирует значение примитивного типа данных.
    • При передаче объекта он копирует адрес объекта и переходит к переменной метода вызываемого.

Пример использования примитивного типа данных:

public class PassByValuePrimitive {
    public static void main(String[] args) {
        int i=5;
        System.out.println(i);  //prints 5
        change(i);
        System.out.println(i);  //prints 5
    }


    private static void change(int i) {
        System.out.println(i);  //prints 5
        i=10;
        System.out.println(i); //prints 10

    }
}

Пример использования объекта:

public class PassByValueObject {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("prem");
        list.add("raj");
        new PassByValueObject().change(list);
        System.out.println(list); // prints [prem, raj, ram]

    }


    private  void change(List list) {
        System.out.println(list.get(0)); // prem
        list.add("ram");
        list=null;
        System.out.println(list.add("bheem")); //gets NullPointerException
    }
}
6

Java передается по значению.

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

class Test    {

public static void main (String[] args) throws java.lang.Exception
{
    // Primitive type
    System.out.println("Primitve:");
    int a = 5;
    primitiveFunc(a);
    System.out.println("Three: " + a);    //5

    //Object
    System.out.println("Object:");
    DummyObject dummyObject = new DummyObject();
    System.out.println("One: " + dummyObject.getObj());    //555
    objectFunc(dummyObject);
    System.out.println("Four: " + dummyObject.getObj());    //666 (555 if line in method uncommented.)

}

private static void primitiveFunc(int b)    {
    System.out.println("One: " + b);    //5
    b = 10;
    System.out.println("Two:" + b);    //10
}

private static void objectFunc(DummyObject b)   {
    System.out.println("Two: " + b.getObj());    //555
    //b = new DummyObject();
    b.setObj(666);
    System.out.println("Three:" + b.getObj());    //666
}

}

class DummyObject   {
    private int obj = 555;
    public int getObj() { return obj; }
    public void setObj(int num) { obj = num; }
}

Если строка b = new DummyObject() раскоментирована, изменения, сделанные после этого, производятся на новом объекте, новом экземпляре. Следовательно, это не отражается в том месте, откуда вызывается метод. Однако в противном случае изменение отражается, поскольку изменения производятся только по "ссылке" объекта, т.е. B указывает на тот же объект dummy.

Иллюстрации в одном из ответов в этом потоке (https://stackoverflow.com/questions/40480/is-java-pass-by-reference-or-pass-by-value) могут помочь получить более глубокое понимание.

  • 0
    Сначала вы сказали - «Java передается по значению», а затем вы сказали - «[...] в противном случае изменение отражается, поскольку изменения производятся только для« ссылки »объекта» = Java передается по ссылке
6

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

все аргументы передаются по значению.

Вызов метода может передавать два types of values методу

  • копии примитивных значений (например, значения типа int и double)
  • копии ссылок на объекты.

Objects themselves cannot be passed to methods. Когда метод изменяет параметр примитивного типа, изменения в параметре не влияют на исходное значение аргумента в вызывающем методе.

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

Ссылки: Java Как программировать (ранние объекты), десятое издание

6

Простая программа

import java.io.*;
class Aclass
{
    public int a;
}
public class test
{
    public static void foo_obj(Aclass obj)
    {
        obj.a=5;
    }
    public static void foo_int(int a)
    {
        a=3;
    }
    public static void main(String args[])
    {
        //test passing an object
        Aclass ob = new Aclass();
        ob.a=0;
        foo_obj(ob);
        System.out.println(ob.a);//prints 5

        //test passing an integer
        int i=0;
        foo_int(i);
        System.out.println(i);//prints 0
    }
}

С точки зрения программиста C/С++, java использует pass by value, поэтому для примитивных типов данных (int, char и т.д.) изменения в функции не отражаются в вызывающей функции. Но когда вы передаете объект и в функции вы меняете своих членов данных или вызываете функции-члены, которые могут изменять состояние объекта, вызывающая функция получит изменения.

  • 1
    Вы можете определить только один класс на файл. Это не включая вложенные и внутренние классы. Учитывая, что это будет что-то, что будет читать новый программист, вы должны объяснить это пользователю; позволяя им дублировать код на своей машине.
  • 2
    @ mrres1 Не совсем правильно. Вы можете определить только один общедоступный класс / интерфейс верхнего уровня для каждого файла. Поддержка нескольких классов для каждого файла - это остаток от первой версии Java, в которой не было вложенных классов, но она все еще поддерживается, хотя часто осуждается.
6

Самый короткий ответ:)

  • Java имеет пропускную способность (и pass-reference-by-value.)
  • С# также имеет пропущенную ссылку

В С# это выполняется с помощью ключевых слов "out" и "ref".

Передача по ссылке: переменная передается таким образом, что переназначение внутри метода отражается даже вне метода.

Здесь следует пример передачи по ссылке (С#). Эта функция не существует в java.

class Example
{
    static void InitArray(out int[] arr)
    {
        arr = new int[5] { 1, 2, 3, 4, 5 };
    }

    static void Main()
    {
        int[] someArray;
        InitArray(out someArray);

        // This is true !
        boolean isTrue = (someArray[0] == 1);
    }
}

См. также: Библиотека MSDN (С#): передача массивов по ссылке и из

См. также: Библиотека MSDN (С#): передача по значению и по ссылке

  • 0
    в Java мы можем сделать: someArray = InitArray(someArray) если у нас есть это: static int [] InitArray( int[] arr){ ... return ...}
  • 0
    Ты прав. Это возможная альтернатива для простой передачи по ссылке. Но передача по ссылке может сделать более сильные вещи. например, он может назначать несколько значений: например, int[] array1; int[] array2; InnitArrays(out array1, out array2); при условии, что вы создаете метод static void InitArray(out int[] a1, out int[] a2){...}
6

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

Когда вы передаете объект методу, вы передаете ссылку, и объект может быть изменен реализацией метода.

void bithday(Person p) {
    p.age++;
}

Ссылка на сам объект передается по значению: вы можете переназначить параметр, но изменение не отразится обратно:

void renameToJon(Person p) { 
    p = new Person("Jon"); // this will not work
}

jack = new Person("Jack");
renameToJon(jack);
sysout(jack); // jack is unchanged

Как следствие, "p" является ссылкой (указатель на объект) и не может быть изменен.

Примитивные типы передаются по значению. Ссылка на объект также может считаться примитивным типом.

Чтобы повторить, все передается по значению.

6

По-моему, "пройти по значению" - это ужасный способ синхронно описать два похожих, но разных события. Думаю, они должны были спросить меня в первую очередь.

С примитивами мы передаем действительное значение примитива в метод (или конструктор), будь то целое число "5", символ "c" или что у вас есть. Это фактическое значение затем становится его собственным местным примитивным. Но с объектами все, что мы делаем, дает тот же объект дополнительную ссылку (локальную ссылку), так что теперь у нас есть две ссылки, указывающие на один и тот же объект.

Надеюсь, это простое объяснение поможет.

  • 4
    «Pass by value» является стандартным термином в информатике и существует с 1950-х годов. Нет смысла жаловаться на это сейчас.
5

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

public static void dummyIncrease(int[] x, int y)
{
    x[0]++;
    y++;
}
public static void main(String[] args)
{
    int[] arr = {3, 4, 5};
    int b = 1;
    dummyIncrease(arr, b);
    // arr[0] is 4, but b is still 1
}

main()
  arr +---+       +---+---+---+
      | # | ----> | 3 | 4 | 5 |
      +---+       +---+---+---+
  b   +---+             ^
      | 1 |             | 
      +---+             |
                        |
dummyIncrease()         |
  x   +---+             |
      | # | ------------+
      +---+      
  y   +---+ 
      | 1 | 
      +---+ 
4

Я попытался упростить приведенные выше примеры, сохранив только суть проблемы. Позвольте мне представить это как историю, которую легко запомнить и применить правильно. История такова: У тебя есть собака, Джимми, чей хвост длиной 12 дюймов. Вы оставляете его у ветеринара в течение нескольких недель, пока вы едете за границу.

Ветеринар не любит длинный хвост Джимми, поэтому он хочет сократить его наполовину. Но, будучи хорошим ветеринаром, он знает, что он не имеет права калечить других собак. Поэтому он сначала делает клон собаки (с новым ключевым словом) и режет хвост клона. Когда собака наконец вернется к вам, у нее есть оригинальный 12-дюймовый хвост в такте. Счастливый конец!

В следующий раз, когда вы путешествуете, вы берете собаку, невольно, злобному ветеринару. Он также ненавидит длинные хвосты, поэтому он разрезает его на жалкие 2 дюйма. Но он делает это с вашим дорогим Джимми, а не с клоном. Когда вы вернетесь, вы потрясены, увидев, как Джимми трогательно помахал 2-дюймовым заглушкой.

Мораль истории: когда вы передаете своего питомца, вы отдаете целую и свободную опека над домашним животным ветеринару. Он волен играть с ним какой-то хаос. Передача по значению, по ссылке, указателем - это всего лишь технические споры. Если ветеринар сначала не клонирует его, он заканчивает калечить оригинальную собаку.

public class Doggie {

    public static void main(String...args) {
        System.out.println("At the owner home:");
        Dog d = new Dog(12);
        d.wag();
        goodVet(d);
        System.out.println("With the owner again:)");
        d.wag();
        badVet(d);
        System.out.println("With the owner again(:");
        d.wag();
    }

    public static void goodVet (Dog dog) {
        System.out.println("At the good vet:");
        dog.wag();
        dog = new Dog(12); // create a clone
        dog.cutTail(6);    // cut the clone tail
        dog.wag();
    }

    public static void badVet (Dog dog) {
        System.out.println("At the bad vet:");
        dog.wag();
        dog.cutTail(2);   // cut the original dog tail
        dog.wag();
    }    
}

class Dog {

    int tailLength;

    public Dog(int originalLength) {
        this.tailLength = originalLength;
    }

    public void cutTail (int newLength) {
        this.tailLength = newLength;
    }

    public void wag()  {
        System.out.println("Wagging my " +tailLength +" inch tail");
    }
}

Output:
At the owner home:
Wagging my 12 inch tail
At the good vet:
Wagging my 12 inch tail
Wagging my 6 inch tail
With the owner again:)
Wagging my 12 inch tail
At the bad vet:
Wagging my 12 inch tail
Wagging my 2 inch tail
With the owner again(:
Wagging my 2 inch tail
4

Понять это в 2 шага:

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

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

public static void foo(Dog d) {
  d.Name = "belly";
  System.out.println(d); //Reference: Dog@1540e19d

  d = new Dog("wuffwuff");
  System.out.println(d); //Dog@677327b6
}
public static void main(String[] args) throws Exception{
  Dog lisa = new Dog("Lisa");
  foo(lisa);
  System.out.println(lisa.Name); //belly
}
4

Г-н @Скотт Стейнфилд написал отличный ответ. Вот класс, который вы проверили бы что он имел в виду:

public class Dog {

    String dog ;
    static int x_static;
    int y_not_static;

    public String getName()
    {
        return this.dog;
    }

    public Dog(String dog)
    {
        this.dog = dog;
    }

    public void setName(String name)
    {
        this.dog = name;
    }

    public static void foo(Dog someDog)
    {
        x_static = 1;
        // y_not_static = 2;  // not possible !!
        someDog.setName("Max");     // AAA
        someDog = new Dog("Fifi");  // BBB
        someDog.setName("Rowlf");   // CCC
    }

    public static void main(String args[])
    {
        Dog myDog = new Dog("Rover");
        foo(myDog);
        System.out.println(myDog.getName());
    }
}

Итак, мы переходим от main() к собаке с именем Rover, затем присваиваем новый адрес указателю, который мы проходили, но в конце имя собаки не Rover, а не Fifi и, конечно, не Rowlf, но Max.

4

Java всегда имеет значение pass-by-value, параметры - это копии переданных переменных, все объекты определяются с помощью ссылки, а reference - это переменная, которая хранит адрес памяти, в котором находится объект.

Проверьте комментарии, чтобы понять, что происходит при выполнении; следуйте номерам, пока они показывают ход выполнения.

class Example
{
    public static void test (Cat ref)
    {
        // 3 - <ref> is a copy of the reference <a>
        // both currently reference Grumpy
        System.out.println(ref.getName());

        // 4 - now <ref> references a new <Cat> object named "Nyan"
        ref = new Cat("Nyan");

        // 5 - this should print "Nyan"
        System.out.println( ref.getName() );
    }

    public static void main (String [] args)
    {
        // 1 - a is a <Cat> reference that references a Cat object in memory with name "Grumpy"
        Cat a = new Cat("Grumpy");

        // 2 - call to function test which takes a <Cat> reference
        test (a);

        // 6 - function call ends, and <ref> life-time ends
        // "Nyan" object has no references and the Garbage
        // Collector will remove it from memory when invoked

        // 7 - this should print "Grumpy"
        System.out.println(a.getName());
    }
}
  • 0
    «Передать свою внутреннюю ценность» не имеет смысла.
  • 0
    @EJP спасибо за примечание, извините за мой плохой английский с 2013 года, я отредактировал все это, если вы видите лучшую формулировку, вы можете предложить или отредактировать
Показать ещё 1 комментарий
3

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

Класс-S

class S{
String name="alam";
public void setName(String n){
this.name=n; 
}}

Класс-образец

    public class Sample{
    public static void main(String args[]){
    S s=new S();
    S t=new S();
    System.out.println(s.name);
    System.out.println(t.name);
    t.setName("taleev");
    System.out.println(t.name);
    System.out.println(s.name);
    s.setName("Harry");
    System.out.println(t.name);
    System.out.println(s.name);
    }}

Выход

Alam

Alam

taleev

Alam

taleev

Юарры

Как мы определили класс S с именем переменной экземпляра со значением taleev, поэтому для все объекты, которые мы инициализируем из него, будут иметь переменную name со значением taleev, но если мы изменим значение имени любого объекта, то оно изменит имя только этой копии класса (Object) не для каждого класса, поэтому после этого, когда мы делаем System.out.println(s.name), он печатает taleev, мы не можем изменить имя, которое мы имеем и значение, которое мы изменяем, - это значение объекта, а не значение переменной экземпляра, поэтому, когда мы определяем переменную экземпляра, мы не можем ее изменить.

Итак, я так думаю, что это показывает, что java имеет дело с значениями только с ссылками

Распределение памяти для примитивных переменных можно понять с помощью this

3

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

Возьмите метод badSwap(), например:

    public void badSwap(int var1, int
 var2{ int temp = var1; var1 = var2; var2 =
 temp; }

Когда функция badSwap() вернется, переменные, переданные как аргументы, по-прежнему будут иметь свои исходные значения. Метод также будет терпеть неудачу, если мы изменим тип аргументов от int до Object, так как Java также передает ссылки на объекты по значению. Теперь вот где это становится сложно:

public void tricky(Point arg1, Point   arg2)
{ arg1.x = 100; arg1.y = 100; Point temp = arg1; arg1 = arg2; arg2 = temp; }
public static void main(String [] args) { 

 Point pnt1 = new Point(0,0); Point pnt2
 = new Point(0,0); System.out.println("X:
 " + pnt1.x + " Y: " +pnt1.y);

     System.out.println("X: " + pnt2.x + " Y:
 " +pnt2.y); System.out.println(" ");

     tricky(pnt1,pnt2);
 System.out.println("X: " + pnt1.x + " Y:" + pnt1.y);

     System.out.println("X: " + pnt2.x + " Y: " +pnt2.y); }

Если мы выполним этот метод main(), мы увидим следующий вывод:

X: 0 Y: 0 X: 0 Y: 0 X: 100 Y: 100 X: 0 Y: 0

Метод успешно изменяет значение pnt1, хотя он передается по значению; однако, смена pnt1 и pnt2 терпит неудачу! Это основной источник путаницы. В методе themain() pnt1 и pnt2 являются не более чем объектными ссылками. Когда вы passpnt1 и pnt2 для метода tricky(), Java передает ссылки по значению так же, как и любой другой параметр. Это означает, что ссылки, переданные методу, фактически являются копиями исходных ссылок. На рисунке 1 ниже показаны две ссылки, указывающие на один и тот же объект после того, как Java передает объект методу.

Java копирует и передает ссылку по значению, а не объекту. Таким образом, манипуляция с методом будет изменять объекты, поскольку ссылки указывают на исходные объекты. Но поскольку ссылки являются копиями, свопы сбой. Как показано на рисунке 2, метод ссылается на swap, но не на исходные ссылки. К сожалению, после вызова метода у вас остались только исходные ссылки, которые не были отпущены. Для того, чтобы своп мог преуспеть за пределами вызова метода, нам нужно поменять исходные ссылки, а не копии.

3

Много путаницы вокруг этой проблемы возникает из-за того, что Java попыталась переопределить значение "Pass by value" и "Pass by reference". Важно понимать, что это "Отраслевые условия" и не могут быть правильно поняты вне этого контекста. Они предназначены для того, чтобы помочь вам, как вы кодируете и ценны для понимания, поэтому пусть сначала перейдет то, что они означают.

Хорошее описание обоих можно найти здесь.

Передача по значению Значение, полученное функцией, является копией объекта, который использует вызывающий. Он полностью уникален для функции, и все, что вы делаете с этим объектом, будет видно только внутри функции.

Передача по ссылке Значение, полученное функцией, является ссылкой на объект, который использует вызывающий. Все, что функция делает с объектом, к которому относится значение, будет видно вызывающей стороне, и она будет работать с этими изменениями с этой точки.

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

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

И нет, С++ специальный синтаксис "передать по ссылке" не является исключительным определением pass by reference. Это просто синтаксис удобства, который должен сделать так, что вам не нужно использовать синтаксис указателя после передачи указателя. Он все еще передает указатель, компилятор просто скрывает этот факт от вас. Он также по-прежнему передает этот указатель BY VALUE, компилятор просто скрывает это от вас.

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

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

  • 1
    Java не переопределил эти термины. Никто не имеет. Он просто избегает термина C указатель.
  • 4
    Эти термины существовали задолго до того, как Java или C. Pointer были всего лишь методом реализации одного из них. Если вы примете определение Java для них, то они станут бессмысленными, потому что по этому определению каждый когда-либо созданный язык является только Pass by Value.
3

Java передает все по значению!

//создаем объект, передавая имя и возраст:

PersonClass variable1 = new PersonClass("Mary", 32);

PersonClass variable2;

//И переменная2, и переменная1 теперь ссылаются на один и тот же объект

variable2 = variable1; 


PersonClass variable3 = new PersonClass("Andre", 45);

//variable1 теперь указывает на variable3

variable1 = variable3;

//ЧТО ВЫХОДИТ ЭТО?

System.out.println(variable2);
System.out.println(variable1);

Mary 32
Andre 45

если вы могли бы понять этот пример, мы сделали. в противном случае, пожалуйста, посетите эту веб-страницу для подробного объяснения:

веб-страница

  • 0
    На самом деле это ничего не объясняет в отношении потенциала с помощью свойства ref / by val Java.
3

В ссылке для Java используется обходной путь. Позвольте мне объяснить в этом примере:

public class Yo {
public static void foo(int x){
    System.out.println(x); //out 2
    x = x+2;
    System.out.println(x); // out 4
}
public static void foo(int[] x){
    System.out.println(x[0]); //1
    x[0] = x[0]+2;
    System.out.println(x[0]); //3
}
public static void main(String[] args) {
    int t = 2;
    foo(t);
    System.out.println(t); // out 2 (t did not change in foo)

    int[] tab = new int[]{1};
    foo(tab);
    System.out.println(tab[0]); // out 3 (tab[0] did change in foo)
}}

Надеюсь, это поможет!

3

Нижняя строка для пропущенного значения: вызываемый метод не может изменить вызывающий переменная, хотя для ссылочных переменных объекта вызываемый метод может изменить объект, на который ссылается переменная. Какая разница между изменением переменной и изменение объекта? Для ссылок на объекты это означает, что вызываемый метод не может переназначить исходную ссылочную переменную вызывающего и сделать ее ссылкой на другой объект, или null.

Я взял этот код и объяснение из книги по сертификации Java и внес некоторые незначительные изменения.
Я думаю, это хорошая иллюстрация к переходу по значению объекта. В приведенном ниже коде, переназначение g не переназначает f! В конце метода bar() два объекта Foo были созданы, причем одна из них ссылается на локальную переменную f и одну, на которую ссылается локальная (аргументная) переменная g.

Поскольку метод doStuff() имеет копию ссылочной переменной, у него есть способ получить к исходному объекту Foo, например, для вызова метод setName(). Но метод doStuff() не имеет возможности добраться до контрольная переменная f. Поэтому doStuff() может изменять значения внутри объекта f to, но doStuff() не может изменить фактическое содержимое (битовая диаграмма) f. В других слова, doStuff() может изменять состояние объекта, к которому относится f, но он не может сделайте f ссылкой на другой объект!

package test.abc;

public class TestObject {

    /**
     * @param args
     */
    public static void main(String[] args) {
        bar();
    }

    static void bar() {
        Foo f = new Foo();
        System.out.println("Object reference for f: " + f);
        f.setName("James");
        doStuff(f);
        System.out.println(f.getName());
        //Can change the state of an object variable in f, but can't change the object reference for f.
        //You still have 2 foo objects.
        System.out.println("Object reference for f: " + f);
        }

    static void doStuff(Foo g) {
            g.setName("Boo");
            g = new Foo();
            System.out.println("Object reference for g: " + g);
        }
}


package test.abc;

public class Foo {
    public String name = "";

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

Обратите внимание, что ссылка на объект не изменилась на выходе консоли ниже:

Консольный выход:

Ссылка на объект для f: test.abc.Foo@62f72617

Ссылка на объект для g: test.abc.Foo@4fe5e2c3

Boo Ссылка на объект для f: test.abc.Foo@62f72617

2

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

package com.asok.cop.example.task;
public class Example {
    int data = 50;

    void change(int data) {
        data = data + 100;// changes will be in the local variable 
        System.out.println("after add " + data);
        }

    public static void main(String args[]) {
        Example op = new Example();
        System.out.println("before change " + op.data);
        op.change(500);
        System.out.println("after change " + op.data);
    }
}

Вывод:

before change 50
after add 600
after change 50
  • 0
    Пожалуйста, поясните, что примитивы передаются по значению, тогда как объекты передаются по ссылке. Рассмотрим метод, подобный следующему: void changeName(Person person, String newName){ person.name = newName; } который действительно изменяет исходный объект. Если бы это было действительно передачей по значению, разве мы не ожидали бы, что данный аргумент person будет копией исходного объекта?
  • 1
    Объекты @SnobOfTheGolfClub по-прежнему передаются по значению, хотя операции с ними ведут себя как передача по ссылке. Рассмотрим void changePerson(Person person){ person = new Person(); } ссылка вызывающего абонента на объект person останется неизменной. Сами объекты передаются по значению, но изменения могут повлиять на их членов. Чтобы быть истинной передачей по ссылке, мы должны были бы иметь возможность переназначить аргумент новому объекту и отразить изменение в вызывающей стороне.
2

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

2

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

Изображение 1294

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

Это означает:

  • Вы можете изменить значение myObject внутри функции
  • Но вы не можете изменить то, на что ссылается myObject, внутри функции, потому что point не является myObject
  • Помните, что и point и myObject являются ссылками, разными ссылками, однако эти ссылки указывают на одну и ту же new Point(0,0)
1

Когда C передает структуру данных по ссылке, указатель на структуру данных существует где-то, но он неизменен. В коде

   void foo(Struct& x)

x всегда означает ту же ячейку памяти внутри метода. Он устанавливается, когда вызывается метод, и вы не можете заставить его указывать на какой-либо другой объект Struct где-то. В Java аналогом передается ссылка объекта в качестве конечного параметра.

   void foo(final Struct x)

Присвоение значения непосредственно x будет ошибкой. Java не может просто скопировать все поля одной структуры в другую при назначении.

Прохождение регулярного параметра (без окончательного) - это некая странная конструкция для C, но ближайшая конструкция может быть

   void foo(Struct *x)

Здесь x - указатель, и, хотя вы можете использовать его для изменения структуры, сам указатель передается по значению (вы можете присвоить ему другое значение, это изменение не будет распространяться обратно на вызывающий код). Вам также нужен другой синтаксис для доступа к полям структуры (*x).field или x->field, а не x.field, как в Java, но это всего лишь подробная информация о языке.

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

1

В СООТВЕТСТВИИ С ТЕРМИНОЛОГИЕЙ С++:

  • Примитивные типы и их обертки - Pass by Value
  • Другие сложные типы данных - Pass by Reference

(Хотя Java полностью передается по значению, во втором случае он передает ссылку на объект, и в этом случае значение объекта, если оно изменено, отражается в основной функции, и поэтому я назвал его "Передача по ссылке" в соответствии с Терминология С++.) Если вы родом из С++, тогда Java передается по значению для примитивных типов и их классов Wrapper, таких как int, Integer, bool, Boolean, т.е., Если вы передадите значение этих типов данных, в исходной функции изменений не будет. Для всех других типов данных java просто передает их, и если какие-либо изменения сделаны, это изменение можно увидеть в исходной функции (его можно назвать pass by reference в соответствии с терминологией С++)

  • 3
    Пожалуйста, предоставьте точную ссылку для этого; т.е. конкретная ссылка на конкретный раздел официальной спецификации C ++. (Я подозреваю, что именно так вы бы описали >> представления << в C ++, если бы вы имитировали поведение параметра Java с использованием функций C ++. Но это упускает из виду. Мы говорим здесь о языковых моделях, и это хорошо известно, что соответствующие языковые модели Java и C ++ (очень) различны.)
1

Язык программирования Java передает аргументы только по значению, т.е. вы не можете изменить значение аргумента в вызывающем методе изнутри вызываемый метод.


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


Для многих людей это выглядит как пошаговая ссылка, и поведенчески много общего с передачей по ссылке. Однако есть две причины это неточно.

  • Во-первых, способность изменять вещь передавалась метод применяется только к объектам, а не к примитивным значениям.

  • Во-вторых, фактический значение, связанное с переменной типа объекта, является ссылкой на объект, а не сам объект. Это важное различие в других путей и, если они будут понятны, полностью поддерживает тот факт, что язык программирования Java передает аргументы по значению.


The following code example illustrates this point:
1 public class PassTest {
2
3   // Methods to change the current values
4   public static void changeInt(int value) {
5     value = 55;
6  }
7   public static void changeObjectRef(MyDate ref) {
8     ref = new MyDate(1, 1, 2000);
9  }
10   public static void changeObjectAttr(MyDate ref) {
11     ref.setDay(4);
12   }
13
14 public static void main(String args[]) {
15     MyDate date;
16     int val;
17
18     // Assign the int
19     val = 11;
20     // Try to change it
21     changeInt(val);
22     // What is the current value?
23     System.out.println("Int value is: " + val);
24
25 // Assign the date
26     date = new MyDate(22, 7, 1964);
27     // Try to change it
28     changeObjectRef(date);
29     // What is the current value?
30 System.out.println("MyDate: " + date);
31
32 // Now change the day attribute
33     // through the object reference
34     changeObjectAttr(date);
35     // What is the current value?
36 System.out.println("MyDate: " + date);
37   }
38 }

This code outputs the following:
java PassTest
Int value is: 11
MyDate: 22-7-1964
MyDate: 4-7-1964
The MyDate object is not changed by the changeObjectRef method;
however, the changeObjectAttr method changes the day attribute of the
MyDate object.
  • 0
    Это очень вводит в заблуждение. Вы, конечно, можете изменить значение аргумента из метода.
0

Пожалуйста, перейдите по этой ссылке видео. Если ваши сомнения не проясняются, вы можете прийти и понизить. У меня было такое же замешательство, я много гуглял и где-то узнал эту ссылку. Который сделал все очень ясно.

0

Уже есть отличные ответы, которые охватывают это. Я хотел внести небольшой вклад, поделившись очень простым примером (который будет скомпилирован), сравнивающим поведение между передачей по ссылке в c++ и передачей по значению в Java.

Несколько моментов:

  1. Термин ссылка перегружен. В Java это просто означает указатель. В термине "Передача по ссылке" это означает ссылку на исходную переменную, которая была передана.
  2. Java является Pass-by-value, но позволяет нам эмулировать pass be reference, передавая Java-ссылку (т.е. указатель) по значению. Это означает, что он передает копию ссылки Java.
  3. c++ позволяет передавать по ссылке, объявляя опорный параметр с помощью символа "&" (который, как оказалось, является тем же символом, который используется для указания "адреса переменной" как в C, так и в c++). Например, если мы передадим указатель, параметр и аргумент не просто указывают на один и тот же объект, но на то, что они являются одной и той же переменной. Если один из них настроен на другой адрес или на ноль, то и другой.
  4. В приведенном ниже примере c++ я передаю указатель на завершенную нулем строку по ссылке. И в приведенном ниже примере с Java я передаю ссылку на Java на строку (опять же, так же, как указатель на строку) по значению. Обратите внимание на вывод в комментариях.

c++ пройти по ссылочному примеру:

using namespace std;
#include <iostream>

void change (char *&str){   // the '&' makes this a reference parameter
    str = NULL;
}

int main()
{
    char *str = "not Null";
    change(str);
    cout<<"str is " << str;      // ==>str is <null>
}

Java передает "Java-ссылку" на примере значения

public class ValueDemo{

    public void change (String str){
        str = null;
    }

     public static void main(String []args){
        ValueDemo vd = new ValueDemo();
        String str = "not null";
        vd.change(str);
        System.out.println("str is " + str);        // ==> str is not null
     }
}

И последнее замечание: каждый раз, когда я сталкиваюсь с этим вопросом по stackoverflow, меня бесит бесконечно, что на этот вопрос нет единого ответа, как будто это не решенный вопрос. Pass-by-reference и Pass-by-value - это стандартные отраслевые термины, которые имеют определенные значения. В этой теме есть несколько приемлемых ответов, которые четко объясняют, почему Java является передачей по значению. Пользователь, который разместил этот вопрос, ничего не опубликовал в stackoverflow с 3 сентября 2008 года и, вероятно, никогда не выберет приемлемый ответ. Как бы сильно я не любил stackoverflow, я вижу в этом огромную слабость.

  • 0
    Java не позволяет нам «эмулировать передачу по ссылке». Отказ одного пользователя от ответа не является огромной слабостью всего сайта.
  • 0
    Объясните, что вы подразумеваете под «Java не позволяет нам подражать». Что касается слабости, то это разница во мнениях.
Показать ещё 5 комментариев
0

Просто: Java передается по копиям.

0

Легкий ответ для новичка

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

Обратите внимание, что Объект, который не является "сложным" (не похожим на нашу собаку), как Integer или String, передается по значению. Поэтому, даже если они являются объектами со всем, что вы можете сделать на них, они не могут быть модифицированы внутри метода... Да, это просто разобрано и не имеет никакого смысла...

Пример (не собака):

public class HelloWorld {
    private Integer i;
    private String s;
    private Boolean b;

    public static void main(String[] args) {
        HelloWorld h = new HelloWorld();

        h.s = "Bill";
        h.ModMe(h.s);
        h.i = 2;
        h.ModMe(h.i);
        h.b = true;
        h.ModMe(h.b);
        System.out.println(h.s + " " + h.i + " " + h.b);

        h.ModMe(h);
        System.out.println(h.s + " " + h.i + " " + h.b);

        String test = "TEST";
        h.ModMe(test);
        System.out.println(test);
    }

    public void ModMe(Object o) {
        if (o instanceof HelloWorld) {
            ((HelloWorld) o).i = (int) Math.pow(((HelloWorld) o).i, 2);
            ((HelloWorld) o).b = !((HelloWorld) o).b;
            ((HelloWorld) o).s = ((HelloWorld) o).s.concat(" modded successfully");
        } else if (o instanceof Integer) {
            o = (Integer) o + (Integer) o;
        } else if (o instanceof String) {
            o = ((String) o).concat(" is modified.");
        } else if (o instanceof Boolean) {
            o = !(Boolean) o;
        }
    }
}

Если вы запустите, что объект h будет изменен, если вы используете весь объект в качестве аргумента, но не поля si или b, если вы используете их только как аргумент. если вы отлаживаете это, вы заметите, что для полей идентификатор объекта будет меняться, как только вы запустите строку, где значение будет изменено. Таким образом, он будет делать то же самое, что и "new Integer", "new String" и "new Boolean" автоматически.

0

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

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

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

0

Java передает ссылки на объекты по значению.

Итак, если какая-либо модификация будет сделана для объекта, к которому указывает опорный аргумент, он будет отражен обратно на исходный объект.

Но если ссылочный аргумент указывает на другой объект, то исходная ссылка укажет на исходный объект.

-1
class Codechef
{
    public static void main (String[] args) throws java.lang.Exception
    {
        // your code goes here
        Person p = new Person();
        p.age = 30;
        System.out.println(p.age); //30
        ss(p); //40
        System.out.println(p.age); //40
        yy(p); //20
        System.out.println(p.age); //40
    }
    static void  ss(Person p) {
// Change the value in object then it gets retained.
        p.age = 40;
        System.out.println(p.age);
    }

    static void  yy(Person p) {
// Change the object itself it don't retain. So its pass by value
        p = new Person();
        p.age = 20;
        System.out.println(p.age);
    }
}


class Person {
    int age = 10;   
}

О/р

30 40 40 20 40

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

-1
public class Main {

    public static void main(String[] args) {
        Person person = new Person();
        person.setName("Murat");
        System.out.println(person);
        passby(person);
        System.out.println(person);

    }

    public static void passby(Person a){
        //a= new Person(); //if you open this line you will see that reference value change so main reference not change
        a.setName("Kemal");
        System.out.println(a);
    }

    private static class Person {
        String name;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        @Override
        public String toString() {
            return getName();
        }
    }
}
-1

Мышление таким образом просто заставляет вещи звучать все более и более сложными. Просто думает, что ключевое слово new создает целый новый объект, поэтому, когда вы передаете объект из переменной в другую переменную без ключевого слова new, вы передаете один и тот же объект.

Предположим, что оператор = означает слово "есть" на английском языке:

Pokemon a = new Pokemon(); // Pokemon a is a new Pokemon
Pokemon b = a; //Pokemon b is pokemon a

Итак, если вы внесли какие-либо изменения в сторону b, влияет на a, так как b является a или вы можете считать b как псевдоним для.

Посмотрим, что произойдет, когда мы изменим операнды на примитивные переменные, такие как int, float и т.д.:

int x = 1; // x is 1
int y = x; // y is 1
y = 5 // y is 5

x является псевдонимом для 1, когда я говорю "x", это означает 1. Итак, изменения в y не будут влиять на x...

Другая аналогия:

Предположим, что Томми Спейд, Деннис Серд и Джеки Даймонд - друзья. Томми называет Джеки "Джеком", а Деннис называют Джеки "Диа". Пусть думают, что Джек и Ди - переменные. Независимо от того, Джеки и Дия - это одна и та же сущность (объект). Когда Томми ударил по лицу Джека, Деннис тоже может видеть синяк на лице Диа, так как Джек и Дия - это тот же [человек].

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

Одним из основных понятий ООП является абстракция. Вам не нужно знать подробные сведения о том, как устройство работает для его использования, вам нужно только знать, как его использовать, а не как это работает. Если вы не хотите быть исследователем, все в порядке. Надеюсь, мой ответ поможет.

-3

Короткий ответ. Java всегда pass-by-value. Когда мы говорим о передаче объектов, Java передает копию ссылки на "реальный" объект

-3
public static void main(String args[]) {

    Point p1 = new Point(1, 1); //This is pointing to a location in memory let say that the reference is x1=5
    Point p2 = new Point(2, 2); //This is pointing to a location in memory let say that the reference is x2=6

    // Next we need to pass something to the function, what is it and how we pass it? 
    // There are two references x1,x2 are going to be passed by value
    // Meaning that if x1=5, x2=6 as we mentioned, we are passing 5,6. We are not passing x1,x2
    // That a big difference. If we are passing x1 and x2, we can change values there inside the trickMe method.
    // but we are passing only two numbers which are 5 and 6.
    trickMe(p1, p2);

    System.out.println(p1); // outputs java.awt.Point[x=1,y=1]
    System.out.println(p2); // outputs java.awt.Point[x=2,y=2]
}

//  Here we received two values which are 5,6.
// This method will create another two references which are x3,x4 for objects arg3,arg4
// arg3 and arg4 are actually same exact objects as p1,p2 with the same memory location. 
// BUT we use x1,x2 to access p1,p2
// AND we use x3,x4 to access arg3,arg4
// x3 is corresponding to x1 and each one of them equal 5. Each one of them means that they are not the same obviously, but have equal values.
// x4 is corresponding to x2 and each one of them equal 6.
// Beautiful, now if we replace x3 with x4,, does this replace  x1 with x2?
// NO THEY ARE TOTALLY INDEPENDENT. x3 and x4 are inside the trickMe Method. they have no acces to x1,x2 at all.
// So, no matter what I did to x3,x4 inside the trickMe method, x1 and x2 in the main method will not change at all.
// They will remain x1=5 and x2=6
public static void trickMe(Point arg3, Point arg4) {
    // Now we have a reference to arg3 which is x3 and it is equal to 5 and a reference to arg4 which is x4 and equal to 6, right?
    // Great! When we say arg3=arg4, we means that make the reference of arg3 which is x3 equals to the reference of arg4 which is x4.
    // Meaning, from this point,, we will make x3=6 instead of 5 and of course x4 is still 6 as it was.
    // We didn't touch niether x1 nor x2, we also can't thouch them inside trickMe method.
    // We only have access to x3 and x4.
    // That why swapping methods always fail in java if we just put the equal sign "=" to swap.
    // We really have two memory locations which represents two, but with 4 different refernces.
    // Two are in the main method, and another two are in this method
    arg3 = arg4;
}
-4

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

Все это

  • 0
    Это не верно. Как объясняют многие комментарии и ответы, это сбивает с толку, потому что в Java-объектах используются ссылки. Однако фраза «передать по ссылке» означает нечто конкретное. «Передача по значению» означает, что объект, идущий в метод, копируется - совершенно новая вещь. «Передача по ссылке» означает, что объект, передаваемый в метод, является общим для обоих мест. Java всегда всегда «передается по значению». Когда вы отправляете объект, он копирует ссылку, но это не делает его «передаваемым по ссылке».
  • 0
    Ваш ответ имеет смысл. Я не знаю, почему люди отвергли этот вопрос.
-4

В JAVA все собственные типы передаются только по значению. И все остальные типы, включая объекты, классы Wrapper и т.д., Проходят по ссылке.

С родными типами вы также можете использовать те же функции, что и pass by reference, путем преобразования переменных --- > переменных класса (статические).

-4

TL; DR: Минимальный пример:

package foobar;
import java.util.ArrayList;

public class FooBar {

  public static void main(String[] args) {
    ArrayList<Integer> list1 = new ArrayList<>(); // An object.
    list1.add(1);

    ArrayList<Integer> list2 = new ArrayList<>(); // Another object.
    list2.add(2);

    int x = 42; // A primitive (same with Integer).

    sideEffects(list1, list2, x);

    System.out.println(list1); // Output: [1]     (unchanged)
    System.out.println(list2); // Output: [2, 3]  (changed !)
    System.out.println(x);     // Output: 42      (not changed)
  }

  private static void sideEffects(ArrayList<Integer> list1, ArrayList<Integer> list2, int x) {
    list1 = list2;
    list1.add(3);
    x = 21;
  }
}
-4

Чтобы упростить задачу:
addr - адрес ячейки памяти или ссылка.

Java pass-by-reference, потому что:

a; // => addrA(valueA);
function (a) {// => newAddrA(addrA(valueA));
    // change "a in function" affects the change of the value "a outside function" 
   // because the newAddrA points to addrA with valueA.
    b; // => addrB(valueB)
    a = b; // => newAddrA(addrB(valueB));
    // change "a in function" does not affect the value of "a outside function" 
   // because now newAddrA points to addrB with valueB.
}

С++ pass-by-value, потому что:

a; // => addrA(valueA);
function (a) {// => newAddrA(valueCloneA);
    // change "a in function" does not affect the value of "a outside function" 
   // outside the function, because the newAddrA with new value valueCloneA;
    b; // => addrB(valueB);
    a = b; // => newAddrA(addrB(valueB));
}
-5

Вот пример прохождения по ссылке:

Test t=new Test();
Test x=t;

оба x и t относятся к тому же объекту Test.

В отличие от этого, это показывает переход по значению:

int i=0;
int j=i;
i++;
//i=1;
//j=0;

Когда вы используете new, вы создаете объект ref, но когда вы используете var, например int, float или boolean, он будет передан по значению.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню