Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология
Образование Политология Производство Психология Стандартизация Технологии 


Тема 2.14 Более пристальный взгляд на передачу аргументов




 

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

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

Листинг 2.15

// Элементарные типы передаются по значению.

public class Test {

public void meth(int i, int j) {

i *= 2;

j /= 2;

}

}

 

class CallByValue {

public static void main(String args[]) {

Test ob = new Test();

int a = 15, b = 20;

System.out.println("аи b передвызовом: " + a + " " + b);

ob.meth(a, b);

System.out.println("аи b послевызова: " + a + " " + b);

}

}

Вывод этой программы имеет следующий вид:

а и b перед вызовом: 15 20
а и b после вызова: 15 20

Как видите, выполняемые внутри метода meth () операции не влияют на значения а и b, использованные в вызове. Их значения не изменились на 30 и 10.

При передаче объекта методу ситуация изменяется коренным образом, поскольку по существу объекты передаются посредством вызова по ссылке. Следует помнить, что при создании переменной типа класса создается лишь ссылка на объект. Таким образом, при передаче этой ссылки методу, принимающий ее параметр будет ссылаться на тот же объект, на который ссылается аргумент. По сути это означает, что объекты передаются методам посредством вызова по ссылке. Изменения объекта внутри метода влияют на объект, использованный в качестве аргумента(листинг 2.16).

Листинг 2.16

// Объекты передаются по ссылке.

public class Test {

public int a, b;

public Test(int i, int j) {

a = i;

b = j;

}

// передача объекта

public void meth(Test o) {

o.a *= 2;

o.b /= 2;

}

}

 

public class CallByRef {

public static void main(String args[]) {

Test ob = new Test(15, 20);

System.out.println("ob.аи ob.b передвызовом: " + ob.a + " " + ob.b);

ob.meth(ob);

System.out.println("ob.аи ob.b послевызова: " + ob.a + "" + ob.b);

}

}

Эта программа генерирует следующий вывод:

ob.a и ob.b перед вызовом: 15 20
ob.a и ob.b после вызова: 30 10

Как видите, в данном случае действия внутри метода meth () влияют на объект, использованный в качестве аргумента.

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

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

 

 Тема 2.15 Возврат объектов

 

Метод может возвращать любой тип данных, в том числе созданные типы классов. Например, в следующей программе(листинг 2.17) метод incrByTen () возвращает объект, в котором значение переменной а на 10 больше значения этой переменной в вызывающем объекте.

Листинг 2.17

// Возвращение объекта.

public class Test {

public int а;

public Test(int i) {

а = i;

}

public Test incrByTen() {

Test temp = new Test(а + 10);

return temp;

}

}

 

public class {

public static void main(String args[]) {

Test ob1 = new Test(2);

Test ob2;

ob2 = ob1.incrByTen();

System.out.println("ob1.a: " + ob1.а);

System.out.println("ob2.a: " + ob2.а);

ob2 = ob2.incrByTen();

System.out.println("ob2.апослевторогоувеличения: " + ob2.а);

}

}

Эта программа генерирует следующий вывод:

ob1.a: 2
ob2.а: 12
ob2.а после второго увеличения: 22

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

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



 

Тема 2.16 Рекурсия

 

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

Классический пример рекурсии – вычисление факториала числа. Факториал числа N – это произведение всех целых чисел от 1 до N. Например, факториал 3 равен 1x2x3, или 6. В листинге 2.18 показано как можно вычислить факториал, используя рекурсивный метод.

Листинг 2.18

public class Factorial {

// эторекурсивныйметод

public int fact(int n) {

int result;

if (n == 1) {

return 1;

}

result = fact(n - 1) * n;

return result;

}

}

 

public class Recursion {

public static void main(String args[]) {

Factorial f = new Factorial();

System.out.println("Факториал 3 равен " + f.fact(3));

System.out.println("Факториал 4 равен " + f.fact(4));

System.out.println("Факториал 5 равен " + f.fact(5));

}

}

