methods in - Является ли Java «pass-by-reference» или «pass-by-value»?




out object (25)

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

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

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

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

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

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

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

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

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


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

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


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


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


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

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

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

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

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

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

Вот как работают прыжки.

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

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

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

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

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

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

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

Короче говоря, и в собственной терминологии Java, Java является pass-by-value, где значение может быть: либо реальное значение, либо значение , являющееся представлением ссылки .


Как уже упоминалось ранее, Java всегда имеет значение pass-by-value

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

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

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

Печать:

До: a = 2, b = 3
После: a = 2, b = 3

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


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

private void foo(Object bar) {
    bar = null;
}

public static void main(String[] args) {
    String baz = "Hah!";
    foo(baz);
    System.out.println(baz);
}

распечатает "Hah!" вместо null .Причина этого в том, что barэто копия значения baz, которое является просто ссылкой на"Hah!" ,Если бы это было само собой фактическое задание, тогда fooбы переопределена bazв null.


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

public void test() {
    MyClass obj = null;
    init(obj);
    //After calling init method, obj still points to null
    //this is because obj is passed as value and not as reference.
}
private void init(MyClass objVar) {
    objVar = new MyClass();
}

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

Сократить длинный рассказ короткий:

Для тех, кто приходит из C #: нет параметра «out».

Для тех, кто прибывает из PASCAL: нет параметра «var» .

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

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


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

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

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

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


Насколько мне известно, Java знает только вызов по значению. Это означает, что для примитивных типов данных вы будете работать с копией и для объектов, которые будут работать с копией ссылки на объекты. Однако я думаю, что есть некоторые подводные камни; например, это не сработает:

public static void swap(StringBuffer s1, StringBuffer s2) {
    StringBuffer temp = s1;
    s1 = s2;
    s2 = temp;
}


public static void main(String[] args) {
    StringBuffer s1 = new StringBuffer("Hello");
    StringBuffer s2 = new StringBuffer("World");
    swap(s1, s2);
    System.out.println(s1);
    System.out.println(s2);
}

Это заполнит Hello World, а не World Hello, потому что в функции swap вы используете копии, которые не влияют на ссылки в основном. Но если ваши объекты не являются неизменными, вы можете изменить его, например:

public static void appendWorld(StringBuffer s1) {
    s1.append(" World");
}

public static void main(String[] args) {
    StringBuffer s = new StringBuffer("Hello");
    appendWorld(s);
    System.out.println(s);
}

Это запустит Hello World в командной строке. Если вы измените StringBuffer на String, он произведет только Hello, потому что String неизменен. Например:

public static void appendWorld(String s){
    s = s+" World";
}

public static void main(String[] args) {
    String s = new String("Hello");
    appendWorld(s);
    System.out.println(s);
}

Однако вы можете сделать обертку для String, подобную этой, которая позволила бы ей использовать ее со строками:

class StringWrapper {
    public String value;

    public StringWrapper(String value) {
        this.value = value;
    }
}

public static void appendWorld(StringWrapper s){
    s.value = s.value +" World";
}

public static void main(String[] args) {
    StringWrapper s = new StringWrapper("Hello");
    appendWorld(s);
    System.out.println(s.value);
}

edit: Я считаю, что это также причина использования StringBuffer, когда дело доходит до «добавления» двух строк, потому что вы можете модифицировать исходный объект, который не может быть с неизменяемыми объектами, такими как String.


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

/ ** * * Pass By Value * * /

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

    public void caller() {
        String value = new String("Nikhil");
        boolean valueflag = false;
        String output = method(value, valueflag);
        /*
         * 'output' is insignificant in this example. we are more interested in
         * 'value' and 'valueflag'
         */
        System.out.println("output : " + output);
        System.out.println("value : " + value);
        System.out.println("valueflag : " + valueflag);

    }

    public String method(String value, boolean valueflag) {
        value = "Anand";
        valueflag = true;
        return "output";
    }
}

Результат

output : output
value : Nikhil
valueflag : false

Пример 3:

/ ** У этого «Pass By Value» есть ощущение «Pass By Reference»

Некоторые люди говорят, что примитивные типы и «String» являются «pass by value», а объекты «проходят по ссылке».

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

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

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

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

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

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

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

Я объясню это шагами:

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

    Foo f = new Foo("f");
    

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

    public static void changeReference(Foo a)
    

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

    changeReference(f);
    

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

    Foo b = new Foo("b");
    

  5. a = b переназначает ссылку NOT f объекту, чей атрибут "b" .

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

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

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


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

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

public static void main(String[] args) {
    Dog aDog = new Dog("Max");
    // we pass the object to foo
    foo(aDog);
    // aDog variable is still pointing to the "Max" dog when foo(...) returns
    aDog.getName().equals("Max"); // true
    aDog.getName().equals("Fifi"); // false 
}

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

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

Точно так же:

public static void main(String[] args) {
    Dog aDog = new Dog("Max");
    foo(aDog);
    // when foo(...) returns, the name of the dog has been changed to "Fifi"
    aDog.getName().equals("Fifi"); // true
}

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

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


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


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

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

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

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

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

public class PassByValue {
    public static void main(String[] args) {
        Test t = new Test();
        t.name = "initialvalue";
        new PassByValue().changeValue(t);
        System.out.println(t.name);
    }

    public void changeValue(Test f) {
        f.name = "changevalue";
    }
}

class Test {
    String name;
}

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

changevalue

Давайте поэтапно поймем:

Test t = new Test();

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

new PassByValue().changeValue(t);

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

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

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


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

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


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

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

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

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

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

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

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

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


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

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

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

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

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

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


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

void getValues(int& arg1, int& arg2) {
    arg1 = 1;
    arg2 = 2;
}
void caller() {
    int x;
    int y;
    getValues(x, y);
    cout << "Result: " << x << " " << y << endl;
}

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

void getValues(int[] arg1, int[] arg2) {
    arg1[0] = 1;
    arg2[0] = 2;
}
void caller() {
    int[] x = new int[1];
    int[] y = new int[1];
    getValues(x, y);
    System.out.println("Result: " + x[0] + " " + y[0]);
}

Как было объяснено в предыдущих ответах, в Java вы передаете указатель на массив как значение getValues. Этого достаточно, потому что метод затем модифицирует элемент массива, и по соглашению вы ожидаете, что элемент 0 будет содержать возвращаемое значение. Очевидно, вы можете сделать это другими способами, такими как структурирование вашего кода, чтобы это не было необходимо, или создание класса, который может содержать возвращаемое значение или разрешить его установку. Но простой шаблон, доступный вам в C ++ выше, недоступен на Java.


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

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

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

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

Поток программы:

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

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

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

Как ожидается, выход будет:

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

В этой строке «pass-by-value» переходит в игру ...

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

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

В trickyметоде:

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

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

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

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

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

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

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

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

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

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

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


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

Java Spec говорит, что все в Java является передачей по значению. В Java нет такой вещи, как «pass-by-reference».

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

Dog myDog;

не является Собакой; это на самом деле указатель на Собака.

Что это значит, когда вы

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

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

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

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

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

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

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

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

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

myDog ли myDog ?

Есть ключ.

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

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

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

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

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

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


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

Но, я думаю, вы уже прочитали его на SO; here вас есть требуемая документация:

here







java methods parameter-passing pass-by-reference pass-by-value