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

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

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

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

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

Мы бы чаще говорили, что переменная "переданная по ссылке" может быть изменена. Этот термин появляется в учебниках, потому что теоретикам языка нужен был способ отличить, как вы относитесь к примитивным типам данных (int, bool, byte) от сложных и структурированных объектов (массив, потоки, класс), то есть объекты с возможно неограниченной памятью. распределение.

jlau 04.08.2020 14:35

Хочу отметить, что в большинстве случаев об этом не нужно думать. Я программировал java много лет, пока не изучил C++. До этого момента я понятия не имел, что такое передача по ссылке и передача по значению. У меня всегда работало интуитивно понятное решение, поэтому java - один из лучших языков для начинающих. Так что, если вы в настоящее время беспокоитесь, если вашей функции нужна ссылка или значение, просто передайте его как есть, и все будет в порядке.

Tobias 20.10.2020 14:44

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

The Student 20.10.2020 22:05

Короче говоря, эта путаница возникает из-за того, что в Java все непримитивные типы данных обрабатываются / доступны через Рекомендации. Однако передача всегда имеет значение. Таким образом, для всех непримитивных типов ссылка передается по его значению. Все примитивные типы также передаются по значению.

Ozair Kafray 10.11.2020 09:56

Для тех, кто идет с C++ и начинает путаться при касании Java, это может помочь как ярлык.

jack 27.12.2020 00:06

Я нашел это весьма полезным: baeldung.com/java-pass-by-value-or-pass-by-reference

Natasha Kurian 20.01.2021 12:52

В этом разница между foo.bar (sth) и foo = sth. В первом случае объект изменяется с использованием переменной, указывающей на него, и сама переменная, указывающая на объект, не была изменена. Однако во втором случае сама переменная, которая раньше указывала на объект, изменилась и теперь указывает на другой объект. Если у вас есть опыт работы с C++: указатель - это переменная, которая содержит адрес памяти, а ссылка имеет тот же адрес памяти, что и элемент, на который она ссылается. В Java действительно указатель передается по значению, но Javaistas называют его ссылка.!

aderchox 28.02.2021 09:28
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
6 950
7
2 136 566
83

Ответы 83

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

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

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

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

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

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

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

Brian Goetz 01.11.2020 18:52

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

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

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

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.

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

Java всегда передача по стоимости.
К сожалению, мы вообще никогда не обрабатываем объект, вместо этого манипулируем дескрипторами объектов, называемыми Рекомендации(which are passed by value of course). Выбранная терминология и семантика легко сбивают с толку многих новичков.

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

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.

так что же происходит с «Фифи» в 1-м примере? Он перестает существовать, никогда не создавался или существует в куче, но без ссылочной переменной в стеке?

dbrewster 29.09.2020 21:03

Что вы имеете в виду под последним предложением but it is not possible to change the value of the variable aDog itself.?

TMOTTM 16.10.2020 21:33

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

user36800 27.10.2020 21:46

@ user36800: Вы ошибаетесь. Вы проработали пример с Фифи и внимательно просмотрели результаты? Убедитесь, что foo(aDog); действительно изменил нетaDog, несмотря на то, что foo перезаписал значение d, показывая, что действительно все входные данные функции передаются по значению.

user21820 23.12.2020 18:03

@ user21820: Да, я проработал примеры, и оба они мне понятны. В своем предыдущем комментарии я сделал два заявления. Если вы можете указать, с чем вы не согласны, я могу попытаться лучше понять ваш последний комментарий. Спасибо.

user36800 28.12.2020 05:29

@ user36800: Что ж, оба утверждения неверны. Передача объекта по ссылке будет означать, что если функция изменяет переменную, то она изменяет сам объект. Это не то, что происходит в Java; объекты не могут передаваться по ссылке, но вместо этого можно передавать только ссылки в качестве входных данных в функцию, и когда функция выполняет d = new Dog("Fifi");, она перезаписывает входную переменную d, которая магазины является ссылкой, но не является «объектом, переданным по ссылке». Сравните с &d в сигнатуре функции в C, которая будет передаваться по ссылке. [продолжение]

user21820 28.12.2020 08:49

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

user21820 28.12.2020 08:51

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

user36800 29.12.2020 02:32

Java передается по значению, НО В противном случае существует ВРЕМЕННОЕ РЕШЕНИЕ. Использование массива, содержащего ссылку на объект. Несмотря на то, что массив передается как значение, поэтому, если вы измените массив, он не будет отражаться. Но массив содержит ссылку на объект Integer. ПОПРОБУЙТЕ ЭТО: - {Целое число [] i = новое целое число [] {новое целое число (0)}; System.out.println (я [0]); я [0] = я [0] +1; System.out.println (я [0]); изменить (я); System.out.println (я [0]); } общедоступное статическое изменение недействительности (целое число [] я) {я [0] = я [0] +1; }

Prateek Pande 13.01.2021 16:27