Вывод этой программы имеет вид:

Факториал 3 равен 6
Факториал 4 равен 24
Факториал 5 равен 120

Для тех, кто не знаком с рекурсивными методами, работа метода fact () может быть не совсем понятна. Вот как работает этот метод. При вызове метода fact () с аргументом, равным 1, функция возвращает 1. В противном случае она возвращает произведение fact (n-1) *n. Для вычисления этого выражения программа вызывает метод fact () с аргументом 2. Это приведет к третьему вызову метода с аргументом, равным 1. Затем этот вызов возвратит значение 1, которое будет умножено на 2 (значение n во втором вызове метода). Этот результат (равный 2) возвращается исходному вызову метода fact () и умножается на 3 (исходное значение n). В результате мы получаем ответ, равный 6. В метод fact () можно было бы вставить операторы println (), которые будут отображать уровень каждого вызова и промежуточные результаты.

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

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

Основное преимущество применения рекурсивных методов состоит в том, что их можно использовать для создания более понятных и простых версий некоторых алгоритмов, чем при использовании итерационных аналогов. Например, алгоритм быстрой сортировки достаточно трудно реализовать итерационным методом. А некоторые типы алгоритмов, связанных с искусственным интеллектом, легче всего реализовать именно с помощью рекурсивных решений.

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

Рассмотрим еще один пример рекурсии(листинг 2.19). Рекурсивный метод printArray () выводит первые i элементов массива values.

Листинг 2.19

public class RecTest {

public int values[];

public RecTest(int i) {

values = new int[i];

}

// рекурсивное отображение элементов массива

public void printArray(int i) {

if (i == 0) {

return;

} else {

printArray(i - 1);

}

System.out.println(" [" + (i - 1) + "] " + values[i - 1]);

}

}

 

public class Recursion2 {

public static void main(String args[]) {

RecTest ob = new RecTest(10);

int i;

for (i = 0; i < 10; i++) {

ob.values[i] = i;

}

ob.printArray(10);

}

}

 

Эта программа генерирует следующий вывод:

[0] 0
[1] 1
[2] 2
[3] 3
[4] 4
[5] 5
[6] 6
[7] 7
[8] 8
[9] 9

 





Рекомендуемые страницы:


Читайте также:

  1. A.19. Противопожарная система
  2. A.32.4.5.3. Система УСАВП: тест управления рекуперативным торможением
  3. Hаиболее хаpактеpными для остpого панкpеатита являются боли
  4. II. Поселение в Испании. Взаимоотношения вестготов и римлян. Королевская власть. Система управления. Церковная политика.
  5. II. ТЕМАТИЧЕСКИЙ РАСЧЕТ ЧАСОВ
  6. V. Письменно переведите текст и выпишите слова юридической тематики.
  7. VII. ЛЕКСИКА ДРУГИХ АКТУАЛЬНЫХ ТЕМАТИЧЕСКИХ ГРУПП
  8. Абзацы нужно делать более мелкими и отделять друг от друга пустой строкой
  9. АВАРИИ НА КОММУНАЛЬНЫХ СИСТЕМАХ ЖИЗНЕОБЕСПЕЧЕНИЯ
  10. Автоматизированная система мониторинга вычислительной среды и обнаружения сетевых атак.
  11. АВТОМАТИЧЕСКАЯ СИСТЕМА ОПОВЕЩЕНИЯ И ТУШЕНИЯ ПОЖАРА АСОТП ИГЛА-М.5К-Т И СКТБ
  12. Автор специального исследования по этому вопросу Середонин пришел к выводу, что в конце XVI в. было не более 23–25 тыс. детей боярских и дворян, числившихся в разрядных списках.




Последнее изменение этой страницы: 2016-05-30; Просмотров: 345; Нарушение авторского права страницы


lektsia.com 2007 - 2021 год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! (0.012 с.) Главная | Обратная связь