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

avatar
user4315
2 сентября 2008 в 20:14
2237197
94
7149

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

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

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

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

Источник
jlau
4 августа 2020 в 11:35
2

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

Tobias
20 октября 2020 в 11:44
2

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

The Student
20 октября 2020 в 19:05
13

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

Ozair Kafray
10 ноября 2020 в 06:56
2

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

jack
26 декабря 2020 в 21:06
0

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

Natasha Kurian
20 января 2021 в 09:52
0

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

aderchox
28 февраля 2021 в 06:28
0

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

hippietrail
3 июня 2021 в 04:56
0

Что тогда будет означать для некоторого языка передать ссылку не по значению? Передача указателей на указатели? Есть ли такие языки? Разве языки, такие как C / C ++, также не передают ссылки и указатели по значению ??

Ответы (94)

avatar
9 мая 2022 в 17:53
-1

Простыми словами:

  • Передача по значению: относится к передаче копии значения.

  • Передача по ссылке: относится к передаче реальной ссылки переменной в памяти.

Пример:

1- Пример передачи по значению: посмотрите комментарии в примере, чтобы понять концепцию.

 public static void main(String[] args) {
      ...
      int y = 5;
      System.out.println(y); // it will print 5
      myMethod(y);
      System.out.println(y); it will also print 5
  }

  public static void myMethod(int x) {
      ...
      x = 4; // myMethod has a copy of x, so it doesn't
             // overwrite the value of variable y used
             // in main() that called myMethod
  }

2- Передача по ссылке Пример: Здесь сохраняется копия адреса (или ссылки) на параметр, а не само значение.

public class Cat {
    private String name;

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

    // Getter
    public void getName() {
        return this.name;
    }
}

public class Example {
    public static void adoptCat(Cat c, String newName) {
        c.setName(newName);
    }

    public static void main(String[] args) {
        Cat myCat = new Cat();
        myCat.setName("Charlotte");
        System.out.println(myCat.getName()); // prints "Charlotte"

        adoptCat(myCat, "Lydia");
        System.out.println(myCat.getName()); // prints "Lydia"
    }
}

Преимущества / недостатки:

Перейти по ссылке Преимущества:

Существуют различные преимущества передачи по ссылке:

  1. Нет копии аргумента, следовательно, он очень быстрый.
  2. Чтобы избежать каких-либо непреднамеренных изменений, мы можем даже передать его по константной ссылке.
  3. Мы также можем возвращать несколько значений из функции.

Перейти по ссылке недостатки:

1- Функция, принимающая ссылку, требует, чтобы ввод был ненулевым.

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

3- Наконец, со ссылками, пожизненная гарантия - большая проблема. Это особенно опасно при работе с лямбда-выражениями и многопоточными программами.

Преимущества передачи по значению:

1- Аргументы в вызове могут принимать различные формы: константы, переменные или более сложные выражения.

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

Недостатки передачи по значению:

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

Пояснение к схеме:

explaination

Что выбрать:

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

- При передаче по значению: При передаче в качестве значения функция копирует 1 ГБ переданных ей данных. Всего ваша программа потребляет 2 гигабайта памяти.

- Передать по ссылке: При передаче в качестве ссылки функция вместо копирования данных напрямую обращается к существующим данным 1 ГБ. Изменение данных функцией можно защитить, применив «const» к ссылочному параметру в функции.

Dalija Prasnikar
9 мая 2022 в 18:38
0

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

avatar
20 января 2022 в 17:52
-1

"Я начинающий разработчик Java и хотел бы знать, использует ли Java вызов по значению или вызов по ссылке?"

Универсального ответа на этот вопрос нет и быть не может. У нас есть два разных термина и два разных способа подачи данных в метод (копирование и указатель), но как минимум 3(!) разных способа обработки указанных данных:

  1. Данные копируются, и копия передается методу. Изменения копии не распространяются наружу.
  2. Вместо этого методу передается указатель (адрес памяти) наших данных. Изменения в наших данных действительно распространяются наружу. Мы также можем указать на какой-то новый экземпляр, оставив наши исходные данные подвешенными.
  3. То же, что и №2, за исключением того, что мы можем изменить только исходные данные, но не то, на что указывает указатель.

В мире объектно-ориентированного программирования бесспорным является тот факт, что №1 — это вызов по значению, а №2 — вызов по ссылке. Однако, поскольку у нас есть только два термина для трех состояний, нет четкого разграничения в отношении #3.

.

"Что это значит для меня?"

Это означает, что в сфере Java вы можете разумно предположить, что № 3 предполагается в рамках Call-by-Value (или более словесного гимнастического термина Call-References-by-Value).

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

"Но я читал, что JLS определяет его как Call-by-Value!"

Поэтому ваше исходное предположение при работе с Java-разработчиками должно быть таким. Однако JLS имеет гораздо меньше полномочий за пределами Java.

"Почему разработчики Java настаивают на своей собственной терминологии?"

Не мне строить догадки, но я считаю справедливым отметить, что есть некоторые потенциальные проблемы с #2 (что явно является Call-by-Reference), как намекается, что приводит к тому, что Call-by-Reference не имеет лучшая репутация повсюду.

"Есть ли решение этой неразберихи?"

3 варианта, всего 2 вездесущих имени. Очевидным выходом является третий термин, который получает такое же широкое распространение, как «Вызов по значению» и «Вызов по ссылке» (и который менее запутан, чем «Вызов-ссылки по значению»). До тех пор вам нужно предположить или спросить, как описано выше.

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

Community
20 января 2022 в 19:54
0

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

avatar
4 января 2022 в 23:33
0

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

/**
 * 
 * @author Sam Ginrich
 * 
 * All Rights Reserved!
 * 
 */