@dbrewster извини, но ... "Фифи" больше нет среди нас

ghilesZ 02.02.2021 22:51

@ghilesZ рип Фифи

Matthew 10.02.2021 07:11

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

Как правило, в 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). Это можно было бы использовать с оператором & для передачи адреса переменной.

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

Это просто неправильно. То, что Java называет «ссылкой», C++ называет «указателем». То, что C++ называет «ссылкой», в Java не существует. Ссылка C++ - это указатель типа, но с глобальной областью действия. Когда вы изменяете ссылку C++, все вхождения этих ссылок изменяются как в вызываемой функции, так и в вызывающей функции. Java не может этого сделать. Java передается строго по значению, и изменения в ссылках на Java строго локальны. Вызываемая Java функция не может изменить ссылочное значение вызывающей функции. Вы можете эмулировать ссылку C++, используя объекты-оболочки, такие как AtomicReference.

Talijanac 18.08.2020 12:14

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

Paul de Vrieze 15.09.2020 20:13

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

Talijanac 16.09.2020 11:40

Для примера правильной передачи по ссылке см. Программу подкачки здесь: geeksforgeeks.org/references-in-c Невозможно написать метод подкачки в Java с такими же побочными эффектами. Существует «качество» (поведение операторов языка) для ссылок C++, которого нет в ссылках Java или указателях C.

Talijanac 16.09.2020 11:45

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

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

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

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

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

Печать:

Before: a = 2, b = 3
After: a = 2, b = 3

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

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

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

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

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

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

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

Я только что заметил, что вы упомянули моя статья.

В спецификации Java говорится, что все в Java передается по значению. В Java нет такой вещи, как «передача по ссылке».

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

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» его имя теперь «Max» - все та же Dog; значение myDog не изменилось).

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

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

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

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

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

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

В C++: Примечание. Неверный код - утечка памяти! Но это демонстрирует суть.

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

Это показывает, что java не передается по значению, поскольку он не копирует весь объект в стек, как это делает C++, как показано в примере выше - ..., Dog obj, ...

Solubris 26.11.2020 02:03

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

Eclipse 27.11.2020 01:03

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

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

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

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

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 будет содержать возвращаемое значение. Очевидно, вы можете сделать это другими способами, например, структурировать код, чтобы в этом не было необходимости, или создать класс, который может содержать возвращаемое значение или позволять его устанавливать. Но простой шаблон, доступный вам в C++ выше, недоступен в Java.

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

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

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

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

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

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

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

Взгляните на этот код. Этот код не выдаст 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.

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

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

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

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

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

user207421 07.06.2016 06:59

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

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

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

  • Ученик: Мастер, означает ли это, что Java передается по ссылке?
  • Владелец: Кузнечик, No.

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

  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» содержат одно и то же значение 3bad086a.
    • Не пытайтесь это сделать, но person == anotherReferenceToTheSamePersonObject будет истинным.
    • Обе переменные имеют ОДИНАКОВЫЕ КОПИИ ссылки, и обе они относятся к одному и тому же объекту Person, ОДНОМУ объекту в куче, а НЕ КОПИИ.

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

Pass by Value

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

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

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

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

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

Java is pass-by-value because inside a method you can modify the referenced Object as much as you want but no matter how hard you try you'll never be able to modify the passed variable that will keep referencing (not p _ _ _ _ _ _ _) the same Object no matter what!


The changeName function above will never be able to modify the actual content (the bit values) of the passed reference. In other word changeName cannot make Person person refer to another Object.


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

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

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

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» является ссылкой (указателем на объект) и не может быть изменена.

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

Напомним, все передается по значению.

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

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

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

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


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

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

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

    public static void changeReference(Foo a)
    

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

    changeReference(f);
    

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

    Foo b = new Foo("b");
    

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

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

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

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

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

dan carter 07.12.2020 11:48

Это красивое объяснение. Спасибо.

Liam Bohl 26.01.2021 02:29

Нет, не по ссылке.

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

When the method or constructor is invoked (§15.12), the values of the actual argument expressions initialize newly created parameter variables, each of the declared type, before execution of the body of the method or constructor. The Identifier that appears in the DeclaratorId may be used as a simple name in the body of the method or constructor to refer to the formal parameter.

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

В Java не существует такой вещи, как «постоянная ссылка», если программист не укажет «finally».

user207421 02.08.2013 14:05

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

fatma.ekici 09.09.2013 17:06

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

user207421 25.09.2014 04:41

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

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

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

«Передавать его внутреннюю ценность» бессмысленно.

user207421 17.09.2017 13:09

@EJP спасибо за заметку, извините за плохой английский с 2013 года, я все это отредактировал, если вы видите лучшую формулировку, вы можете предложить или отредактировать

Khaled.K 17.09.2017 19:41

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

user6091735 18.11.2017 19:26

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

Passing Variables into Methods (Objective 7.3)

