Является ли Java «передачей по ссылке» или «передачей по значению»? - PullRequest
5963 голосов
/ 03 сентября 2008

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

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

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

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

Ответы [ 79 ]

7 голосов
/ 16 декабря 2017
  • передано по ссылке: вызывающая сторона и вызываемая сторона используют одну и ту же переменную для параметра.

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

  • 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
    }
}
6 голосов
/ 20 апреля 2012

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

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

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

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

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

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

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

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

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

6 голосов
/ 24 июня 2011

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

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

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

6 голосов
/ 30 марта 2017

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

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

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

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

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

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

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

6 голосов
/ 25 апреля 2016

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

Diagram of how data is created and passed

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

Это значит:

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

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

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

6 голосов
/ 17 августа 2013

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

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

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

5 голосов
/ 02 июня 2013

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());
    }
}
5 голосов
/ 23 сентября 2013

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 | 
      +---+ 
5 голосов
/ 27 января 2017

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

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

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