public class JavaIsPassByValue
{

    static class SomeClass
    {
        int someValue;

        public SomeClass(int someValue)
        {
            this.someValue = someValue;
        }
    }

    static void passReferenceByValue(SomeClass someObject)
    {
        if (someObject == null)
        {
            throw new NullPointerException(
                    "This Object Reference was passed by Value,\r\n   that's why you don't get a value from it.");
    }
        someObject.someValue = 49;
    }

    public static void main(String[] args)
    {
        SomeClass someObject = new SomeClass(27);
        System.out.println("Here is the original value: " + someObject.someValue);

        passReferenceByValue(someObject);
        System.out.println(
                "\nAs ´Java is pass by value´,\r\n   everything without exception is passed by value\r\n   and so an object's attribute cannot change: "
                    + someObject.someValue);

        System.out.println();
        passReferenceByValue(null);
    }

}

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

Here is the original value: 27

As ´Java is pass by value´,
   everything without exception is passed by value
   and so an object´s attribute cannot change: 49

'Exception in thread "main" java.lang.NullPointerException: This Object Reference was passed by value,
   that's why you don't get a value from it. 
    at JavaIsPassByValue.passReferenceByValue(JavaIsPassByValue.java:26)
    at JavaIsPassByValue.main(JavaIsPassByValue.java:43)
avatar
Sam Ginrich
5 сентября 2021 в 13:09
3

Думаю, общий канон неверен из-за неточного языка

Авторы языка программирования не имеют права переименовывать установленную концепцию программирования.

Примитивные типы Java byte, char, short, int, long float, double обязательно передаются по значению.

Все остальные типы: Objects: элементы и параметры объекта технически являются ссылками .

Итак, эти «ссылки» передаются «по значению», но в стеке не происходит построение объекта. Любое изменение членов объекта (или элементов в случае массива) применяется к тому же исходному объекту; такая ссылка точно соответствует логике указателя экземпляра, переданного в некоторую функцию на любом C-диалекте, где мы вызывали это , передавая объект по ссылке

В частности, у нас есть эта штука java.lang.NullPointerException , что не имеет смысла в чистой концепции по значению

Michael
21 сентября 2021 в 04:50
1

Он по-прежнему передается по значению, даже если само значение является ссылкой. Аргумент копируется, и его переназначение не влияет ни на что за пределами вызываемой функции - это ключевое различие между передачей по значению / ссылке.

Sam Ginrich
25 сентября 2021 в 11:08
0

Мы согласны с тем, что референсы и объекты - это две пары обуви.

h0r53
19 октября 2021 в 16:09
0

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

avatar
12 августа 2021 в 15:29
0

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

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

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

avatar
Pallav Khare
9 августа 2021 в 10:49
5

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

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

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

avatar
Himanshu arora
12 июня 2021 в 07:13
85

Java - это передача по значению (стековая память)

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

  • Давайте сначала поймем, где java хранит примитивный тип данных и тип данных объекта.

  • Сами примитивные типы данных и ссылки на объекты хранятся в стеке. Сами объекты хранятся в куче.

  • Это означает, что в стековой памяти хранятся примитивные типы данных, а также адреса объектов.

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

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

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

SangLe
28 апреля 2021 в 03:45
0

Молодец, ты был ясно дал понять.

avatar
Scott Stanchfield
10 июня 2021 в 07:03
3344

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

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

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

Dog myDog;

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

Это означает, что у вас есть

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

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

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

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

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

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

давайте посмотрим, что происходит.

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

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

Изменилось ли myDog?

Вот и ключ.

Учитывая, что myDog - это указатель , а не фактический Dog, ответ - НЕТ. myDog по-прежнему имеет значение 42; он по-прежнему указывает на исходный Dog (но обратите внимание, что из-за строки «AAA» его имя теперь «Max» - все та же Dog; значение myDog не изменилось.)

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

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

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

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

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

ebresie
13 марта 2021 в 14:40
0

Незначительный уточняющий вопрос по приведенному выше примеру, так что при создании нового Dog в BBB по адресу 72, подразумевается ли это, что при возврате созданного Dog по адресу 72 его значение теряется и возвращается к 42?

Ravikumar
15 марта 2021 в 17:08
1

@ebresie javarevisited.blogspot.com/2015/09/….

Scott Stanchfield
16 марта 2021 в 18:42
0

@ebresie В основном да ("в основном" я поясню чуть позже). Единственный указатель на новую собаку на 74 (я полагаю, вы имели в виду 74, а не 72) - это параметр функции foo. Когда foo возвращается, все его параметры извлекаются из стека, поэтому ничего не остается, указывающее на 72, и его можно собрать сборщиком мусора. Я говорю «в основном», поскольку «возврата» не происходит; указатель myDog в вызывающей программе все время указывал на 42 и никогда не менялся, что бы ни происходило в функции, следовательно, не было «возврата».

NiharGht
8 июня 2021 в 06:30
0

«Однако вы не можете изменить место, куда указывает этот указатель». -> Разве это не должно быть «Однако вы не можете изменить положение этого указателя в каком-либо другом методе».

Scott Stanchfield
10 июня 2021 в 07:03
1

@NiharGht Хороший момент - я разъяснил его (прокомментируйте еще раз, если все еще не ясно)

Jonathan
24 июня 2021 в 17:20
0

Java не действует точно так же, как C. Если вы передаете указатель на функцию в C и изменяете место, на которое указывает этот указатель, эффект переназначения этого указателя виден в поле зрения вызова, а не только в рамках вызова. Поиск такого поведения в языках - это цель ключевого слова const. Пожалуйста, перестаньте говорить, что java похожа на C, потому что во многих основных отношениях она полностью НЕ c (или c ++), и все, что вы делаете, это сбивает с толку людей, которые действительно знают C (или C ++) и пытаются получить рабочий обзор java. . См .: course.washington.edu/css342/zander/css332/passby.html

