Является ли Java "pass-by-reference" или "pass-by-value"?

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

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

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

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

Ответ 1

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 самой.

Ответ 2

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

В 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.

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

Ответ 3

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

    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 :)

Ответ 4

Это даст вам представление о том, как 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.

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

Pass by Value

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

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

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

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

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

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


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


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

Ответ 5

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

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

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

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

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

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

Ответ 6

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

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

Ответ 7

Мне кажется, что спорить о "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;

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

primitives on the stack

Когда вы объявляете и создаете экземпляр объекта. Фактический объект переходит в кучу. Что происходит в стеке? Адрес объекта в куче. Программисты на C++ назвали бы это указателем, но некоторые разработчики 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, так как адрес второй строки такой же, как и первый, не создается новая String и не выделяется новое место кучи, но в стек создается новый идентификатор. Затем мы вызываем shout(): создается новый стек стека и создается новый идентификатор name и назначается адрес уже существующей строки.

la da di da da da da

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

Ответ 8

Чтобы показать контраст, сравните следующие 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 имеет только два типа передачи: по значению для встроенных типов и по значению указателя для типов объектов.

Ответ 9

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

Ответ 10

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

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.

Ответ 11

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

Ответ 12

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

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

Ответ 13

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

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

enter image description here

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 с двумя связанными ссылками. enter image description here

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) enter image description here

В методе tricky:

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

enter image description here

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

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

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

enter image description here

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

Итак, после выполнения метода tricky, когда вы вернетесь в main, у вас возникнет такая ситуация: enter image description here

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

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

Ответ 14

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, это просто пример).

first illustration

new PassByValue().changeValue(t);

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

second illustration

Если вы измените что-либо в функции с помощью ссылки 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, как показано ниже:

third illustration

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

Ответ 15

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

Получив внешний вид окна, просмотрите сборку или некоторое управление памятью низкого уровня. На уровне ЦП ссылка на что-либо сразу становится значением, если она записывается в память или в один из регистров 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 ссылка.

Ответ 16

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

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

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

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

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

Ответ 17

Насколько я знаю, 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.

Ответ 18

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

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

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

Ответ 19

Позвольте мне попытаться объяснить свое понимание с помощью четырех примеров. 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]

Ответ 20

Вы никогда не можете передавать по ссылке в 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.

Ответ 21

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

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

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

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

Или из 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 и т.д.).

Ответ 22

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

Ответ 23

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

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

  1. Термин "ссылка" перегружен двумя значениями. В Java это просто означает указатель, но в контексте "Передачи по ссылке" это означает дескриптор исходной переменной, которая была передана.
  2. Java является Pass-by-value, но позволяет нам эмулировать pass be reference, передавая Java-ссылку (то есть указатель) по значению. Это означает, что он передает копию ссылки Java. РЕДАКТИРОВАТЬ: так как кто-то сделал комментарий по этому поводу, позвольте мне объяснить. До C некоторые (но не все) более ранние языки, такие как FORTRAN и COBOL, поддерживали PBR, но C - нет. Чтобы изменить значения переменных внутри функций, программисты на C эмулировали PBR, передавая указатели на переменные в функции. Языки, вдохновленные C, такие как Java, заимствовали эту идею и продолжают эмулировать PBR, как и C.
  3. c++ разрешает передачу по ссылке, объявляя опорный параметр с помощью "& amp;" символ (который оказывается тем же символом, который используется для обозначения "адреса переменной" как в C, так и в c++). Например, если мы передаем указатель по ссылке, параметр и аргумент не просто указывают на один и тот же объект. Скорее, это одна и та же переменная. Если один из них настроен на другой адрес или имеет значение NULL, то и другой.
  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!!
                                                // Note that if "str" was
                                                // passed-by-reference, it
                                                // WOULD BE NULL after the
                                                // call to change().
     }
}

EDIT

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

В паскале параметры, передаваемые по ссылке, называются "параметрами вар". В процедуре setToNil ниже обратите внимание на ключевое слово "var", которое предшествует параметру "ptr". Когда указатель передается этой процедуре, он передается по ссылке. Обратите внимание на поведение: когда эта процедура устанавливает для ptr значение nil (этот паскаль говорит за NULL), она устанавливает для аргумента значение nil - вы не можете сделать это в Java.

program passByRefDemo;
type 
   iptr = ^integer;
var
   ptr: iptr;

   procedure setToNil(var ptr : iptr);
   begin
       ptr := nil;
   end;

begin
   new(ptr);
   ptr^ := 10;
   setToNil(ptr);
   if (ptr = nil) then
       writeln('ptr seems to be nil');     { ptr should be nil, so this line will run. }
end.

ОБНОВЛЕНИЕ 2

Некоторые выдержки из "языка программирования Java" Кена Арнольда, Джеймса Гослинга (человека, который изобрел Java) и Дэвида Холмса, глава 2, раздел 2.6.5

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

Он продолжает делать то же самое в отношении объектов., ,

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

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

Язык программирования Java не передает объекты по ссылке; это передает ссылки на объекты по значению. Потому что две копии одного и того же ссылка ссылается на тот же фактический объект, изменения, сделанные через один ссылочная переменная видна через другую. Есть ровно один передача параметров mode-передача по значению -and, которая помогает сохранить вещи простой.

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

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

Я надеюсь, что это решит спор, но, вероятно, не будет.

ОБНОВЛЕНИЕ 3

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

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

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

Передача ссылки по значению --Changes в ссылку не отражается в области вызова, но изменения в объекте. Это потому, что ссылка копируется, но и оригинал, и копия ссылаются на один и тот же объект. Passing Object references By Value

Передача по ссылке --There не является копией ссылки. Одиночная ссылка является общей как для вызывающей стороны, так и для вызываемой функции. Любые изменения в ссылке или данных объекта отражаются в области вызова. Pass by reference

ОБНОВЛЕНИЕ 4

Я видел посты на эту тему, которые описывают низкоуровневую реализацию передачи параметров в Java, что я считаю замечательным и очень полезным, потому что оно делает абстрактную идею конкретной. Однако, для меня вопрос больше о поведении, описанном в спецификации языка, чем о технической реализации поведения. Это выдержка из спецификации языка Java, раздел 8.4.1 :

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

Это означает, что java создает копию переданных параметров перед выполнением метода. Как и большинство людей, которые брали компиляторы в колледже, я использовал "Книгу Дракона", которая является Книгой компиляторов. В главе 1 есть хорошее описание "Call-by-value" и "Call-by-Reference". Описание Call-by-value точно соответствует спецификациям Java.

Когда я брал компиляторы, я использовал первое издание книги 1986 года, которое предшествовало Java примерно на 9 или 10 лет. Тем не менее, я только что наткнулся на копию 2nd Eddition от 2007 года , в которой на самом деле упоминается Java! Раздел 1.6.6, помеченный "Механизмы передачи параметров", довольно красиво описывает передачу параметров. Вот выдержка под заголовком "Call-by-value", в которой упоминается Java:

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

Ответ 24

Как уже упоминалось ранее, 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 будет меняться только в локальной области, а не вне этого метода.

Ответ 25

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

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

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

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

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

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

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

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

Ответ 26

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

Ответ 27

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

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

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

Ответ 28

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

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

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

Ответ 29

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

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

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

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

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

Ответ 30

Короче говоря, 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 все переменные, относящиеся к объектам, фактически относятся к ссылочному типу, фактически вызывая вызов по ссылке для большинства целей и целей без мелкозернистого управления (и сложности), предоставляемого, например, С++.