Предавателна стойност като механизъм за преминаване на параметри в Java

1. Въведение

Двата най-разпространени режима на предаване на аргументи на методите са „предаване на стойност“ и „предаване на препратка“. Различните езици за програмиране използват тези понятия по различни начини. Що се отнася до Java, всичко е строго Pass-by-Value .

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

2. Предаване на стойност срещу преминаване на препратка

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

  • стойност
  • справка
  • резултат
  • стойност-резултат
  • име

Двата най-често срещани механизма в съвременните програмни езици са „Pass-by-Value” и „Pass-by-Reference”. Преди да продължим, нека първо да обсъдим тези:

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

Нека се опитаме да разберем твърденията в горната програма, като анализираме как тези стойности се съхраняват в паметта:

  1. Променливите " x" и " y" в основния метод са примитивни типове и техните стойности се съхраняват директно в паметта на стека
  2. Когато извикаме метод modify () , се създава точно копие за всяка от тези променливи и се съхранява на различно място в паметта на стека
  3. Всяка модификация на тези копия засяга само тях и оставя оригиналните променливи непроменени

3.2. Предаване на препратки към обекти

В Java всички обекти се съхраняват динамично в Heap пространство под капака. Тези обекти се препращат от препратки, наречени референтни променливи.

Обектът на 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 винаги е Pass-by-Value. Контекстът обаче се променя в зависимост от това дали имаме работа с примитиви или обекти:

  1. За примитивните типове параметрите са предавани по стойност
  2. За типовете обекти препратката към обект е стойност за предаване

Кодовите фрагменти, използвани в тази статия, могат да бъдат намерени в GitHub.