Scott Stanchfield
26 июня 2021 в 20:14
0

@Jonathan Эта ссылка - C ++, а не C. C так не работает. C строго передается по значению, как и Java. Если вы передадите указатель на что-либо, указатель будет значением, которому вы можете следовать. Вы не можете изменить указатель, но можете следовать за ним и изменять значение, на которое он указывает. Если его перенаправить, вызывающий не видит изменения. В C ++ вы можете передать ссылку на что-то (на этой странице вы указываете как int &), что похоже на псевдоним; если вы измените его в функции / методе, он действительно изменит объект / примитив / указатель, переданный в качестве аргумента.

Jonathan
25 июля 2021 в 14:36
0

@ScottStanchfield Есть ли в C указатели? Ява нет! Передайте это вашему любимому компилятору c и проверьте результат. C имеет ссылочную семантику, а Java - нет. Вы не можете поменять местами значения внутри функции в java и отразить их на сайте вызова. pastebin.com/U9QBFrwL

Scott Stanchfield
26 июля 2021 в 16:38
0

@Jonathan Это похоже на это в java: pastebin.com/1tZsVVRw. Символ * создает указатель на аргумент (который сам может быть указателем), что аналогично созданию «корзины» для хранения значения - Java не позволяет создавать синтаксис C и указатели для управления существующими данными, но это не означает, что Java не имеет указателей (обратите внимание, что C по-прежнему не имеет ссылочной семантики ...). Паскаль, например, использует ^ аналогично C * - тот факт, что языки имеют разный синтаксис, не означает, что у них разные концепции (например, указатели).

Jonathan
29 июля 2021 в 23:49
0

@ScottStanchfield C действительно имеет ссылочную семантику, это достигается с помощью указателей, точно так же, как я вставил в Pastebin. Итак, что делает оператор адресации в C? ХМ? C имеет ссылочную семантику. У него всегда была ссылочная семантика. Я уверен, что вы здесь перепутали лингвистику и семантику. Я не говорю о грамматике, это может быть *, a ^ или тип &, значение (в основном) одно и то же, каваты - это правила каждой системы типов языков, что ОПЯТЬ, является еще одной причиной того, что JAVA НЕ ПОДОБНО C

KnockingHeads
30 июля 2021 в 16:21
2

Вот это да. Просто и элегантно. У меня возникла дилемма.

Scott Stanchfield
31 июля 2021 в 01:37
0

@Jonathan No. C имеет только передачу по значению (в вашем примере вы передаете значение указателя - см. coderhelper.com/questions/2229498/passing-by-reference-in-c для хорошего обсуждения, особенно ответ Эли). Передача по ссылке имеет очень конкретное значение на языке компилятора. К вашему сведению - я занимаюсь компилятором и входил в комитет ANSI C ++ 98 ... C ++ имеет эталонную семантику; C не делает. Разница в том, можно ли изменить фактический аргумент. Когда вы передаете & i, фактическое значение аргумента - это адрес i, а не ссылка на i.

Scott Stanchfield
31 июля 2021 в 01:50
0

@Jonathan (отправлено слишком рано и не может быть изменено) Внутри метода вы не меняете этот адрес; вы следуете по этому адресу и меняете хранящиеся там данные.

Jonathan
8 августа 2021 в 15:05
0

@ScottStanchfield Очевидно, вы не понимаете указатели и ссылки. Единственное различие между указателем и ссылкой состоит в том, что тип указателя можно отбросить. Ссылка - это указатель с безопасностью типов. Указатель - это просто переменная, которая содержит адрес чего-то еще. Угадайте, что это за ссылка? Переменная, содержащая адрес чего-то еще. Ссылка не является экземпляром чего-то еще. Ссылка - это в основном синтаксический сахар для указателей с безопасностью типов. Если в языке есть указатели, он имеет ссылочную семантику.

Scott Stanchfield
9 августа 2021 в 08:39
0

@Jonathan Вы путаете "ссылку" как тип с "передачей по ссылке". Существует огромная разница между «передачей значения по ссылке» (семантика ссылки) и «передачей указателя / ссылки по значению» (семантика значения). Все дело в том, как вы определяете ФОРМАЛЬНЫЕ параметры функции / метода, а не ФАКТИЧЕСКИЕ параметры. Если язык предоставляет вам синтаксис для автоматического отмены ссылки на значения в параметрах FORMAL (сигнатура функции / метода), он имеет ссылочную семантику. Создание указателя на что-то в ФАКТИЧЕСКИХ аргументах (сайт вызова) по-прежнему передает только значение.

Scott Stanchfield
9 августа 2021 в 08:44
0

@Jonathan C и Java строго передают по значению. Вы можете ИМУЛИРОВАТЬ в них передачу по ссылке, выполнив что-то как внутри функции / метода, так и на месте вызова (например, в C, используя & на сайте вызова и * внутри функции / метода, или в Java, обернув значение на сайте вызова и работает внутри оболочки внутри функции / метода), но это все еще передача по значению. Передача по ссылке - это особый термин компилятора для логического псевдонима переменной, переданной в функцию, ТОЛЬКО путем указания этого поведения в ФОРМАЛЬНЫХ параметрах, а не на месте вызова.

avatar
erlando
13 мая 2021 в 16:12
6392

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

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

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

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

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

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

Аналогично:

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

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

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

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

dbrewster
29 сентября 2020 в 18:03
6

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

TMOTTM
16 октября 2020 в 18:33
0

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

