Вопрос: Является ли Java «pass-by-reference» или «pass-by-value»?


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

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

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

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


5385


источник


Ответы:


Java всегда Передача по стоимости , К сожалению, они решили назвать местоположение объекта «ссылкой». Когда мы передаем значение объекта, мы передаем Справка к нему. Это пугает начинающих.

Это происходит так:

public static void main(String[] args) {
    Dog aDog = new Dog("Max");
    // 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, java passes by value
    aDog.getName().equals("Fifi"); // false 
}

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");
    foo(aDog);
    // when foo(...) returns, the name of the dog has been changed to "Fifi"
    aDog.getName().equals("Fifi"); // 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(за исключением случаев, когда dизменен, чтобы указать на другой Dogпример вроде d = new Dog("Boxer")).


4633



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

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) предлагается изменить свое имя на Макс.
  • на линии "BBB"
    • новый Dogсоздано. Допустим, он по адресу 74
    • мы присваиваем параметр someDogдо 74
  • на линии "CCC"
    • someDog следует за Dogэто указывает на Dogобъект по адресу 74)
    • что Dog(по адресу 74) просят сменить свое имя на Rowlf
  • то мы возвращаемся

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

Сделал myDogизменение?

Есть ключ.

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

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

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

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

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

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


2594



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


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

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");
    

    enter image description here

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

    public static void changeReference(Foo a)
    

    enter image description here

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

    changeReference(f);
    

    enter image description here

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

    Foo b = new Foo("b");
    

    enter image description here

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

    enter image description here


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

    enter image description here

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

    enter image description here

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


1371



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

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

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

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

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

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

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

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

Одна картинка стоит тысячи слов:

Pass by Value

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

Если вы этого не поняли, просто доверьтесь мне и помните, что лучше сказать, что Java передается по значению , Что ж, пройти по эталонному значению , Ну, еще лучше Передача по копировально-оф-переменной значение! ;)

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

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

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

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


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


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


643



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

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

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

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

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

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


551



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

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


272



Я чувствую, что спорить о «pass-by-reference vs pass-by-value» не очень полезно.

Если вы говорите: «Java - это проход за кадром (ссылка / значение)», в любом случае вы не предоставляете полный ответ. Вот дополнительная информация, которая, надеюсь, поможет понять, что происходит в памяти.

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

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

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

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

primitives on the stack

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

Вот так:

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

a b*7ch aint one!

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

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

marx brothers

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

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, так как адрес второй строки такой же, как и первый, новая строка не создается и не выделяется новое пространство кучи, а в стек создается новый идентификатор. Тогда мы называем shout(): создается новый стек стека и новый идентификатор, nameсоздается и назначается адрес уже существующей строки.

la da di da da da da

Итак, ценность, ссылка? Вы говорите «картофель».


195



Just to show the contrast, compare the following C++ and Java snippets:

In C++: Note: Bad code - memory leaks! But it demonstrates the point.

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

In 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 only has the two types of passing: by value for built-in types, and by value of the pointer for object types.


160



Java passes references to objects by value.


143



I can't believe that nobody mentioned Barbara Liskov yet. When she designed CLU in 1974, she ran into this same terminology problem, and she invented the term call by sharing (also known as call by object-sharing and call by object) for this specific case of "call by value where the value is a reference".


134