7.3 Determine the effect upon object references and primitive values when they are passed into methods that perform assignments or other modifying operations on the parameters.

Methods can be declared to take primitives and/or object references. You need to know how (or if) the caller's variable can be affected by the called method. The difference between object reference and primitive variables, when passed into methods, is huge and important. To understand this section, you'll need to be comfortable with the assignments section covered in the first part of this chapter.

Passing Object Reference Variables

When you pass an object variable into a method, you must keep in mind that you're passing the object reference, and not the actual object itself. Remember that a reference variable holds bits that represent (to the underlying VM) a way to get to a specific object in memory (on the heap). More importantly, you must remember that you aren't even passing the actual reference variable, but rather a copy of the reference variable. A copy of a variable means you get a copy of the bits in that variable, so when you pass a reference variable, you're passing a copy of the bits representing how to get to a specific object. In other words, both the caller and the called method will now have identical copies of the reference, and thus both will refer to the same exact (not a copy) object on the heap.

For this example, we'll use the Dimension class from the java.awt package:

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

When we run this class, we can see that the modify() method was indeed able to modify the original (and only) Dimension object created on line 4.

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

Notice when the Dimension object on line 4 is passed to the modify() method, any changes to the object that occur inside the method are being made to the object whose reference was passed. In the preceding example, reference variables d and dim both point to the same object.

Does Java Use Pass-By-Value Semantics?

If Java passes objects by passing the reference variable instead, does that mean Java uses pass-by-reference for objects? Not exactly, although you'll often hear and read that it does. Java is actually pass-by-value for all variables running within a single VM. Pass-by-value means pass-by-variable-value. And that means, pass-by-copy-of- the-variable! (There's that word copy again!)

It makes no difference if you're passing primitive or reference variables, you are always passing a copy of the bits in the variable. So for a primitive variable, you're passing a copy of the bits representing the value. For example, if you pass an int variable with the value of 3, you're passing a copy of the bits representing 3. The called method then gets its own copy of the value, to do with it what it likes.

And if you're passing an object reference variable, you're passing a copy of the bits representing the reference to an object. The called method then gets its own copy of the reference variable, to do with it what it likes. But because two identical reference variables refer to the exact same object, if the called method modifies the object (by invoking setter methods, for example), the caller will see that the object the caller's original variable refers to has also been changed. In the next section, we'll look at how the picture changes when we're talking about primitives.

The bottom line on pass-by-value: the called method can't change the caller's variable, although for object reference variables, the called method can change the object the variable referred to. What's the difference between changing the variable and changing the object? For object references, it means the called method can't reassign the caller's original reference variable and make it refer to a different object, or null. For example, in the following code fragment,

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

reassigning g does not reassign f! At the end of the bar() method, two Foo objects have been created, one referenced by the local variable f and one referenced by the local (argument) variable g. Because the doStuff() method has a copy of the reference variable, it has a way to get to the original Foo object, for instance to call the setName() method. But, the doStuff() method does not have a way to get to the f reference variable. So doStuff() can change values within the object f refers to, but doStuff() can't change the actual contents (bit pattern) of f. In other words, doStuff() can change the state of the object that f refers to, but it can't make f refer to a different object!

Passing Primitive Variables

Let's look at what happens when a primitive variable is passed to a method:

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

In this simple program, the variable a is passed to a method called modify(), which increments the variable by 1. The resulting output looks like this:

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

Notice that a did not change after it was passed to the method. Remember, it was a copy of a that was passed to the method. When a primitive variable is passed to a method, it is passed by value, which means pass-by-copy-of-the-bits-in-the-variable.

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

Кратчайший ответ :)

  • В Java есть передача по значению (и передача по ссылке по значению).
  • В C# также есть передача по ссылке

В C# это достигается с помощью ключевых слов out и ref.

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