user36800
27 октября 2020 в 18:46
40

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

user21820
23 декабря 2020 в 15:03
5

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

user36800
28 декабря 2020 в 02:29
0

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

user21820
28 декабря 2020 в 05:49
5

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

user21820
28 декабря 2020 в 05:51
1

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

user36800
28 декабря 2020 в 23:32
0

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

Prateek Pande
13 января 2021 в 13:27
0

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

ghilesZ
2 февраля 2021 в 19:51
18

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

redd77
30 апреля 2021 в 00:48
0

Так в чем же тогда разница между «ссылкой на объект» и «значением ссылки на объект».

Sanjeev
8 июня 2021 в 22:38
0

@ redd77, когда вы передаете "значение", вы не передаете фактическую переменную, а только то значение, которое она содержит. Это означает, что вы делаете копию значений и передаете копию. Ссылка на объект (то есть указатель) - это переменная, содержащая физический адрес памяти, в которой находится объект. Значения ссылки на объект - это копия этого адреса. Языки, поддерживающие передачу по ссылке, позволяют передавать фактическую переменную, а не только копию, так что изменение ее в функции (например, установка значения null) также изменяет ее для вызывающего.

Matt F.
22 июня 2021 в 01:14
0

@ redd77 Единственное отличие состоит в том, что присвоение «значению ссылки на объект» не затрагивает объект. В этом смысле он похож на указатель C / C ++.

avatar
28 апреля 2021 в 06:17
5

Для простоты и многословности Его pass reference by value:

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
}
avatar
23 апреля 2021 в 15:32
-1

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

Этот код показывает, как можно сделать что-то вроде этого:

public class Test
{
    private static void needValue(SomeObject so) throws CloneNotSupportedException
    {
        SomeObject internalObject = so.clone();
        so=null;
        
        // now we can edit internalObject safely.
        internalObject.set(999);
    }
    public static void main(String[] args)
    {
        SomeObject o = new SomeObject(5);
        System.out.println(o);
        try
        {
            needValue(o);
        }
        catch(CloneNotSupportedException e)
        {
            System.out.println("Apparently we cannot clone this");
        }
        System.out.println(o);
    }
}

public class SomeObject implements Cloneable
{
    private int val;
    public SomeObject(int val)
    {
        this.val = val;
    }
    public void set(int val)
    {
        this.val = val;
    }
    public SomeObject clone()
    {
        return new SomeObject(val);
    }
    public String toString()
    {
        return Integer.toString(val);
    }
}

Здесь у нас есть функция needValue, которая сразу же создает клон объекта, который необходимо реализовать в классе самого объекта, и этот класс должен быть помечен как Cloneable. После этого не обязательно устанавливать so на null, но я сделал это здесь, чтобы показать, что мы не собираемся использовать эту ссылку после этого.

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

siegi
24 апреля 2021 в 04:59
3

Примечание: безопасно в «теперь мы можем безопасно редактировать internalObject» зависит от реализации метода clone() (т.е. «глубокий» или «неглубокий») и от того, насколько далеко вниз по дереву объектов ваши изменения имеют влияние. Однако для этого простого примера это правильно.

avatar
charles
16 марта 2021 в 14:09
3

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

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

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

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

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

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

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

avatar
Alex de Kruijff
16 марта 2021 в 06:12
1

Есть только две версии:

  • Вы можете передать значение, например (4,5)
  • Вы можете передать адрес, например, 0xF43A

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

В C ++ вы можете делать следующее:

Point p = Point(4,5);

Это резервирует 8 байтов в стеке и сохраняет в нем (4,5).

Point *x = &p;

Это резервирует 4 байта в стеке и сохраняет в нем 0xF43A.

Point &y = p;

Это резервирует 4 байта в стеке и сохраняет в нем 0xF43A.

  1. Я думаю, все согласятся, что вызов f (p) является передачей по значению, если определение f равно f (точка p). В этом случае зарезервированы дополнительные 8 байтов и (4,5) скопированы в них. Когда f меняет p, оригинал гарантированно не изменится, когда f вернется.

  2. Я думаю, что все согласятся, что вызов f (p) является передачей по ссылке, если определение f равно f (Point & p). В этом случае зарезервированы дополнительные 4 байта и в них копируется 0xF43A. Когда f меняет p, гарантируется, что оригинал будет изменен, когда f вернется.

  3. Вызов f (& p) также передается по ссылке, если определение f - f (Point * p). В этом случае зарезервированы дополнительные 4 байта и в них копируется 0xF43A. Когда f меняет * p, оригинал гарантированно будет изменен, когда f вернется.

  4. Вызов f (x) также передается по ссылке, если определение f - f (Point * p). В этом случае зарезервированы дополнительные 4 байта и в них копируется 0xF43A. Когда f меняет * p, оригинал гарантированно будет изменен, когда f вернется.

  5. Вызов f (y) также передается по ссылке, если определение f - f (Point & p). В этом случае зарезервированы дополнительные 4 байта и в них копируется 0xF43A. Когда f меняет p, гарантируется, что оригинал будет изменен, когда f вернется.

Конечно, то, что происходит после передачи, отличается, но это только языковая конструкция. В случае указателя вы должны использовать -> для доступа к членам и в случае ссылок, которые вы должны использовать .. Если вы хотите поменять местами значения оригинала, вы можете сделать tmp = a; а = б; b = tmp; в случае ссылок и tmp = * a; * b = tmp; * a = tmp для указателей. А в Java вам нужно: tmp.set (a); a.set (b); b.set (tmp). Сосредоточиться на операторе присваивания глупо. Вы можете сделать то же самое на Java, если напишете немного кода.

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

Для полноты:

Point p = new Point(4,5);

