1. Введение
Двумя наиболее распространенными способами передачи аргументов методам являются «передача по значению» и «передача по ссылке». Различные языки программирования используют эти концепции по-разному. Что касается Java, то здесь все строго по принципу Pass-by-Value
.
В этом руководстве мы собираемся проиллюстрировать, как 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;
}
}
Давайте попробуем понять утверждения в приведенной выше программе, проанализировав, как эти значения хранятся в памяти:
- Переменные «
x»
и «y»
в основном методе являются примитивными типами, и их значения хранятся непосредственно в памяти стека. - Когда мы вызываем метод
modify()
, создается точная копия каждой из этих переменных и сохраняется в другом месте в памяти стека. - Любая модификация этих копий влияет только на них и оставляет исходные переменные без изменений.
3.2. Передача ссылок на объекты
В Java все объекты динамически хранятся в пространстве кучи под капотом. На эти объекты ссылаются по ссылкам, называемым ссылочными переменными.
Объект Java, в отличие от примитивов, хранится в два этапа. Ссылочные переменные хранятся в памяти стека, а объект, на который они ссылаются, хранится в памяти кучи.
Всякий раз, когда объект передается в качестве аргумента, создается точная копия ссылочной переменной, которая указывает на то же местоположение объекта в куче памяти, что и исходная ссылочная переменная.
В результате всякий раз, когда мы вносим какие-либо изменения в один и тот же объект в методе, это изменение отражается в исходном объекте. Однако, если мы создадим новый объект для переданной ссылочной переменной, он не будет отражен в исходном объекте.
Попробуем разобраться в этом на примере кода:
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
в метод modify()
, которые имеют одно и то же значение 1
. Первоначально эти ссылки на объекты указывают на два разных местоположения объектов в пространстве кучи:
Когда эти ссылки a
и b
передаются в метод modify()
, он создает зеркальные копии тех ссылок a1
и b1
, которые указывают на одни и те же старые объекты:
В методеmodify()
, когда мы изменяем ссылку a1
, он изменяет исходный объект. Однако для ссылки b1
мы назначили новый объект. Итак, теперь он указывает на новый объект в куче памяти.
Любое изменение, внесенное в b1
, ничего не отразит в исходном объекте:
4. Вывод
В этой статье мы рассмотрели, как обрабатывается передача параметров в случае примитивов и не-примитивов.
Мы узнали, что передача параметров в Java всегда осуществляется по значению. Однако контекст меняется в зависимости от того, имеем ли мы дело с примитивами или объектами:
- Для примитивных типов параметры передаются по значению.
- Для типов объектов ссылка на объект передается по значению.
Фрагменты кода, использованные в этой статье, можно найти на GitHub .