Здесь следует пример передачи по ссылке (C#). Эта функция не существует в 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 (C#): передача массивов по ссылке и из

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

в java мы можем сделать: someArray = InitArray(someArray), если у нас есть это: static int [] InitArray( int[] arr){ ... return ...}

Kachna 27.02.2016 14:06

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

bvdb 27.02.2016 19:28

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

Я взял этот код и объяснение из книги по сертификации 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

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

Мне кажется, что споры о передаче по ссылке и передаче по значению бесполезны.

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

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

la da di da da da da

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

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

Dude156 13.09.2020 00:22

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 | 
      +---+ 

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

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

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

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

Иногда 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 функции она не будет напрямую передавать фактическое значение ссылки объекта test, но создаст копию 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

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

Язык программирования 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.

Это очень вводит в заблуждение. Вы определенно может изменяете значение аргумента внутри метода.

Gray 28.11.2017 23:33

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

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

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

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

В этой строке вступает в игру «проход по значению» ...

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

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

В методе tricky:

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

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

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

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

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

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

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

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

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

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

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

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

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.

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

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

Passing by reference means the called functions' parameter will be the same as the callers' passed argument (not the value, but the identity - the variable itself).

Pass by value means the called functions' parameter will be a copy of the callers' passed argument.

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

In call-by-reference evaluation (also referred to as pass-by-reference), a function receives an implicit reference to a variable used as argument, rather than a copy of its value. This typically means that the function can modify (i.e. assign to) the variable used as argument—something that will be seen by its caller.

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

In call-by-value, the argument expression is evaluated, and the resulting value is bound to the corresponding variable in the function [...]. If the function or procedure is able to assign values to its parameters, only its local copy is assigned [...].

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

When the method or constructor is invoked (§15.12), the values of the actual argument expressions initialize newly created parameter variables, each of the declared type, before execution of the body of the method or constructor.

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

В чем ценность аргумента?

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

There are three kinds of reference types: class types, array types, and interface types. Their values are references to dynamically created class instances, arrays, or class instances or arrays that implement interfaces, respectively.

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

The reference values (often just references) are pointers to these objects, and a special null reference, which refers to no object.

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

Так

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

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

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

This typically means that the function can modify (i.e. assign to) the variable used as argument—something that will be seen by its caller.

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


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

Разберитесь в этом за 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
}

Во всех ответах мы видим, что передача Java по значению или, скорее, как @Gevorg написал: «передача-копией-значения-переменной», и это идея, которую мы должны иметь в виду все время.

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

Из [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]

shows nicely the mechanism watch max 5 min

(Передача по ссылке) передача по копии значения переменной

Пример из [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 по значению или, скорее, «передачи по копии-значению-переменной», как написал @Gevorg.

  1. REF 1 Lynda.com
  2. REF 2 Профессор Мехран Сахами
  3. REF 3 c4learn

В 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)
}}

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

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

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 / C++, java использует передачу по значению, поэтому для примитивных типов данных (int, char и т. д.) Изменения в функции не отражаются в вызывающей функции. Но когда вы передаете объект и в функции меняете его элементы данных или вызываете функции-члены, которые могут изменять состояние объекта, вызывающая функция получит изменения.

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

mrres1 25.01.2015 04:30

@ mrres1 Не совсем правильно. Вы можете определить только один класс / интерфейс верхнего уровня общественный для каждого файла. Поддержка нескольких классов в файле - это пережиток первой версии Java, в которой не было вложенных классов, но она все еще поддерживается, хотя ее часто осуждают.

MrBackend 19.03.2015 16:53

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

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

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

PersonClass variable2;

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

variable2 = variable1; 


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

// теперь переменная1 указывает на переменную3

variable1 = variable3;

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

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

Mary 32
Andre 45

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

страница в Интернете

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

Mox 18.01.2017 14:38

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

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

For those coming from C#: THERE IS NO "out" parameter.

For those coming from PASCAL: THERE IS NO "var" parameter.

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

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

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

/ ** Эта передача по значению имеет ощущение передачи по ссылке.

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

Но из этого примера мы можем понять, что это фактически передача только по значению, имея в виду, что здесь мы передаем ссылку как значение. т.е. ссылка передается по значению. Вот почему могут меняться, и по-прежнему остается в силе после локальной области видимости. Но мы не можем изменить реальную ссылку за пределами исходной области. что это означает, демонстрируется в следующем примере 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 такое же, как в Example3.

* /

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's 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]

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

p скопирует ссылочное значение 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 остаются такими же

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

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

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

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

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

Diagram of how data is created and passed

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

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

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

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

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

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

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

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

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

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

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

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

В Java эти термины не были пересмотрены. Ни у кого нет. Он просто избегает использования термина C «указатель».

user207421 07.06.2016 07:05

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

Cdaragorn 01.09.2016 00:05

Есть очень простой способ понять это. Давайте возьмем C++ по ссылке.

#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 () присвоил новое значение переданной «ссылке», он фактически изменил значение, которое было передано из самой main, объясняя последний вызов 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

Вуаля, ссылка на Foo в основном, которая была передана в bar, все еще не изменилась!

Этот пример ясно показывает, что java - это не то же самое, что C++, когда мы говорим «передать по ссылке». По сути, java передает «ссылки» как «значения» функциям, то есть java передается по значению.

Есть ли проблема в вашей версии C++, когда вы рискуете получить segfault, когда Foo(99) выходит за рамки, но вы ссылаетесь на него в своем основном методе?

matt 15.06.2016 11:14

В самом деле. Ах, исходит от использования Java в течение 10 лет. Но идея все еще актуальна. И я исправил это сейчас.

Ravi Sanwal 16.06.2016 18:16

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

matt 16.06.2016 20:41

Этот ответ полезен только для тех, кто имеет опыт работы с C++, кто хочет определить «ссылку» так, как вы это делаете, в соответствии с определением C++. Это не всегда так.

