Главная » Java » Параметры метода Java

0

При вызове метода аргументы передаются "по значению". Другими словами, значения переменных-параметров в теле метода – это копии тех объектов, которые код-инициатор передал методу в виде аргументов. Если, например, метод получает в качестве аргумента значение типа doublе, в теле метода соответствующий параметр сохраняет копию значения, и возможные изменения этой копии в процессе работы метода никоим образом не воздействуют на содержимое переменных в коде-инициаторе. Рассмотрим следующий пример:

 

class PassByValue { // передача аргумента по значению

            public static void main(String[] args) {

                        double one = 1.0;

                        Sуstеm.оut.println("до: оnе = " + оnе);

                        halvert(one) ;

                        Sуstеm.оut.println("после: оnе = " + оnе);

            }

public static void halveIt(double arg) {

arg /= 2.0; // Разделить значение arg пополам

sуstеm.оut.println("половина: arg = " + arg);

}

}

ниже показан результат работы программы – операция деления значения Параметра arg в методе halveIt не влияет на содержимое переменной оnе в теле метода main.

до: one = 1.0

половина: arg = 0.5

после: оnе = 1.0

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

class PassRef { // Передача аргумента-ссылки

public static void main(String[] args) {

 Body venus = new Body("Beнepa", null);

Sуstеm.оut.ргintln("до: " + venus);

 commonName(venus);

 

Sуstеm.оut.ргintln("после: " + venus);

}

public static void CommonName(Body bodyRef) {

bodyRef.name = "утренняя звезда";

bodyRef = null;

}

}

Результат работы программы выглядит так:

До: 0 (венера)

После: 0 (Утренняя звезда)

Обратите внимание, что состояние "внешнего" по отношению к методу CommonName объекта поддалось изменению "изнутри" этого метода; кроме того, переменная venus все еще сохранила ссылку на тот же объект класса Body, а в методе CommonName копия bodyRef ссылочной переменной venus получила другое значение – null. Все это, разумеется, требует более подробного разъяснения.

Следующая диаграмма иллюстрирует состояние переменных непосредственно после  вызова из main метода СommonName.

В этот момент две переменные, venus в main и bodyRef в commonName, указывают на один и тот же объект. Когда метод commonName вносит изменение в поле bodyRef.name, модифицируется содержимое того же объекта, общего для двух ссылочных переменных. Когда же commonName присваивает переменной bodyRef значение null, изменяется только содержимое самой переменной bodyRef; состояние ссылки venus остается прежним, поскольку параметр bodyRef – это копия переменной venus, переданная в качестве аргумента по значению. Единственный элемент данных, который в последнем случае подвергается воздействию, – это параметр bodyRef как таковой (то же наблюдалось и в предыдущем примере, когда единственной "пострадавшей" стороной в процессе работы метода halveIt оказывался параметр arg). Если бы изменение bodyRef влияло на значение venus в main, строка после:, выводимая на экран, содержала бы слово null. Что касается первой операции присваивания в теле метода commonName (bodyRef. name = … ), следует повторить еще раз: на один и тот же объект одновременно указывают обе переменные, venus в main и bodyRef в commonName, поэтому изменения, вносимые в состояние объекта посредством ссылки bodyRef, будут видимы и при обращении к объекту с помощью других существующих ссылок (в частности, venus).

Бытует насколько распространенное, настолько же и неверное суждение о том, что объекты Java якобы передаются "по ссылке". В теории языков программирования достаточно употребительный термин передача по ссылке, строго говоря, означает следующее: когда аргумент передается в функцию, вызванная функция получает ссылку на исходное значение, но не копию этого значения. Если в теле функции значение параметра изменяется, соответствующие данные в коде, инициировавшем вызов, также подвергаются изменению, поскольку аргумент и параметр указывают на один и тот же блок памяти. Если бы язык Java в действительности позволял использовать аргументы, передаваемые по ссылке, существовали бы способы объявить метод halveIt так, чтобы его внутренний код мог изменить значение внешней переменной оnе, и разрешить методу СommonName изменить значение переменной venus на null. Но сделать это невозможно. В Java объекты не передаются по ссылке – ссылки па объекты передаются по значению. Поскольку две копии одной и той же ссылочной переменной указывают на один объект, любые изменения в состоянии объекта, произведенные с помощью одной ссылки, будут "видимы" посредством другой. Существует единственный способ передачи аргументов (по значению), и это позволяет значительно упростить жизнь.

Существует еще одно средство языка, имеющее прямое отношение к обсуждаемой теме, – в объявлении метода параметры позволено помечать модификатором final, имея в виду, что значение параметра не может быть изменено в ходе выполнения тела метода. Будь параметр bodyRef объявлен как final, компилятор не позволил бы изменить его значение на null. Если логика программы обусловливает недопустимость изменения значения параметра, вам достаточно снабдить его признаком final, чтобы компилятор со своей Стороны обеспечил поддержку ваших начинаний. Кроме того, такое объявление способно помочь компилятору или виртуальной машине оптимизировать некоторые выражения, в которых используется параметр, поскольку final дает понять, что значение обязано оставаться неизменным. Применение модификатора final по отношению к параметрам – это одна из тонкостей реализации метода как такового, не воздействующая на код, откуда производится вызов метода, поэтому вы вправе, ничем не рискуя, принять подобное проектное решение в любой момент.

 

Источник: Арнолд, Кен, Гослинг, Джеймс, Холмс, Дэвид. Язык программирования Java. 3-е изд .. : Пер. с англ. – М. : Издательский дом «Вильяме», 2001. – 624 с. : ил. – Парал. тит. англ.

По теме:

  • Комментарии