Урок 8. Методы
Метод — именованный кусок программы, который можно вызывать много раз. Метод main уже знаком: с него начинается программа. В этом уроке — свои методы. В других языках то же самое называют функциями.
Методы избавляют от повторов и делят программу на понятные части: вместо одного длинного main — несколько коротких методов с говорящими именами.
Первый метод
public class Main {
static int square(int x) {
return x * x;
}
static void greet(String name) {
System.out.println("Привет, " + name + "!");
}
public static void main(String[] args) {
System.out.println(square(5));
int s = square(3) + square(4);
System.out.println(s);
greet("Анна");
}
}25
25
Привет, Анна!Из чего состоит объявление static int square(int x):
static— метод принадлежит классу, и его можно вызвать изmainбез создания объекта. Что это значит подробно, рассказывает справка Что такое static; пока пишитеstaticу всех методов рядом сmain.int— тип возвращаемого значения: метод вернёт целое число.square— имя метода, глаголом или по смыслу, в стиле camelCase.(int x)— параметры: что метод получает на вход. Их может быть несколько через запятую или ни одного.
return возвращает значение и сразу завершает метод. Метод с типом void ничего не возвращает — он что-то делает, например печатает. В нём return; без значения можно использовать, чтобы выйти раньше.
Методы объявляются внутри класса, но вне других методов. Порядок не важен: main может стоять и в начале, и в конце класса.
Метод с условием и циклом
Метод может содержать любой код: условия, циклы, вызовы других методов. Проверка числа на простоту:
public class Main {
static boolean isPrime(int n) {
if (n < 2) {
return false;
}
for (int i = 2; i * i <= n; i++) {
if (n % i == 0) {
return false;
}
}
return true;
}
public static void main(String[] args) {
System.out.println(isPrime(97));
System.out.println(isPrime(91));
int count = 0;
for (int i = 1; i <= 100; i++) {
if (isPrime(i)) {
count++;
}
}
System.out.println("Простых до 100: " + count);
}
}true
false
Простых до 100: 25Как только найден делитель, return false завершает метод — дальше проверять незачем. До return true программа доходит, только если делителей не нашлось. Делители ищут до квадратного корня (i * i <= n): если у числа есть делитель больше корня, то есть и парный меньше. 91 = 7 · 13, поэтому оно не простое.
Если в методе с типом int или boolean есть путь, на котором return не встречается, компилятор выдаст ошибку missing return statement.
Перегрузка
В одном классе могут быть методы с одним именем, но разными параметрами — это перегрузка. Компилятор выбирает нужный по типам аргументов:
public class Main {
static int max(int a, int b) {
return a > b ? a : b;
}
static double max(double a, double b) {
return a > b ? a : b;
}
static int max(int a, int b, int c) {
return max(max(a, b), c);
}
public static void main(String[] args) {
System.out.println(max(3, 7));
System.out.println(max(2.5, 1.5));
System.out.println(max(4, 9, 6));
}
}7
2.5
9Методы различаются числом и типами параметров. Одного лишь другого типа возвращаемого значения для перегрузки мало.
Переменные метода
Параметры и переменные, объявленные внутри метода, — локальные: они существуют, только пока метод выполняется, и не видны из других методов. Две переменные i в разных методах — разные переменные.
Аргументы передаются по значению: метод получает копию. Если это число, изменения внутри метода снаружи не видны. Если массив или объект — копируется ссылка, и метод может изменить сами элементы:
import java.util.Arrays;
public class Main {
static void addOne(int x) {
x = x + 1;
}
static void addOneToAll(int[] arr) {
for (int i = 0; i < arr.length; i++) {
arr[i]++;
}
}
public static void main(String[] args) {
int n = 5;
addOne(n);
System.out.println(n);
int[] a = {1, 2, 3};
addOneToAll(a);
System.out.println(Arrays.toString(a));
}
}5
[2, 3, 4]Чтобы метод «изменил» число, он должен вернуть новое значение, а вызывающий код — сохранить его: n = plusOne(n);.
Рекурсия
Метод может вызывать сам себя — это рекурсия. Классический пример — факториал: 5! = 5 · 4 · 3 · 2 · 1.
public class Main {
static long factorial(int n) {
if (n <= 1) {
return 1;
}
return n * factorial(n - 1);
}
public static void main(String[] args) {
System.out.println(factorial(5));
System.out.println(factorial(20));
}
}120
2432902008176640000У рекурсии обязательно должен быть случай выхода — здесь n <= 1. Без него метод вызывает себя бесконечно, и программа падает с ошибкой StackOverflowError — «переполнение стека». Стек вызовов — память, где каждый незаконченный вызов метода хранит свои переменные; у бесконечной рекурсии она кончается. Тип long взят потому, что 20! не помещается в int.
Частые ошибки
non-static method … cannot be referenced from a static context— у метода забылиstatic, а вызывают его изmain. Справка Что такое static.missing return statement— не на всех путях естьreturn.- Метод объявлен внутри
main— так нельзя, методы пишут на уровне класса. - Результат не сохранён —
square(5);отдельной строкой считает квадрат и теряет его.
Коротко
- Метод:
static тип имя(параметры) { … };returnвозвращает значение,void— ничего не возвращает. - Методы объявляются в классе, вне других методов; порядок не важен.
- Перегрузка — одно имя, разные параметры.
- Числа передаются копией, у массивов и объектов копируется ссылка — элементы метод изменить может.
- У рекурсии обязателен случай выхода.