Loduwijk 02.08.2017 18:41

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

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

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

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

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

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

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

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

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 не удается! Это главный источник путаницы. В методе main () pnt1 и pnt2 - это не что иное, как ссылки на объекты. Когда вы передаете pnt1 и pnt2 методу tricky (), Java передает ссылки по значению, как и любой другой параметр. Это означает, что ссылки, переданные методу, на самом деле являются копиями исходных ссылок. На рисунке 1 ниже показаны две ссылки, указывающие на один и тот же объект после того, как Java передает объект методу.

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

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

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

(Не для Java) Базовая структура функции подкачки

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

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

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

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

Возможно, вы захотите уточнить свое последнее предложение. Моя первая реакция на «передачу только значений, а не указателей ...» заключается в том, что ваша реализация Java, вероятно, выполняет именно это, передает указатель. Тот факт, что вы не можете разыменовать этот указатель, кажется несущественным.

Loduwijk 02.08.2017 18:25

Главное краеугольное знание должно быть цитируемым,

When an object reference is passed to a method, the reference itself is passed by use of call-by-value. However, since the value being passed refers to an object, the copy of that value will still refer to the same object referred to by its corresponding argument.

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

Кажется, что в 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

harry

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

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

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

Java, безусловно, без сомнения, является «передачей по значению». Кроме того, поскольку 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]

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

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

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

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

public class Doggie {