Это резервирует 4 байта в стеке и сохраняет в нем 0xF43A, а также резервирует 8 байтов в куче и сохраняет в нем (4,5).

Если вы хотите поменять местами ячейки памяти, например,

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

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

v010dya
21 апреля 2021 в 13:51
0

Один из наиболее полных ответов находится внизу. Ваш № 3 лучше всего описывает ситуацию на Java.

avatar
6 марта 2021 в 13:22
0

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

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

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

avatar
jack
26 декабря 2020 в 20:58
10

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

  • 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 нет передачи по ссылке », => БУМ, начинается неразбериха ...

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

David Schwartz
29 марта 2021 в 23:37
0

В обоих языках «передача по значению» означает передачу значения объекта, если это объект. Это ни в коем случае не означает передачу адреса значения. Нет никакой разницы в терминологии. Нет случая, когда мы вызываем что-то, передаваемое по значению, и изменение значения в вызываемой функции изменяет значение в вызывающей стороне. Если вы вызываете функцию, подобную этой foo(bar) в java, значение bar никогда не изменяется - если bar является ссылкой на объект, его значение является идентификатором объекта, на который он ссылается, и независимо от того, какая функция делает, он по-прежнему ссылается на тот же объект в вызывающей стороне.

Joachim Sauer
21 апреля 2021 в 15:08
0

Я согласен с Дэвидом Шварцем: на самом деле терминологической разницы нет. Большая разница между Java и C ++ заключается в том, что в Java «значение» не может никогда быть целым объектом. Это всегда либо ссылка, либо примитивное значение. Вы просто не можете передать весь объект в Java вообще.

avatar
Alexandr
22 декабря 2020 в 16:05
4

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

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

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

Abhishek Attri
22 декабря 2020 в 10:05
0

Четко и ясно!

avatar
Mr.Robot
7 декабря 2020 в 22:20
13

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

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

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

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

Edit-2020-12-08 : Обновлено Штат Валгалла

Sanjeev
20 февраля 2020 в 22:24
5

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

Mr.Robot
2 апреля 2020 в 16:28
3

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

Sanjeev
2 апреля 2020 в 18:17
0

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

Sanjeev
2 апреля 2020 в 18:49
0

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

Stephen C
5 апреля 2020 в 11:40
3

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

avatar
Eng.Fouad
7 декабря 2020 в 10:37
1982

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


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

public class Main {

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

     public static void changeReference(Foo a) {
          Foo b = new Foo("b");
          a = b;
     }

     public static void modifyReference(Foo c) {
          c.setAttribute("c");
     }

}

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

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

    Foo f = new Foo("f");
    

    enter image description here

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

    public static void changeReference(Foo a)
    

    enter image description here

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

    changeReference(f);
    

    enter image description here

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

    Foo b = new Foo("b");
    

    enter image description here

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

    enter image description here

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

    enter image description here

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

    enter image description here

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

dan carter
7 декабря 2020 в 08:48
31

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

Harsha
27 марта 2021 в 19:51
1

Я надеюсь, что книга Герберта Шильдта, по которой я изучал Java, научила этому

avatar
Duleepa Wicramasingha
18 октября 2020 в 06:28
8

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

Mohith7548
3 ноября 2020 в 10:26
0

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

Chrispher
14 ноября 2020 в 11:57
0

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

avatar
20 июня 2020 в 09:12
5

Кажется, в 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);
    }}

Выход

Алам

алам

талеев

Алам

талеев

Гарри

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

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

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

avatar
20 июня 2020 в 09:12
10

PT 1: Объявления о недвижимости

На улице Мейн-стрит, 1234, сейчас припаркован синий "Крошечный домик" площадью 120 кв. Футов с ухоженным газоном и клумбой перед фасадом.

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

Назовем этого риэлтора Бобом. Привет, Боб.

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

Когда кому-то нужна информация о синем Крошечном домике на Мейн-стрит, 1234, они спрашивают Боба.

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

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

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

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

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

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

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

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

Служба на своей стороне называет этот Листинг houseToLookAt, но на самом деле то, что он получает, является точной копией листинга Боба с точно такими же ЗНАЧЕНИЯми в нем, которые относятся к дому по адресу 1234 Main Street

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

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

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

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

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

Получив объект Listing, он позволяет клиентам фактически перекрашивать НАСТОЯЩИЙ дом на 1234 Main St, используя удаленный флот роботов-дронов! Это позволяет клиентам управлять роботом-бульдозером, чтобы ДЕЙСТВИТЕЛЬНО выкопать клумбу! Это безумие !!!

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

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

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

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

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

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

avatar
Ganesh
20 июня 2020 в 09:12
90

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

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

avatar
Michael
2 мая 2020 в 20:18
37

В отличие от некоторых других языков, 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 */
    }
}

enter image description here

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

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

avatar
Sanjeev
4 апреля 2020 в 19:05
88

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

Несколько точек:

  1. Термин «ссылка» имеет два разных значения. В Java это просто означает указатель, но в контексте «Передача по ссылке» это означает дескриптор исходной переменной, которая была передана.
  2. Java - это передача по значению . Java является потомком C (среди других языков). До C некоторые (но не все) более ранние языки, такие как FORTRAN и COBOL, поддерживали PBR, а C - нет. PBR позволял этим другим языкам вносить изменения в переданные переменные внутри подпрограмм. Чтобы выполнить то же самое (т.е. изменить значения переменных внутри функций), программисты на C передавали указатели на переменные в функции. Языки, вдохновленные C, такие как Java, позаимствовали эту идею и продолжают передавать указатели на методы, как это делал C, за исключением того, что Java вызывает свои указатели References. Опять же, это другое использование слова «Ссылка», чем в «Передача по ссылке».
  3. C ++ позволяет передавать по ссылке , объявляя параметр ссылки с помощью символа «&» (который является тем же символом, который используется для обозначения «адреса переменной» как в C, так и в C ++). Например, если мы передаем указатель по ссылке, параметр и аргумент не просто указывают на один и тот же объект. Скорее, это одна и та же переменная. Если один получает другой адрес или значение null, то же самое происходит и с другим.
  4. В приведенном ниже примере C ++ я передаю указатель на строку с завершающим нулем по ссылке . А в приведенном ниже примере Java я передаю ссылку Java на String (опять же, как указатель на String) по значению. Обратите внимание на результат в комментариях.

