Cadeias de caracteres e trabalho com texto em Java 17: guia detalhado
Методы в Java: параметры, перегрузка и рекурсия для новичков
Методы (функции) — один из главных инструментов программирования. Они позволяют написать блок кода один раз и вызывать его многократно. Без методов каждая программа была бы огромным монолитом из повторяющегося кода.
Зачем нужны методы?
- DRY (Don't Repeat Yourself) — написал один раз, вызываешь много раз
- Читаемость —
calculateDiscount(price)понятнее 20 строк арифметики - Тестируемость — маленькие методы легче тестировать
- Разбивка задачи — сложное = сумма простых
Структура и вызов метода
public class MethodBasics {
// Структура:
// [модификатор] [static] тип_возврата имяМетода(тип параметр, ...) { тело }
// Без параметров, без возврата (void)
static void printSep() {
System.out.println("══════════════════════════════");
}
// С одним параметром
static void greet(String name) {
System.out.println("Привет, " + name + "! Добро пожаловать!");
}
// С несколькими параметрами
static void introduce(String name, int age, String city) {
System.out.printf("Меня зовут %s, мне %d лет, я из %s.%n", name, age, city);
}
// Возвращает значение
static int multiply(int a, int b) {
return a * b; // return: возвращает значение и ЗАВЕРШАЕТ метод
}
// Несколько точек return
static String classify(int n) {
if (n > 0) return "положительное";
if (n < 0) return "отрицательное";
return "ноль"; // достигается только когда n == 0
}
// Метод вычисляющий что-то полезное
static boolean isPrime(int n) {
if (n < 2) return false;
for (int i = 2; i <= Math.sqrt(n); i++)
if (n % i == 0) return false;
return true;
}
public static void main(String[] args) {
printSep();
greet("Иван");
greet("Мария"); // тот же метод, другой аргумент
introduce("Алексей", 25, "Москва");
int result = multiply(7, 8);
System.out.println("7 × 8 = " + result); // 56
System.out.println("3 × 4 = " + multiply(3, 4)); // можно сразу использовать
System.out.println(classify(42)); // положительное
System.out.println(classify(-5)); // отрицательное
System.out.println(classify(0)); // ноль
for (int i = 1; i <= 20; i++)
if (isPrime(i)) System.out.print(i + " ");
System.out.println(); // 2 3 5 7 11 13 17 19
printSep();
}
}
void vs return
public class VoidReturn {
// void: метод ДЕЛАЕТ что-то (побочный эффект), ничего не возвращает
static void printTable(int n, int rows) {
System.out.println("Таблица умножения для " + n + ":");
for (int i = 1; i <= rows; i++)
System.out.printf(" %d × %d = %d%n", n, i, n*i);
}
// return: метод ВЫЧИСЛЯЕТ и возвращает результат
static double circleArea(double r) { return Math.PI * r * r; }
static double circlePerim(double r) { return 2 * Math.PI * r; }
static boolean isLeapYear(int year) { return (year%4==0 && year%100!=0) || year%400==0; }
static int factorial(int n) { return n<=1 ? 1 : n * factorial(n-1); }
// clamp: ограничить значение диапазоном
static int clamp(int v, int lo, int hi) {
if (v < lo) return lo;
if (v > hi) return hi;
return v;
}
// Построить строку с разделителем
static String join(String sep, String... parts) {
return String.join(sep, parts);
}
public static void main(String[] args) {
printTable(9, 5);
System.out.printf("Круг r=5: площадь=%.2f, периметр=%.2f%n",
circleArea(5), circlePerim(5)); // 78.54, 31.42
System.out.println("2024 — високосный? " + isLeapYear(2024)); // true
System.out.println("5! = " + factorial(5)); // 120
System.out.println(clamp(50, 0, 100)); // 50
System.out.println(clamp(-5, 0, 100)); // 0
System.out.println(clamp(150, 0, 100)); // 100
System.out.println(join(", ", "яблоко", "груша", "банан")); // яблоко, груша, банан
}
}
Перегрузка методов (Overloading)
Аналогия: Слово "открыть" означает разное: открыть дверь, открыть файл, открыть счёт в банке. Одно имя — разные реализации в зависимости от контекста.
public class Overloading {
// Несколько методов с одинаковым именем — разные параметры
static double area(double r) { // Площадь круга
return Math.PI * r * r;
}
static double area(double w, double h) { // Площадь прямоугольника
return w * h;
}
static double area(double a, double b, double c) { // Треугольник (Герон)
double s = (a+b+c)/2;
return Math.sqrt(s*(s-a)*(s-b)*(s-c));
}
// Перегрузка print
static void print(int n) { System.out.println("int: " + n); }
static void print(double d) { System.out.println("double: " + d); }
static void print(String s) { System.out.println("String: " + s); }
static void print(boolean b){ System.out.println("bool: " + b); }
// max для разных типов
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 String max(String a, String b) { return a.compareTo(b)>0 ? a : b; } // лексически
public static void main(String[] args) {
// Java выбирает метод по типам аргументов — автоматически!
System.out.printf("Круг r=5: %.2f%n", area(5.0)); // 78.54
System.out.printf("Прямоугольник 4×6: %.2f%n", area(4.0, 6.0)); // 24.00
System.out.printf("Треугольник 3,4,5: %.2f%n", area(3.0, 4.0, 5.0)); // 6.00
print(42); // int: 42
print(3.14); // double: 3.14
print("Привет"); // String: Привет
print(true); // bool: true
System.out.println(max(3, 7)); // 7
System.out.println(max(3.5, 2.1)); // 3.5
System.out.println(max("abc","xyz")); // xyz
}
}
Параметры: pass by value
public class PassByValue {
static void tryModify(int value) {
value = 999; // Изменяем КОПИЮ — оригинал не тронут!
System.out.println("Внутри метода: " + value); // 999
}
// Объект: копируется ссылка, объект один и тот же
static void modifyArr(int[] arr) {
arr[0] = 999; // Изменяем через ссылку — работает!
}
static void replaceArr(int[] arr) {
arr = new int[]{1,2,3}; // Копия ссылки → новый объект
// Оригинальная ссылка в main осталась прежней!
}
public static void main(String[] args) {
int x = 42;
tryModify(x);
System.out.println("После вызова: " + x); // 42 — не изменился!
int[] arr = {10, 20, 30};
modifyArr(arr);
System.out.println("arr[0] = " + arr[0]); // 999 — изменился!
int[] arr2 = {10, 20, 30};
replaceArr(arr2);
System.out.println("arr2[0] = " + arr2[0]); // 10 — не изменился!
}
}
Varargs: переменное число аргументов
public class VarargsDemo {
// int... — принимает 0 или более аргументов типа int
static int sum(int... nums) {
int total = 0;
for (int n : nums) total += n;
return total;
}
static double average(double... nums) {
if (nums.length == 0) return 0;
double s = 0;
for (double n : nums) s += n;
return s / nums.length;
}
// Обычный параметр + varargs (varargs ОБЯЗАТЕЛЬНО последним!)
static void log(String level, String... messages) {
for (String m : messages)
System.out.println("[" + level.toUpperCase() + "] " + m);
}
public static void main(String[] args) {
System.out.println(sum()); // 0
System.out.println(sum(5)); // 5
System.out.println(sum(1,2,3,4,5)); // 15
System.out.println(sum(1,2,3,4,5,6,7,8,9,10)); // 55
System.out.println(average(90,85,92,78,95)); // 88.0
log("info", "Программа запущена");
log("error", "Ошибка БД", "Retry #1", "Retry #2");
}
}
Рекурсия: метод вызывает сам себя
Аналогия: Матрёшка. Открываете большую — внутри такая же, но меньше. И так до самой маленькой, которую нельзя открыть (базовый случай). Без базового случая — бесконечная рекурсия!
public class RecursionDemo {
// Факториал: n! = n × (n-1)!
// Базовый случай: 0!=1, 1!=1
static long fact(int n) {
if (n <= 1) return 1; // БАЗОВЫЙ СЛУЧАЙ — стоп!
return n * fact(n - 1); // Рекурсивный вызов
}
/* Трассировка fact(4):
* fact(4) = 4 * fact(3)
* fact(3) = 3 * fact(2)
* fact(2) = 2 * fact(1)
* fact(1) = 1 ← базовый случай
* return 2*1 = 2
* return 3*2 = 6
* return 4*6 = 24
*/
// Числа Фибоначчи: F(n) = F(n-1) + F(n-2)
static int fib(int n) {
if (n <= 0) return 0;
if (n == 1) return 1;
return fib(n-1) + fib(n-2);
}
// Сумма цифр числа: digitSum(123) = 1+2+3 = 6
static int digitSum(int n) {
n = Math.abs(n);
if (n < 10) return n;
return n%10 + digitSum(n/10);
}
// Бинарный поиск в отсортированном массиве
static int bSearch(int[] a, int target, int lo, int hi) {
if (lo > hi) return -1; // Не найдено
int mid = (lo+hi)/2;
if (a[mid] == target) return mid;
if (a[mid] < target) return bSearch(a, target, mid+1, hi);
return bSearch(a, target, lo, mid-1);
}
public static void main(String[] args) {
// Факториал
for (int i=0; i<=10; i++) System.out.printf("%2d! = %d%n", i, fact(i));
// Фибоначчи
System.out.print("Фибоначчи: ");
for (int i=0; i<=10; i++) System.out.print(fib(i)+" ");
System.out.println();
// Сумма цифр
System.out.println("digitSum(12345) = " + digitSum(12345)); // 15
System.out.println("digitSum(9999) = " + digitSum(9999)); // 36
// Бинарный поиск
int[] sorted = {1,3,5,7,9,11,13,15,17,19};
System.out.println("Индекс 7: " + bSearch(sorted,7,0,9)); // 3
System.out.println("Индекс 8: " + bSearch(sorted,8,0,9)); // -1
}
}
Рекурсия vs Итерация
// Итеративный факториал (цикл)
static long factIter(int n) {
long result = 1;
for (int i=2; i<=n; i++) result *= i;
return result;
}
// Рекурсивный — то же самое, но через стек вызовов
static long factRecur(int n) { return n<=1 ? 1 : n*factRecur(n-1); }
System.out.println(factIter(10)); // 3628800
System.out.println(factRecur(10)); // 3628800 (одинаково!)
// ОПАСНОСТЬ: каждый вызов занимает место в стеке!
// factRecur(100_000); // StackOverflowError!
// Правило: если можно написать итеративно — пишите итеративно.
// Рекурсия хороша для деревьев, графов, и других рекурсивных структур.
Итого: что мы узнали
- ✅ Метод — именованный блок кода для повторного использования
- ✅ Структура: [модификатор] [static] тип_возврата имя(параметры) { тело }
- ✅
void— ничего не возвращает;return значение;— возвращает - ✅ Перегрузка: одинаковое имя + разные параметры — Java выбирает нужный
- ✅ Параметры передаются по значению (копия для примитивов)
- ✅ Объекты: копируется ссылка → изменения объекта видны снаружи
- ✅
тип...— varargs: метод принимает любое число аргументов - ✅ Рекурсия: метод вызывает себя + базовый случай обязателен!
- ✅ Без базового случая — бесконечная рекурсия →
StackOverflowError