    public static void main(String...args) {
        System.out.println("At the owner's 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's 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's 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's 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

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

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

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

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

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

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

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

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

Как это устроено

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

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

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

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 указывает на тот же dummyObject.

Иллюстрации в одном из ответов в этой ветке (https://stackoverflow.com/a/12429953/4233180) могут помочь получить более глубокое понимание.

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

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

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

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

Допустим, у нас есть воздушный шар класса, как показано ниже.

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

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

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

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

Когда мы используем оператор new для создания экземпляра класса, создается экземпляр, а переменная содержит ссылочную позицию памяти, в которой сохраняется объект. В нашем примере предположим, что «красный» указывает на 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
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Во-первых, мы должны понять, что в 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 мы ищем принцип работы присвоение переменных. Нашел в документы:

One of the most common operators that you'll encounter is the simple assignment operator " = " [...] it assigns the value on its right to the operand on its left:

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

This operator can also be used on objects to assign object references [...]

Понятно, что этот оператор действует двумя разными способами: присваивает значения и присваивает ссылки. Последнее, когда это объект ... первое, когда это не объект, то есть примитив. Но так, можем ли мы понять, что параметры функции 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's value the width's 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's value an integer other than width's 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's value an integer other than width's 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's 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's value the width's value
->  height is 1 and width is 1, are we the same thing now? 

[3] Assign to height's value an integer other than width's 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's value an integer other than width's 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's value
->  height is 1 and width is 9, we are different things again! 

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

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

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

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

This operator can also be used on objects to assign object references

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

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

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

Что касается объектов, сами объекты нельзя передать методам. Итак, мы передаем ссылку (адрес) объекта. Мы можем управлять исходным объектом, используя эту ссылку.

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

Account account1 = new Account();

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

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

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

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

Pass by value example 1

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

public class Test
{
    public static void reverseArray(int[] array1)
    {
        // ...
    }

    public static void main(String[] args)
    {
        int[] array1 = { 1, 10, -7 };
        int[] array2 = { 5, -190, 0 };

        reverseArray(array1);
    }
}

Pass by value example 2

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

array1[0] = 5;

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

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

array1 = array2;

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

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

Итак, давайте сейчас напишем сразу все, что мы сделали.

public class Test
{
    public static int[] reverseArray(int[] array1)
    {
        int[] array2 = { -7, 0, -1 };

        array1[0] = 5; // array a becomes 5, 10, -7

        array1 = array2; /* array1 of reverseArray starts
          pointing to c instead of a (not shown in image below) */
        return array2;
    }

    public static void main(String[] args)
    {
        int[] array1 = { 1, 10, -7 };
        int[] array2 = { 5, -190, 0 };

        array1 = reverseArray(array1); /* array1 of 
         main starts pointing to c instead of a */
    }
}

И теперь, когда метод reverseArray завершен, его ссылочные переменные (array1 и array2) исчезли. Это означает, что теперь у нас есть только две ссылочные переменные в основном методе array1 и array2, которые указывают на массивы c и b соответственно. Никакая ссылочная переменная не указывает на объект (массив) a. Таким образом, он имеет право на сборку мусора.

Вы также можете присвоить значение array2 в основном array1. array1 начнет указывать на b.

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

как Майкл говорит в комментариях:

objects are still passed by value even though operations on them behave like pass-by-reference. Consider void changePerson(Person person){ person = new Person(); } the callers reference to the person object will remain unchanged. Objects themselves are passed by value but their members can be affected by changes. To be true pass-by-reference, we would have to be able to reassign the argument to a new object and have the change be reflected in the caller.

Описание java как «передачи по значению» сильно вводит в заблуждение. Для непримитивных типов Java использует «передачу по значению ссылки». «Передача по значению» означает, что значение копируется при передаче в метод. Это не так, ссылка скопирована.

AutomatedMike 03.05.2019 15:18

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

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

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

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

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

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

Dennis 26.08.2018 00:08

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

Torben 03.10.2018 07:40

@Dennis Строки - это не примитивы, это объекты.

nasch 27.12.2018 21:49

Дело не в том, что «в твоей книге». «Передавать по ссылке» и «Передавать по значению» - это стандартные отраслевые термины, которые имеют очень конкретные определения. По этим определениям Java - это «передача по значению» без исключений.

Sanjeev 23.01.2019 03:01

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

AndrewF 25.12.2019 03:00

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

Solubris 26.11.2020 02:25

Люди, знающие только Java, никогда не поймут, что «передача по ссылке» означает не что иное, как передачу ссылки (которая, по сути, является указателем) по значению. Который. конечно, это то, что делает JVM.

Ingo 01.12.2020 13:44

Короче:

  1. Непримитивы: Java передает Ценность ссылки.
  2. Примитивы: просто ценность.

Конец.

(2) слишком просто. Теперь, если вы хотите подумать о том, что подразумевает (1), представьте, что у вас есть класс Apple:

class Apple {
    private double weight;
    public Apple(double weight) {
        this.weight = weight;
    }
    // getters and setters ...

}

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

class Main {
    public static void main(String[] args) {
        Apple apple = new Apple(3.14);
        transmogrify(apple);
        System.out.println(apple.getWeight()+ " the goose drank wine...";

    }

    private static void transmogrify(Apple apple) {
        // does something with apple ...
        apple.setWeight(apple.getWeight()+0.55);
    }
}

о ... но вы, наверное, это знаете, вас интересует, что происходит, когда вы делаете что-то вроде этого:

class Main {
    public static void main(String[] args) {
        Apple apple = new Apple(3.14);
        transmogrify(apple);
        System.out.println("Who ate my: "+apple.getWeight()); // will it still be 3.14? 

    }

    private static void transmogrify(Apple apple) {
        // assign a new apple to the reference passed...
        apple = new Apple(2.71);
    }


}

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

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

Это важно, так как это экономит много памяти и увеличивает скорость.

public class Test {

    static class Dog {
        String name;

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + ((name == null) ? 0 : name.hashCode());
            return result;
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            Dog other = (Dog) obj;
            if (name == null) {
                if (other.name != null)
                    return false;
            } else if (!name.equals(other.name))
                return false;
            return true;
        }

        public String getName() {
            return name;
        }

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

        Dog(String sd) {
            this.name = sd;
        }
    }
    /**
     * 
     * @param args
     */
    public static void main(String[] args) {
        Dog aDog = new Dog("Max");

        // we pass the object to foo
        foo(aDog);
        Dog oldDog = aDog;

        System.out.println(" 1: " + aDog.getName().equals("Max")); // false
        System.out.println(" 2 " + aDog.getName().equals("huahua")); // false
        System.out.println(" 3 " + aDog.getName().equals("moron")); // true
        System.out.println(" 4 " + " " + (aDog == oldDog)); // true

        // part2
        Dog aDog1 = new Dog("Max");

        foo(aDog1, 5);
        Dog oldDog1 = aDog;

        System.out.println(" 5 : " + aDog1.getName().equals("huahua")); // true
        System.out.println(" part2 : " + (aDog1 == oldDog1)); // false

        Dog oldDog2 = foo(aDog1, 5, 6);
        System.out.println(" 6 " + (aDog1 == oldDog2)); // true
        System.out.println(" 7 " + (aDog1 == oldDog)); // false
        System.out.println(" 8 " + (aDog == oldDog2)); // false
    }

    /**
     * 
     * @param d
     */
    public static void foo(Dog d) {
        System.out.println(d.getName().equals("Max")); // true

        d.setName("moron");

        d = new Dog("huahua");
        System.out.println(" -:-  " + d.getName().equals("huahua")); // true
    }

    /**
     * 
     * @param d
     * @param a
     */
    public static void foo(Dog d, int a) {
        d.getName().equals("Max"); // true

        d.setName("huahua");
    }

    /**
     * 
     * @param d
     * @param a
     * @param b
     * @return
     */
    public static Dog foo(Dog d, int a, int b) {
        d.getName().equals("Max"); // true
        d.setName("huahua");
        return d;
    }
}

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

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

Для потока Stack хранит все локальные данные, метаданные программы, данные примитивного типа и ссылки на объекты. Причем, Heap отвечает за хранение реального объекта.

Book book = new Book("Effective Java");

В приведенном выше примере ссылочной переменной является «книга», которая хранится в стеке. Экземпляр, созданный оператором new -> new Book («Эффективная Java»), сохраняется в Heap. Переменная ref "book" имеет адрес объекта, размещенного в Heap. Допустим, адрес 1001.

Рассмотрите возможность передачи примитивного типа данных, то есть int, float, double и т. д.

public class PrimitiveTypeExample { 
    public static void main(string[] args) {
       int num = 10;
       System.out.println("Value before calling method: " + num);
       printNum(num);
       System.out.println("Value after calling method: " + num);
    }
    public static void printNum(int num){
       num = num + 10;
       System.out.println("Value inside printNum method: " + num);
    }
}

Выход: Значение перед вызовом метода: 10 Значение внутри метода printNum: 20 Значение после вызова метода: 10

int num = 10; -> это выделяет память для int в стеке запущенного потока, потому что это примитивный тип. Теперь, когда вызывается printNum (..), в том же потоке создается частный стек. Когда "num" передается этому методу, копия "num" создается в кадре стека методов. число = число + 10; -> это добавляет 10 и изменяет переменную int в кадре стека методов. Следовательно, исходное число вне кадра стека методов остается неизменным.

Рассмотрим пример передачи объекта настраиваемого класса в качестве аргумента.

В приведенном выше примере переменная ref "book" находится в стеке потока, выполняющего программу, а объект класса Book создается в пространстве кучи, когда программа выполняет new Book (). Это место в памяти в куче называется «книгой». Когда «книга» передается в качестве аргумента метода, копия «книги» создается в частном стековом фрейме метода в том же стеке потока. Следовательно, скопированная ссылочная переменная указывает на тот же объект класса «Книга» в куче.

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

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

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

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

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

String name = "Mehrose";  // name referencing to 100

ChangeContenet(String name){
 name = "Michael"; // refernce has changed to 1001

} 
System.out.print(name);  //displays Mehrose

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

String names[] = {"Mehrose","Michael"};

changeContent(String[] names){
  names[0] = "Rose";
  names[1] = "Janet"

}

System.out.println(Arrays.toString(names)); //displays [Rose,Janet]

Как мы уже говорили, мы не можем изменить скопированную ссылку в вызове функции, и мы также видели в случае одного объекта String. Причина в том, что переменная names [] ссылается на, скажем, 200, а имена [0] ссылаются на 205 и так далее. Вы видите, что мы не изменили ссылку names [], она по-прежнему указывает на старую ссылку все еще после вызова функции, но теперь ссылка names [0] и names [1] была изменена. Мы по-прежнему придерживаемся нашего определения, что мы не можем изменить ссылку на ссылочную переменную, поэтому мы этого не сделали.

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

Ты не можешь этого сделать

Student student1= new Student("Mehrose");

changeContent(Student Obj){
 obj= new Student("Michael") //invalid
 obj.setName("Michael")  //valid

}

О: 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 не удается! Это главный источник путаницы. В методе main () pnt1 и pnt2 - это не что иное, как ссылки на объекты. Когда вы передаете pnt1 и pnt2 методу tricky (), Java передает ссылки по значению, как и любой другой параметр. Это означает, что ссылки, переданные в метод, на самом деле являются копиями исходных ссылок. На рисунке 1 ниже показаны две ссылки, указывающие на один и тот же объект после того, как Java передает объект методу.


Рисунок 1. После передачи в метод объект будет иметь как минимум две ссылки.

Java копирует и передает ссылку по значению, а не по объекту. Таким образом, манипуляции с методом изменят объекты, поскольку ссылки указывают на исходные объекты. Но поскольку ссылки являются копиями, обмен не удастся. Обмен ссылками на методы, но не на исходные ссылки. К сожалению, после вызова метода у вас остаются только исходные ссылки без замены. Чтобы замена прошла успешно вне вызова метода, нам нужно поменять местами исходные ссылки, а не копии.

Я вижу, что все ответы содержат одно и то же: передать по значению. Однако недавнее сообщение Брайана Гетца о проекте Valhalla на самом деле отвечает на это иначе:

Indeed, it is a common “gotcha” question about whether Java objects are passed by value or by reference, and the answer is “neither”: object references are passed by value.

Вы можете прочитать больше здесь: Штат Валгалла. Раздел 2: языковая модель

Редактировать: Брайан Гетц - архитектор языка Java, возглавляющий такие проекты, как Project Valhalla и Project Amber.

Редактировать-2020-12-08: обновлено Штат Валгалла

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

Sanjeev 21.02.2020 01:24

Я думаю, что архитектор Java Language Architect, который возглавляет Project Amber и Project Valhalla, является надежным источником, утверждающим, что это не проходит по ценности.

Mr.Robot 02.04.2020 19:28

Во-первых, я не думаю, что он заслуживает большего доверия, чем Джеймс Гослинг, создатель Java, который четко заявляет в своей книге «Язык программирования Java», что Java действительно является передачей по значению (глава 2, раздел 2.6.5) . Во-вторых, хотя Гетц говорит, что это не PBV и не PBR, он затем продолжает говорить, что ссылки ПРОХОДЯТ ПО ЗНАЧЕНИЮ, тем самым противореча самому себе. Если вы знаете Java, вы также знаете, что примитивы также проходят по значению. Поскольку все в Java передается по значению, Java является языком PASS BY VALUE.

Sanjeev 02.04.2020 21:17

Другими источниками, которые заслуживают большего доверия, чем Гетц, являются Ахо, Лам, Сетхи и Ульман, которые хорошо известны своей книгой «Компиляторы: принципы, методы и инструменты», стандартным учебником для колледжей по созданию компиляторов. В разделе 1.6.6 2-го издания этой книги также говорится, что Java - это передача по значению.

Sanjeev 02.04.2020 21:49

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

Stephen C 05.04.2020 14:40

Это единственный ответ, который до сих пор имел смысл. Объекты не передаются как чистые значения, потому что они не копируются, как при передаче по значению в C++.

AFP_555 17.11.2020 15:11

Пожалуйста прочтите это.

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

Для получения дополнительной информации:

https://www.javaworld.com/article/3512039/does-java-pass-by-reference-or-pass-by-value.html

Я бы сказал по-другому:

В java передаются ссылки (но не объекты), и эти ссылки передаются по значению (сама ссылка копируется, и в результате у вас есть 2 ссылки, и у вас нет контроля по 1-й ссылке в методе).

Просто скажу: передача по значению может быть недостаточно понятной для новичков. Например, в Python такая же ситуация, но есть статьи, в которых описывается, что это называется pass-by-reference, используются только ссылки на причину.

Четко и ясно!

Abhishek Attri 22.12.2020 13:05

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

Пожалуйста, обратитесь к приведенному ниже примеру,

public class ObjectReferenceExample {

    public static void main(String... doYourBest) {
            Student student = new Student();
            transformIntoHomer(student);
            System.out.println(student.name);
    }

    static void transformIntoDuleepa(Student student) {
            student.name = "Duleepa";
    }
}
class Student {
    String name;
}

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

Запуск этой программы должен помочь любому понять эту концепцию. pastebin.com/VEc8NQcX

Mohith7548 03.11.2020 13:26

В этом примере вы не скопировали объект Student, поэтому я думаю, что фраза «передача копии значения также изменяет реальное значение объекта» вводит в заблуждение. То, что вы копируете и передаете по значению, - это ссылка объекту. Объект продолжает жить в куче, а их всего один. Вот почему, когда вы используете новую ссылку для изменения объекта, вы изменяете его для всех остальных, у кого есть ссылка на объект. Есть только один объект. Когда аргумент функции является примитивным типом, а не ссылкой на объект, он также копируется.

Chrispher 14.11.2020 14:57

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

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

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

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

Иногда 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;
}

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

изменить значение Разберемся пошагово:

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

первая иллюстрация

new PassByValue().changeValue(t);

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

вторая иллюстрация

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

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

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, как показано ниже:

третья иллюстрация

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

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

  • pass by value в Java - это от НЕ РАВНЫЙ до pass by value в C++, хотя это звучит так, вероятно, поэтому возникает путаница.

Разбивая это:

  • pass by value в C++ означает передачу значения объекта (если объект), буквально копию объекта
  • pass by value в Java означает передачу значения адреса объекта (если объект), а не "значение" (копию) объекта, например C++.
  • С помощью pass by value в Java, работая с объектом (например, myObj.setName("new")) внутри функции имеет эффекты на объекте вне функции; на pass by value в C++, он имеет эффекты НЕТ на внешнем.
  • Однако, согласно pass by reference в C++, работа с объектом в функции ДЕЛАЕТ оказывает влияние на внешний объект! Подобно (просто похоже, не то же самое) pass by value в Java, нет? .. и люди всегда повторяют «в Java нет передачи по ссылке», => БУМ, начинается путаница ...

Итак, друзья, все сводится к разница определения терминологии (для разных языков), вам просто нужно знать, как это работает, и все (хотя иногда немного сбивает с толку, как это называется, признаю)!

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

Java всегда передать значение с новой ссылкой

(Таким образом, вы можете изменить исходный объект, но не можете получить доступ к исходной ссылке)

Вот более точное определение:

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

Итак, в C / C++ вы можете создать функцию, которая меняет местами два значения, переданные по ссылке:

void swap(int& a, int& b) 
{
    int& tmp = a;
    a = b;
    b = tmp;
}

Вы можете видеть, что у него есть уникальная ссылка на a и b, поэтому у нас нет копии, tmp просто хранит уникальные ссылки.

Та же функция в java не имеет побочных эффектов, передача параметров такая же, как в приведенном выше коде, без ссылок.

Хотя java работает с указателями / ссылками, параметры не являются уникальными указателями, в каждой атрибуции они копируются, а не просто назначаются как C / C++

Другие вопросы по теме