☕ Java

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