1. Введение

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

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

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

Начнем с некоторых различных механизмов передачи параметров в функции:

ссылка на значение результат имя результата на значение

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

2.1. Передача по значению

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

Это означает, что при вызове метода параметры, передаваемые вызываемому методу, будут клонами исходных параметров. Любая модификация, выполненная в вызываемом методе, не повлияет на исходные параметры вызывающего метода.

2.2. Передача по ссылке

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

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

3. Передача параметров в Java

Фундаментальными понятиями любого языка программирования являются «значения» и «ссылки». В Java примитивные переменные хранят фактические значения, тогда как непримитивные хранят ссылочные переменные, которые указывают на адреса объектов, на которые они ссылаются. И значения, и ссылки хранятся в памяти стека.

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

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

Давайте теперь посмотрим на это в действии с помощью нескольких примеров кода.

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

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

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

Давайте попробуем понять это на примере кода:

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

public class PrimitivesUnitTest {
 
    @Test
    public void whenModifyingPrimitives_thenOriginalValuesNotModified() {
        
        int x = 1;
        int y = 2;
       
        // Before Modification
        assertEquals(x, 1);
        assertEquals(y, 2);
        
        modify(x, y);
        
        // After Modification
        assertEquals(x, 1);
        assertEquals(y, 2);
    }
    
    public static void modify(int x1, int y1) {
        x1 = 5;
        y1 = 10;
    }
}

3.2. Передача ссылок на объекты

  1. The variables “x” and “y” in the main method are primitive types and their values are directly stored in the stack memory
  2. When we call method modify(), an exact copy for each of these variables is created and stored at a different location in the stack memory
  3. Any modification to these copies affects only them and leaves the original variables unaltered

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

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

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

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

Давайте попробуем понять это на примере кода:

Давайте проанализируем утверждения в приведенной выше программе. Мы передали объекты a и b в методеmodify(), которые имеют одно и то же значение 1. Первоначально эти ссылки на объекты указывают на два разных местоположения объектов в пространстве кучи:

public class NonPrimitivesUnitTest {
 
    @Test
    public void whenModifyingObjects_thenOriginalObjectChanged() {
        Foo a = new Foo(1);
        Foo b = new Foo(1);

        // Before Modification
        assertEquals(a.num, 1);
        assertEquals(b.num, 1);
        
        modify(a, b);
        
        // After Modification
        assertEquals(a.num, 2);
        assertEquals(b.num, 1);
    }
 
    public static void modify(Foo a1, Foo b1) {
        a1.num++;
       
        b1 = new Foo(1);
        b1.num++;
    }
}
 
class Foo {
    public int num;
   
    public Foo(int num) {
        this.num = num;
    }
}

Когда эти ссылки a и b передаются в методе модификации () он создает зеркальные копии тех ссылок a1 и b1, которые указывают на одни и те же старые объекты:

В методе Modify(), когда мы модифицируем ссылку a1, он изменяет исходный объект. Однако для ссылки b1 мы назначили новый объект. Итак, теперь он указывает на новый объект в куче памяти.

Любое изменение, внесенное в b1, ничего не отразит в исходном объекте:

4. Заключение

В этой статье мы рассмотрели, как обрабатывается передача параметров в случае примитивов и не-примитивов.

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

Фрагменты кода, использованные в этой статье, можно найти на GitHub.

  1. For Primitive types, parameters are pass-by-value
  2. For Object types, the object reference is pass-by-value

«