Методы, их перегрузка и рекурсия
До сих пор мы использовали только существующие методы классов. Например, для вывода информации в консоль мы часто применяли методы:
System.out.println() и System.out.print()
объекта out. Вместе с тем существующих методов языка Java конечно же недостаточно и возникает необходимость создания своих собственных. В связи с этим нужно понимать, в каких случаях целесообразно их создавать. Часто это делается, чтобы избавить себя много раз писать один и тот же текст в программе. Например, в программах при работе со строками часто требуется сравнивать одну строку с другой, значит, такую операцию лучше определить в виде метода и использовать его по мере необходимости. Вот почему такой метод уже существует и называется equal(). Но на все случаи жизни методы не придумаешь, да это и не нужно, поэтому в программе можно объявить свой, используя следующий синтаксис:
Модификаторы – это такие определения как
именно такие модификаторы записаны перед объявлением метода main, существующего в каждой программе:
public static void main(String[] args)
Далее, тип метода – это тот тип данных, который будет он возвращать. Затем, имя метода служит для его вызова в программе (оно придумывается программистом подобно именам переменных). Список аргументов необходим для передачи методу каких-либо данных при его вызове. Тело метода – это набор операторов, которые выполняются при его вызове.
Рассмотрим пример создания метода, который вычисляет периметр прямоугольника. Запишем его внутри класса Main, чтобы мы могли его вызывать в методе main. Наш метод будет выглядеть так:
float perimetr(float a, float b) { float res = 2 * (a + b); return res; }
Здесь в нем указаны два вещественных аргумента – это ширина и длина прямоугольника, возвращаемый тип также имеет вещественный тип float. Причем, перед каждым аргументом обязательно прописывать его тип. То есть, писать вот так:
нельзя. Далее, внутри самого метода создается временная переменная res, которой присваивается результат вычисления периметра. А оператор return показывает, что будет возвращать метод.
Метод main нашей программы запишем так:
public static void main(String[] args) { int w = 5; float h = 3.4f; float P1 = perimetr(w, h); float P2 = perimetr(5, 4); System.out.println(P1); System.out.println(P2); }
Смотрите, мы здесь сначала определили две переменные и, затем, вызываем наш метод perimeter сначала с передачей ему переменных в качестве аргументов, а затем, передаем обычные числа. Однако интегрированная среда нам здесь говорит о некой ошибке. Дело в том, что метод main объявлен как статический (модификатор static). Что это такое мы поговорим позже, а сейчас заметим, что из статических методов можно вызывать только другие статические методы. Поэтому перед определением нашего метода пропишем модификатор static.
Теперь ошибок никаких нет и мы можем посмотреть на результат работы нашей программы. Как это все работает? Во-первых, метод вызывается по своему имени, за которым идут круглые скобки и в них при необходимости указываются аргументы. Если аргументы не нужны, то пишем просто круглые скобки. Далее, переданные аргументы копируются в переменные a и b, метод вычисляет периметр и возвращаемое значение присваивается переменным P1 и P2.
А теперь давайте добавим в метод, например, такую строчку после оператора return:
Смотрите, интегрированная среда сообщает о какой-то ошибке. Дело в том, что как только в методе встречается оператор return, его работа заканчивается и все что идет дальше не может быть выполнено. Именно об этом здесь и говорит интегрированная среда. А вот если перенести эту строчку до оператора return, то все будет нормально.
В дополнение к рассмотренным ранее примитивным типам переменных у методов часто используется еще один тип данных – void, означающий, что метод ничего не возвращает. Например, мы хотим написать метод, который бы выводил значения массива на экран. Это можно сделать так:
и, затем, вызвать его в методе main:
byte array[] = {1, 3, 0, -2, 7, 9}; show_ar(array);
Так как здесь использован тип void, то оператор return писать не обязательно.
Теперь смотрите, в методах Java можно указывать произвольное число аргументов определенного типа. Это записывается так:
то есть, указывается тип аргументов, затем, через пробел ставятся три точки и сразу записывается имя массива, в котором будут храниться все переданные аргументы. И вызов такой функции теперь будет выглядеть так:
В результате, выведем те же значения в консоль. Вот так можно передавать произвольное число аргументов.
Перегрузка методов
В языке Java существует такая вещь как перегрузка методов. Перегрузка позволяет создавать несколько методов с одинаковыми именами, но разными типами (или числом) аргументов. Например, мы хотим записать метод вычисления модуля числа как для целочисленных значений, так и для вещественных. Это лучше всего сделать через перегрузку таким образом.
static int modul(int x) { if (x 0) x = -x; return x; } static double modul(double x) { return (x 0) ? -x : x; }
Смотрите, здесь записаны два метода modul с разными типами аргумента x. Затем, в main идет их вызов. Как вы думаете, как компилятор определяет вызов каких из двух методов подставить в эти строчки? Это делается по типу входных аргументов. То есть, если вот здесь записано -3, значит, будет вызван метод modul с целочисленным аргументом. Если же стоит вещественное число -3.5, то вызовется метод с вещественным типом аргумента. При этом вот эти типы не играют никакой роли в выборе методов. Имеют значение только входные типы данных.
Вы также можете задаться вопросом: зачем вообще делать перегрузку, давайте зададим один метод с вещественным типом и он будет работать и с целочисленными значениями? Все верно, но работать это будет дольше, так как обработка целочисленных данных как вещественных требует большего машинного времени. А один из критериев создания программ звучит: программа должна работать как можно быстрее. Поэтому здесь лучше использовать перегрузку.
Рекурсивные методы
И последнее, что мы рассмотрим на этом занятии – это рекурсивные методы, то есть, методы, которые вызывают сами себя. Да, да, так тоже можно делать. Давайте я сначала приведу пример работы рекурсивного метода, а, затем, мы разберемся как это работает.
static void up_and_down(int n) { System.out.println("Уровень вниз " + n); if (n 4) up_and_down(n + 1); System.out.println("Уровень вверх " + n); }
Запустим эту программу, и увидим следующее:
Уровень вниз 1
Уровень вниз 2
Уровень вниз 3
Уровень вниз 4
Уровень вверх 4
Уровень вверх 3
Уровень вверх 2
Уровень вверх 1
Почему все так получилось? Давайте разберемся.
Кстати, обратите внимание, если бы у нас не было вот этого условия, то рекурсия ушла бы вниз, пока стек методов не переполнился бы (возникла бы ошибка stack overflow), поэтому при реализации рекурсивных методов нужно внимательно относиться к глубине их вызова – она не должна быть большой.
Видео по теме
#1 Установка пакетов и первый запуск программы
#2 Структура программы, переменные, константы, оператор присваивания
#3 Консольный ввод/вывод, импорт пакетов
#4 Арифметические операции
#5 Условные операторы if и switch
#6 Операторы циклов while, for, do while
#7 Массивы, обработка элементов массива
#8 (часть 1) Строки в Java, методы класса String
#8 (часть 2) Строки — классы StringBuffer и StringBuider
#9 Битовые операции И, ИЛИ, НЕ, XOR
#10 Методы, их перегрузка и рекурсия
© 2023 Частичное или полное копирование информации с данного сайта для распространения на других ресурсах, в том числе и бумажных, строго запрещено. Все тексты и изображения являются собственностью сайта
Java Recursion
Recursion is the technique of making a function call itself. This technique provides a way to break complicated problems down into simple problems which are easier to solve.
Recursion may be a bit difficult to understand. The best way to figure out how it works is to experiment with it.
Recursion Example
Adding two numbers together is easy to do, but adding a range of numbers is more complicated. In the following example, recursion is used to add a range of numbers together by breaking it down into the simple task of adding two numbers:
Example
Use recursion to add all of the numbers up to 10.
public class Main < public static void main(String[] args) < int result = sum(10); System.out.println(result);
>public static int sum(int k) < if (k >0) < return k + sum(k - 1); >else < return 0;
> > >
Example Explained
When the sum() function is called, it adds parameter k to the sum of all numbers smaller than k and returns the result. When k becomes 0, the function just returns 0. When running, the program follows these steps:
10 + sum(9)
10 + ( 9 + sum(8) )
10 + ( 9 + ( 8 + sum(7) ) )
.
10 + 9 + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 1 + sum(0)
10 + 9 + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 1 + 0
Since the function does not call itself when k is 0, the program stops there and returns the result.
Halting Condition
Just as loops can run into the problem of infinite looping, recursive functions can run into the problem of infinite recursion. Infinite recursion is when the function never stops calling itself. Every recursive function should have a halting condition, which is the condition where the function stops calling itself. In the previous example, the halting condition is when the parameter k becomes 0.
It is helpful to see a variety of different examples to better understand the concept. In this example, the function adds a range of numbers between a start and an end. The halting condition for this recursive function is when end is not greater than start:
Example
Use recursion to add all of the numbers between 5 to 10.
public class Main < public static void main(String[] args) < int result = sum(5, 10); System.out.println(result);
>public static int sum(int start, int end) < if (end >start) < return end + sum(start, end - 1); >else < return end; >> >
The developer should be very careful with recursion as it can be quite easy to slip into writing a function which never terminates, or one that uses excess amounts of memory or processor power. However, when written correctly recursion can be a very efficient and mathematically-elegant approach to programming.