Пример передачи C ++ по ссылке:

using namespace std;
#include <iostream>

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

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

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

public class ValueDemo{

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

     public static void main(String []args){
        ValueDemo vd = new ValueDemo();
        String str = "not null";
        vd.change(str);
        System.out.println("str is " + str);    // ==> str is not null!!
                                                // Note that if "str" was
                                                // passed-by-reference, it
                                                // WOULD BE NULL after the
                                                // call to change().
     }
}

РЕДАКТИРОВАТЬ

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

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

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

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

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

РЕДАКТИРОВАТЬ 2

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

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

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

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

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

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

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

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

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

РЕДАКТИРОВАТЬ 3

Я могу быть немного одержим этим постом. Наверное, потому, что я чувствую, что создатели Java непреднамеренно распространяют дезинформацию. Если вместо слова "ссылка" для указателей они использовали что-то другое, скажем Динглберри, проблем бы не было. Вы можете сказать: «Java передает dingleberries по значению, а не по ссылке», и никого не смущает.

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

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

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

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

РЕДАКТИРОВАТЬ 4

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

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

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

Когда я изучал компиляторы - в 90-х, я использовал первое издание книги 1986 года, которое предшествовало Java примерно на 9 или 10 лет. Однако я только что наткнулся на копию 2-го издания от 2007 года , в котором фактически упоминается Java! Раздел 1.6.6, озаглавленный «Механизмы передачи параметров», довольно хорошо описывает передачу параметров. Вот отрывок под заголовком «Вызов по значению», в котором упоминается Java:

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

avatar
13 марта 2020 в 04:45
1

Прочтите это.

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

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

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

avatar
user1767316
22 января 2020 в 19:54
50

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

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

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

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

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

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

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

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

avatar
DeC
3 января 2020 в 06:47
8

A: 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 передает объект методу.

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

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

avatar
21 сентября 2019 в 08:09
3

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

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

Рассмотрим этот простой пример, 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

}
avatar
natwar kumar
6 сентября 2019 в 19:30
4
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;
    }
}

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

avatar
grindlewald
22 августа 2019 в 20:17
5

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

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

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

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

enter image description here

Рассмотрите возможность передачи примитивного типа данных, то есть 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 в кадре стека методов. Следовательно, исходное число вне кадра стека методов остается неизменным.

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

enter image description here

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

enter image description here

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

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

avatar
17 июля 2019 в 05:10
4

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

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

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

avatar
Basheer AL-MOMANI
14 июня 2019 в 19:58
12

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

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

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

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

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

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

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

avatar
30 апреля 2019 в 14:50
12

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

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


}
avatar
asok
25 апреля 2019 в 01:19
8

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

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

объекты по-прежнему передаются по значению, даже если операции с ними ведут себя как передача по ссылке. Учтите, что void changePerson(Person person){ person = new Person(); } ссылка вызывающего абонента на объект person останется неизменной. Сами объекты передаются по значению, но их члены могут быть затронуты изменениями. Чтобы быть истинной передачей по ссылке, мы должны иметь возможность переназначить аргумент новому объекту и отразить изменение в вызывающей стороне.

AutomatedMike
3 мая 2019 в 12:18
0

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

avatar
OverCoder
25 января 2019 в 21:45
10

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

Diagram of how data is created and passed

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

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

  • Вы можете изменить значение myObject внутри функции
  • Но вы не можете изменить то, на что ссылается myObject внутри функции, потому что point не является myObject
  • Помните, что и point, и myObject являются ссылками , разными ссылками , однако эти ссылки ​​указывают на <2173691309 одинаковую>
avatar
Felypp Oliveira
7 декабря 2018 в 19:02
23

Это лучший способ ответить на вопрос 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, - это то, как работает присвоение переменной . Я нашел его в документах:

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

int cadence = 0;
int скорость = 0;
int gear = 1;

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

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

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

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

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

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

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

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

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

    System.out.println("[2] Assign to height'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] объекты были такими же, затем мы назначили новый объект одному из них, и после этого объекты имели разные значения! Как они могли бы отличаться друг от друга, если бы были такими же? Они и здесь должны быть такими же! ?????

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

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

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

avatar
georgeawg
17 августа 2018 в 22:54
12

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

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

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

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

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

Dennis
25 августа 2018 в 21:08
0

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

Torben
3 октября 2018 в 04:40
12

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

nasch
27 декабря 2018 в 18:49
2

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

Sanjeev
23 января 2019 в 00:01
1

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

AndrewF
25 декабря 2019 в 00:00
0

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

Solubris
25 ноября 2020 в 23:25
1

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

Ingo
1 декабря 2020 в 10:44
0

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

avatar
Hank Gay
10 июля 2018 в 09:57
196

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

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.

avatar
JacquesB
6 февраля 2018 в 10:18
130

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

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

avatar
cutmancometh
4 января 2018 в 16:18
266

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

Если вы скажете: «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
12 сентября 2020 в 21:22
9

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

avatar
Gevorg
4 января 2018 в 10:17
769

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

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

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

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

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

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

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

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

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

  • Переменная person создается в строке №1, и она пуста в начале.
  • Новый объект Person создается в строке № 2, сохраняется в памяти, а переменной person дается ссылка на объект Person. То есть его адрес. Допустим, 3bad086a.
  • Переменная person , содержащая адрес объекта, передается функции в строке 3.
  • В строке №4 можно послушать звук тишины
  • Проверьте комментарий в строке 5
  • Создается локальная переменная метода - anotherReferenceToTheSamePersonObject , а затем появляется волшебство в строке # 6:
    • Переменная / ссылка person бит за битом копируется и передается в anotherReferenceToTheSamePersonObject внутри функции.
    • Никаких новых экземпляров Person не создается.
    • И " человек ", и " anotherReferenceToTheSamePersonObject " имеют одинаковое значение 3bad086a.
    • Не пытайтесь это сделать, но person == anotherReferenceToTheSamePersonObject будет истинным.
    • Обе переменные имеют ОДИНАКОВЫЕ КОПИИ ссылки, и обе они относятся к одному и тому же объекту Person, ОДНОМУ объекту в куче, а НЕ КОПИИ.

Картинка лучше тысячи слов:

Pass by Value

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

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

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

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

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

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


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


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

Excessstone
29 июня 2021 в 09:44
0

Я пробовал это: <br /> File file = new File ("C: /"); changeFile (файл); System.out.println (файл.getAbsolutePath ()); } public static void changeFile (File f) {f = new File ("D: /"); } `

avatar
Raj S. Rusia
25 декабря 2017 в 07:35
23

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

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

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

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

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

public class Balloon {

    private String color;

    public Balloon(){}

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

    public String getColor() {
        return color;
    }

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

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

public class Test {

    public static void main(String[] args) {

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

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

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

    }

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

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

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

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

Если вы посмотрите на первые две строки вывода, становится ясно, что метод подкачки не сработал. Это связано с тем, что 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 всегда передается по значению. Будет более понятно, когда вы узнаете о памяти кучи и стека, а также о том, где хранятся различные объекты и ссылки.

avatar
Premraj
16 декабря 2017 в 10:25
11
  • передается по ссылке: вызывающий и вызываемый абоненты используют одну и ту же переменную для параметра.

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

  • 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
    }
}
avatar
NAGHMAAN MOHASEEN
22 ноября 2017 в 12:38
17

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

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

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

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

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

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

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

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

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

avatar
Khaled.K
17 сентября 2017 в 16:38
9

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 сентября 2017 в 10:09
0

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

Khaled.K
17 сентября 2017 в 16:41
0

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

user6091735
18 ноября 2017 в 16:26
0

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

avatar
3 сентября 2017 в 18:52
10

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://coderhelper.com/a/12429953/4233180) могут помочь получить более глубокое понимание.

avatar
Sotirios Delimanolis
23 мая 2017 в 12:18
51

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Итак

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

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

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

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

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


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

avatar
sven
23 мая 2017 в 12:02
30

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

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

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

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();
}
avatar
Michał Żbikowski
10 марта 2017 в 10:26
20

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

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

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

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

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

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

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

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

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

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

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

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

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

Пример из [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
avatar
Christian
11 февраля 2017 в 18:52
25

Java передает параметры по ЗНАЧЕНИЮ и по значению ТОЛЬКО .

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

Для тех, кто поступает с C #: НЕТ параметра "out".

Для пользователей PASCAL: НЕТ параметра var .

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

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

avatar
Raja
27 января 2017 в 20:10
7

Я попытался упростить приведенные выше примеры, сохранив только суть проблемы. Позвольте мне представить это как историю, которую легко запомнить и правильно применить. История такая: У вас есть домашняя собака Джимми, длина хвоста которой составляет 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
avatar
Rahul Kumar
9 января 2017 в 16:35
17

Ява, конечно, без сомнения, проходит по значению. Кроме того, поскольку 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]
avatar
14 ноября 2016 в 16:51
15

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

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

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

avatar
29 сентября 2016 в 09:53
11

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

Loduwijk
2 августа 2017 в 15:25
0

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

avatar
Eclipse
27 сентября 2016 в 14:42
210

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

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

Solubris
25 ноября 2020 в 23:03
0

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

Eclipse
26 ноября 2020 в 22:03
1

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

avatar
Vivek Kumar
26 сентября 2016 в 18:00
6

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, ссылки на методы меняются местами, но не исходные ссылки. К сожалению, после вызова метода у вас остаются только исходные ссылки без замены. Для успешной подкачки вне вызова метода нам нужно поменять местами исходные ссылки, а не копии.

avatar
Ravi Sanwal
21 июня 2016 в 14:30
8

Есть очень простой способ понять это. Давайте пройдем 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 в main, которая была передана в bar, все еще не изменилась!

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

matt
15 июня 2016 в 08:14
0

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

Ravi Sanwal
16 июня 2016 в 15:16
0

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

matt
16 июня 2016 в 17:41
0

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

Loduwijk
2 августа 2017 в 15:41
1

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

avatar
JasonG
7 июня 2016 в 04:03
9

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

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

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

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

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

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

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

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

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

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

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

Использует ли Java семантику передачи по значению?

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

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

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

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

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

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

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

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

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

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

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

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

avatar
12 мая 2016 в 14:36
9

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

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

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

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

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

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

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

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

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

user207421
7 июня 2016 в 04:05
2

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

Cdaragorn
31 августа 2016 в 21:05
7

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

avatar
16 марта 2016 в 15:29
18

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

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

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

avatar
SCdF
22 января 2016 в 03:37
771

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

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

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

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

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

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

Brian Goetz
1 ноября 2020 в 15:52
0

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

georgiana_e
2 марта 2021 в 07:28
0

geeksforgeeks.org/g-fact-31-java-is-strictly-pass-by-value

avatar
21 января 2016 в 12:38
17

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 остаются прежними

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

avatar
karatedog
28 декабря 2015 в 07:36
74

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

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

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

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

Допустим, у нас есть переменная Foo, ее Location находится в 47-м байте памяти, а его Value равно 5. У нас есть другая переменная Ref2Foo <4861049> находится в 223-м байте памяти, и его значение будет 47. Ref2Foo может быть технической переменной, явно не созданной программой. Если вы просто посмотрите на 5 и 47 без какой-либо другой информации, вы увидите всего два значений . Если вы используете их как ссылки, то, чтобы добраться до 5, нам нужно проехать:

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

Вот как работают таблицы переходов.

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

  1. 5 копируется в один из регистров ЦП (например, EAX).
  2. 5 получает PUSHd в стек.
  3. 47 копируется в один из регистров ЦП
  4. 47 PUSHd в стек.
  5. 223 копируется в один из регистров ЦП.
  6. 223 получает PUSHd в стек.

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

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

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

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

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

Короче говоря, в собственной терминологии Java, Java - это передача по значению , где значение может быть: либо реальным значением , либо <23104971895 > значение , которое является представлением ссылки .

avatar
Srle
16 сентября 2015 в 15:18
93

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

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

enter image description here

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

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

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

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

Создание двух разных объектов Point с двумя связанными разными ссылками. enter image description here

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

Ожидаемый результат будет:

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

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

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

Ссылки pnt1 и pnt2 передаются по значению сложному методу, что означает, что теперь ваши ссылки pnt1 и pnt2 имеют имена <325159644> и pnt2 и arg2>. Таким образом, pnt1 и arg1 указывают на один и тот же объект. (То же самое для pnt2 и arg2) enter image description here

В методе tricky:

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

enter image description here

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

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

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

enter image description here

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

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

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

X1: 0         Y1: 0
X2: 0         Y2: 0
X1: 100       Y1: 100
X2: 0         Y2: 0
avatar
rorrohprog
22 августа 2015 в 05:50
58

Нет, не передается по ссылке.

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

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

avatar
spiderman
12 мая 2015 в 21:30
55

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

* /

public class PassByValueObjectCase2 {

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

    public void caller() {
        // student has the actual reference to a Student object created
        // can we change this actual reference outside the local scope? Let'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]
avatar
26 февраля 2015 в 18:44
5

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

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

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

Mox
18 января 2017 в 11:38
0

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

avatar
22 декабря 2014 в 18:48
8

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

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 января 2015 в 01:30
1

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

MrBackend
19 марта 2015 в 13:53
2

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

avatar
6 октября 2014 в 04:34
6

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

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

v010dya
21 апреля 2021 в 13:46
0

Чтобы понять это, нужно понимать, что в отличие от многих языков в Java, массив - это сам по себе Object.

avatar
bvdb
18 июля 2014 в 11:30
8

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

  • 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 #): передача массивов по ссылке и out

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

Kachna
27 февраля 2016 в 11:06
0

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

bvdb
27 февраля 2016 в 16:28
0

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

avatar
10 июля 2014 в 20:19
7

Разберитесь в двух шагах:

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

Если вы хотите изменить значение за ссылкой, вы только объявите новую переменную в стеке с тем же именем '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
}
avatar
Paul de Vrieze
4 мая 2014 в 09:33
31

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

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

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

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

Talijanac
18 августа 2020 в 09:14
0

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

Paul de Vrieze
15 сентября 2020 в 17:13
1

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

Talijanac
16 сентября 2020 в 08:40
0

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

Talijanac
16 сентября 2020 в 08:45
0

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

avatar
ron
30 апреля 2014 в 04:55
7

Мистер @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.

avatar
Harald Schilly
30 апреля 2014 в 04:52
9

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

avatar
shsteimer
30 апреля 2014 в 04:50
44

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

avatar
3 декабря 2013 в 08:26
4

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

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

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

avatar
bhushanpatil
14 ноября 2013 в 07:56
2

Язык программирования 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 ноября 2017 в 20:33
0

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

avatar
23 сентября 2013 в 02:03
8

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 | 
      +---+ 
avatar
James Drinkard
10 сентября 2013 в 16:53
5

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

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

avatar
Luigi R. Viggiano
7 июня 2013 в 08:51
8

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

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

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

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

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

avatar
31 декабря 2012 в 19:35
18

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

user207421
2 августа 2013 в 10:05
1

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

fatma.ekici
9 сентября 2013 в 13:06
0

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

user207421
25 сентября 2014 в 00:41
0

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

avatar
1 августа 2012 в 17:31
20

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

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

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

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


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

avatar
24 июня 2011 в 00:13
5

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

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

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

user207421
7 июня 2016 в 03:59
7

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

avatar
7 сентября 2010 в 22:07
161

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

avatar
9 августа 2010 в 12:21
13

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

avatar
26 декабря 2009 в 21:05
21

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

avatar
26 декабря 2009 в 20:19
28

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

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

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

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

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

avatar
kukudas
2 апреля 2009 в 17:58
61

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

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

avatar
8 марта 2009 в 06:28
52

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

avatar
SWD
12 января 2009 в 20:47
38

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

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

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

avatar
3 сентября 2008 в 20:01
43

Как многие люди упоминали ранее, 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;
  }
}

Печать:

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

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

avatar
2 сентября 2008 в 20:23
197

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

Diana
13 мая 2021 в 19:43
0

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

John Channing
26 июля 2021 в 21:56
1

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

avatar
2 сентября 2008 в 20:20
364

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

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