Параметры и return
Параметры vs аргументы, передача по значению, возврат значения и проверка аргументов
Введение: зачем методам нужны параметры и возврат?
Представьте, что вы приходите в кафе и говорите бариста: «Сделайте мне кофе». Это будет работать, только если бариста знает, какой именно кофе вы хотите. Капучино? Латте? Эспрессо? Без молока? С сахаром? Именно от этих деталей зависит результат.
В программировании методы работают точно так же. Метод без параметров — это как рецепт без списка ингредиентов: он может приготовить что-то, но не совсем то, что вам нужно. А метод без возврата значения — это как бариста, который готовит кофе, но не отдаёт его вам.
В этом уроке мы детально разберём, как методы принимают входные данные (параметры) и как возвращают результат (return). Вы узнаете, почему важно различать понятия «параметр» и «аргумент», как Java обрабатывает передачу данных в методы, и как правильно проверять входные данные, чтобы ваш код был надёжным и безопасным.
Аналогия для запоминания: Метод — это как автомат по выдаче напитков. Параметры — это кнопки на панели (что вы хотите), а return — это напиток, который вы получаете. Без параметров автомат не знает, что делать. Без return вы не получите результат.
Параметры метода: объявление, типы и имена
Параметр — это переменная, которая объявлена в заголовке метода. Он определяет, какой тип данных метод может принять и как это данные будут использоваться внутри метода. Параметры перечисляются в круглых скобках после имени метода.
Каждый параметр состоит из двух частей: типа и имени. Тип определяет, какие значения можно передать, а имя используется внутри метода для обращения к этим значениям.
public static void greet(String name) {
System.out.println("Привет, " + name + "!");
}
В этом примере String name — это параметр. String — тип параметра (строка текста), а name — имя параметра. Внутри метода вы используете name как обычную переменную.
Давайте рассмотрим несколько примеров параметров разных типов:
public static void printArea(double width, double height) {
double area = width * height;
System.out.println("Площадь: " + area);
}
public static void printNumber(int number) {
System.out.println("Число: " + number);
}
public static void printMessage(String message, int repeatCount) {
for (int i = 0; i < repeatCount; i++) {
System.out.println(message);
}
}
public static void printLine(char symbol, int length) {
String line = "";
for (int i = 0; i < length; i++) {
line = line + symbol;
}
System.out.println(line);
}
Важно: Имена параметров должны соответствовать правилам именования переменных в Java: начинаться с маленькой буквы, содержать только буквы, цифры и символ подчёркивания, и не быть зарезервированными словами (int, class, public и т.д.).
Параметры могут быть любого типа: примитивные (int, double, boolean, char) и ссылочные (String, массивы, объекты классов). Это даёт методам огромную гибкость — они могут работать с любыми данными.
Метод без параметров — это тоже合法ный метод. Он просто не принимает никаких входных данных:
public static void sayHello() {
System.out.println("Привет!");
}
Такой метод ничего не принимает на вход, но всё равно может выполнять полезную работу. Однако大多数 методы всё-таки принимают параметры, потому что это делает их более универсальными и переиспользуемыми.
Параметры vs аргументы: чёткое разграничение терминов
Это один из самых частых вопросов у начинающих программистов: «Чем параметр отличается от аргумента?» Давайте разберёмся раз и навсегда.
Параметр (parameter) — это переменная, объявленная в сигнатуре метода. Это как пустая строка в анкете: «ФИО: _______». Параметр определяет, что метод ожидает получить.
Аргумент (argument) — это конкретное значение, которое вы передаёте методу при вызове. Это как заполненная строка в анкете: «ФИО: Иванов Иван Иванович». Аргумент — это то, что метод реально получает.
public static int add(int a, int b) {
return a + b;
}
// Вызов метода:
int result = add(5, 3);
// ^ ^
// | |
// аргумент аргумент
// В объявлении метода:
public static int add(int a, int b)
// ^ ^
// | |
// параметр параметр
Вот как это работает пошагово:
- Метод
addобъявлен с двумя параметрами:int aиint b - При вызове
add(5, 3)вы передаёте два аргумента: 5 и 3 - Java присваивает аргумент 5 параметру
a, а аргумент 3 — параметруb - Внутри метода
aравно 5, аbравно 3
Аналогия: Параметр — это поле в форме заказа («Количество: ___»), а аргумент — это число, которое вы пишете в этом поле («Количество: 3»). Форма не меняется, но данные в ней могут быть разными при каждом заказе.
На практике многие программисты используют эти термины как взаимозаменяемые, и это не вызывает недопонимания в рабочем окружении. Однако на экзаменах, собеседованиях и в документации разграничение важно. Запомните простое правило:
Правило: Параметр — в объявлении метода. Аргумент — при вызове метода. Параметр — это ожидание. Аргумент — это реальность.
Количество аргументов при вызове метода должно точно совпадать с количеством параметров в объявлении метода. Если метод объявлен с тремя параметрами, вы обязаны передать три аргумента — не два, не четыре, а ровно три:
public static String createFullName(String firstName, String middleName, String lastName) {
return firstName + " " + middleName + " " + lastName;
}
// Правильно — три аргумента для трёх параметров:
String full = createFullName("Иван", "Петрович", "Сидоров");
// Ошибка компиляции — не совпадает количество:
// createFullName("Иван", "Петрович"); // Только 2 аргумента!
// createFullName("Иван", "Петрович", "Сидоров", "мл."); // 4 аргумента!
Кроме того, типы аргументов должны быть совместимы с типами параметров. Java не разрешит передать строку туда, где ожидается число, без явного преобразования:
public static void printSum(int a, int b) {
System.out.println(a + b);
}
printSum(10, 20); // OK: int + int
printSum(10, 20L); // Ошибка: long нельзя передать в int без приведения
printSum("10", "20"); // Ошибка: String нельзя передать в int
Несколько параметров: порядок, типы и комбинации
Методы могут принимать сколько угодно параметров — от нуля до десятков (хотя на практике более 5-6 параметров считается признаком того, что метод стоит разделить на более мелкие).
Порядок параметров имеет значение! При вызове метода аргументы привязываются к параметрам позиционно — первый аргумент к первому параметру, второй ко второму и так далее:
public static void printPersonInfo(String name, int age, String city) {
System.out.println("Имя: " + name);
System.out.println("Возраст: " + age);
System.out.println("Город: " + city);
}
// Правильный порядок:
printPersonInfo("Анна", 25, "Москва");
// Результат:
// Имя: Анна
// Возраст: 25
// Город: Москва
// Неправильный порядок — компилятор выдаст ошибку:
// printPersonInfo(25, "Анна", "Москва");
// Ошибка: int нельзя присвоить параметру String
Вы можете смешивать разные типы параметров в одном методе. Это нормальная практика — метод должен принимать именно те данные, которые ему нужны для работы:
public static String createOrderString(String product, int quantity, double price, boolean withDiscount) {
double total = quantity * price;
if (withDiscount) {
total = total * 0.9;
}
return product + " x" + quantity + " = " + total + " руб.";
}
String order1 = createOrderString("Ноутбук", 1, 54999.0, true);
// Результат: "Ноутбук x1 = 49499.1 руб."
String order2 = createOrderString("Мышь", 3, 999.0, false);
// Результат: "Мышь x3 = 2997.0 руб."
Обратите внимание, что в методе createOrderString используются четыре параметра разных типов: строка, целое число, число с плавающей точкой и логическое значение. Это perfectly нормально — главное, чтобы при вызове вы передавали аргументы в правильном порядке и нужных типов.
Давайте рассмотрим ещё один пример — метод, который вычисляет среднее арифметическое трёх чисел:
public static double average(double a, double b, double c) {
return (a + b + c) / 3.0;
}
double avg = average(10.0, 20.0, 30.0);
System.out.println("Среднее: " + avg); // Среднее: 20.0
double avg2 = average(4.5, 7.8, 9.2);
System.out.println("Среднее: " + avg2); // Среднее: 7.166666666666667
Совет: Когда у метода много параметров, записывайте каждый параметр на новой строке в объявлении метода. Это улучшает читаемость кода:
// Плохо — трудно читать:
public static void createUser(String n, String e, int a, String c, boolean a2, String p)
// Хорошо — каждый параметр на новой строке:
public static void createUser(
String name,
String email,
int age,
String city,
boolean isActive,
String password
) {
// код метода
}
Передача по значению: primitive types vs ссылочные типы
Это один из самых важных и самых сложных概念ов в Java. Когда вы передаёте аргумент методу, Java всегда передаёт копию значения. Это называется «передача по значению» (pass by value). Но как именно это работает для разных типов данных?
Примитивные типы: полная копия
Для примитивных типов (int, double, boolean, char и т.д.) Java создаёт полную копию значения и передаёт её методу. Изменения параметра внутри метода никак не влияют на оригинальную переменную:
public static void tryToChange(int number) {
number = 100;
System.out.println("Внутри метода: " + number); // 100
}
public static void main(String[] args) {
int x = 5;
tryToChange(x);
System.out.println("Снаружи метода: " + x); // 5 — НЕ 100!
}
Почему так происходит? Представьте, что у вас есть рецепт торта. Вы даёте копию рецепта другу. Друг может изменить свою копию сколько угодно, но ваш рецепт останется прежним. Вот так же работает передача примитивных типов — метод получает копию, а не оригинал.
Давайте рассмотрим ещё один пример, чтобы закрепить понимание:
public static void addTen(int number) {
number = number + 10;
System.out.println("Внутри: " + number);
}
public static void main(String[] args) {
int score = 50;
addTen(score);
System.out.println("Снаружи: " + score); // Всё ещё 50!
}
// Вывод:
// Внутри: 60
// Снаружи: 50
Метод addTen получает копию числа 50. Он увеличивает свою копию до 60, но оригинал остаётся 50. Это фундаментальное свойство Java — методы не могут изменить примитивные значения, переданные им в качестве аргументов.
Ссылочные типы: копия ссылки, а не объекта
Для ссылочных типов (String, массивы, объекты классов) ситуация более интересная. Java передаёт копию ссылки на объект, а не самого объекта. Это значит, что метод может изменить состояние объекта, на который ссылка указывает, но не может заставить оригинальную ссылку указывать на другой объект.
public static void modifyArray(int[] numbers) {
numbers[0] = 999;
System.out.println("Внутри метода: " + numbers[0]); // 999
}
public static void main(String[] args) {
int[] data = {1, 2, 3, 4, 5};
System.out.println("До: " + data[0]); // 1
modifyArray(data);
System.out.println("После: " + data[0]); // 999 — ИЗМЕНИЛОСЬ!
}
В этом случае метод modifyArray получает копию ссылки на массив. Обе ссылки (оригинальная и копия) указывают на один и тот же массив в памяти. Поэтому изменение элемента массива через копию ссылки влияет на оригинальный массив.
Но попытка заставить ссылку указывать на другой объект не сработает:
public static void tryToReplace(int[] numbers) {
numbers = new int[]{100, 200, 300};
System.out.println("Внутри метода: " + numbers[0]); // 100
}
public static void main(String[] args) {
int[] data = {1, 2, 3};
tryToReplace(data);
System.out.println("После: " + data[0]); // Всё ещё 1!
}
Почему? Потому что внутри метода tryToReplace мы создаём новый массив и присваиваем его локальной копии ссылки. Оригинальная ссылка data в методе main по-прежнему указывает на старый массив.
Ключевое правило: Java ВСЕГДА передаёт по значению. Для примитивных типов — это копия значения. Для ссылочных типов — это копия ссылки (но не объекта). Метод не может изменить原始ную примитивную переменную или заставить原始ную ссылку указывать на другой объект.
Это же правило работает и для строк (String). Строки в Java неизменяемы (immutable), поэтому даже если метод получает ссылку на строку, он не может изменить её содержимое:
public static void tryToChangeString(String s) {
s = "Новое значение";
}
public static void main(String[] args) {
String original = "Старое значение";
tryToChangeString(original);
System.out.println(original); // Старое значение — не изменилось!
}
Это поведение может быть неочевидным для новичков, но оно обеспечивает предсказуемость и безопасность кода. Вы можете быть уверены, что вызов метода не «сломает» ваши переменные где-то за пределами метода.
Возврат значения: return с разными типами и return void
Ключевое слово return выполняет две функции: возвращает значение из метода и немедленно завершает его выполнение. Это как официант, который приносит вам заказ и уходит — метод отдал результат и прекратил работу.
Методы, возвращающие значение
Если метод должен вернуть какое-то значение, в его заголовке указывается тип возвращаемого значения перед именем метода. Тип может быть любым: примитивным или ссылочным:
public static int getMax(int a, int b) {
if (a > b) {
return a;
} else {
return b;
}
}
public static double calculateCircleArea(double radius) {
return Math.PI * radius * radius;
}
public static String toUpperCase(String text) {
return text.toUpperCase();
}
public static boolean isPositive(int number) {
return number > 0;
}
public static int[] createArray(int size) {
int[] arr = new int[size];
for (int i = 0; i < size; i++) {
arr[i] = i + 1;
}
return arr;
}
Обратите внимание: тип значения, которое вы возвращаете через return, должен совпадать с типом, указанным в заголовке метода. Вы не можете объявить метод как возвращающий int и при этом вернуть строку:
public static int getNumber() {
return 42; // OK: int соответствует int
// return "42"; // Ошибка: String не соответствует int
// return 3.14; // Ошибка: double не соответствует int (без приведения)
}
Вызов метода, возвращающего значение, можно использовать где угодно, где ожидается значение этого типа:
int bigger = getMax(10, 20);
double area = calculateCircleArea(5.0);
String upper = toUpperCase("hello");
boolean check = isPositive(42);
int[] numbers = createArray(10);
// Можно использовать прямо в выражениях:
System.out.println("Максимум: " + getMax(15, 25));
if (isPositive(getMax(-5, 10))) {
System.out.println("Результат положительный");
}
Методы без возврата (void)
Когда метод не возвращает никакого значения, в заголовке указывается ключевое слово void. Такие методы выполняют какое-то действие (печатают текст, сохраняют данные, изменяют состояние объекта), но не вычисляют результат для возврата:
public static void printLine(int length) {
for (int i = 0; i < length; i++) {
System.out.print("-");
}
System.out.println();
}
public static void shout(String message) {
System.out.println(message.toUpperCase() + "!!!");
}
public static void increment(int[] numbers) {
for (int i = 0; i < numbers.length; i++) {
numbers[i]++;
}
}
Методы с void нельзя использовать в выражениях, где ожидается значение:
printLine(20); // OK: просто вызываем метод
shout("Привет"); // OK
// Ошибка компиляции:
// int x = printLine(20); // void нельзя присвоить переменной
// String s = shout("Привет"); // void ничего не возвращает
Возврат из void-метода
Даже в методах с void можно использовать return без значения — это немедленно завершает метод:
public static void printPositiveNumbers(int[] numbers) {
for (int num : numbers) {
if (num <= 0) {
return; // Прерываем выполнение — дальше не печатаем
}
System.out.println(num);
}
}
int[] data = {5, 10, -3, 20};
printPositiveNumbers(data); // Печатает: 5, 10 (останавливается на -3)
Ранний return: паттерн guard clause
Паттерн «ранний return» (early return) или «сторожевой класс» (guard clause) — это приём, при котором метод проверяет условия в самом начале и завершается, если условие не выполнено. Это делает код чище и понятнее, потому что основная логика метода не «утопает» в вложенных условиях.
Без раннего return код выглядит так:
public static String processAge(int age) {
String result;
if (age >= 0) {
if (age <= 150) {
result = "Возраст: " + age;
} else {
result = "Некорректный возраст";
}
} else {
result = "Возраст не может быть отрицательным";
}
return result;
}
А с ранним return:
public static String processAge(int age) {
if (age < 0) {
return "Возраст не может быть отрицательным";
}
if (age > 150) {
return "Некорректный возраст";
}
return "Возраст: " + age;
}
Обратите внимание, как упростился код. Вместо трёх уровней вложенности у нас три последовательные проверки, каждая из которых «сторожит» вход в метод. Если какая-то проверка не проходит, метод сразу завершается и возвращает сообщение об ошибке.
Ранний return особенно полезен в следующих ситуациях:
- Проверка входных данных — убедиться, что аргументы допустимы, прежде чем выполнять основную логику
- Проверка особых случаев — обработать edge cases в начале метода
- Уменьшение вложенности — сделать код плоским и легко читаемым
- Устранение else — если после проверки возвращается значение, else не нужен
Вот более сложный пример — метод, который обрабатывает заказ:
public static String processOrder(String product, int quantity, double price) {
if (product == null || product.isEmpty()) {
return "Ошибка: товар не указан";
}
if (quantity <= 0) {
return "Ошибка: количество должно быть положительным";
}
if (price < 0) {
return "Ошибка: цена не может быть отрицательной";
}
double total = quantity * price;
if (total > 1000000) {
return "Ошибка: сумма заказа слишком велика";
}
return "Заказ: " + product + " x" + quantity + " = " + total + " руб.";
}
Этот метод последовательно проверяет каждое условие и «выбрасывает» из себя, если какое-то условие не выполнено. Основная логика (вычисление суммы) оказывается в конце, на видном месте, и её легко найти и понять.
Аналогия: Ранний return — как проверка документов на входе в аэропорт. Сначала проверяют паспорт, потом посадочный талон, потом багаж. Если на каком-то этапе проблема — вас разворачивают, и вы не идёте дальше. Не нужно проходить все проверки, если первая уже не пройдена.
Однако не стоит злоупотреблять ранним return. Если у вас больше 3-4 guard clauses подряд, возможно, стоит задуматься о разделении метода на более мелкие части. Каждый guard clause должен проверять одно логическое условие, и все они должны быть связаны с валидацией входных данных.
Проверка аргументов (validation): if + throw и Objects.requireNonNull
Проверка аргументов (валидация) — это один из самых важных навыков в программировании. Даже если ваш метод работает идеально с правильными данными, он может сломаться или, что ещё хуже, тихо работать неправильно с некорректными данными.
Есть два основных подхода к проверке аргументов в Java:
Подход 1: Проверка с помощью if и throw
Вы проверяете условие и, если оно не выполнено, «бросаете» исключение с помощью throw. Это как сигнализация — если что-то не так, код немедленно останавливается и сообщает об ошибке:
public static double divide(double a, double b) {
if (b == 0) {
throw new IllegalArgumentException("Деление на ноль невозможно");
}
return a / b;
}
public static int getGrade(int score) {
if (score < 0 || score > 100) {
throw new IllegalArgumentException(
"Оценка должна быть от 0 до 100, получено: " + score
);
}
if (score >= 90) return 5;
if (score >= 75) return 4;
if (score >= 60) return 3;
return 2;
}
public static String repeatString(String text, int count) {
if (text == null) {
throw new IllegalArgumentException("Строка не может быть null");
}
if (count < 0) {
throw new IllegalArgumentException(
"Количество повторений не может быть отрицательным: " + count
);
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < count; i++) {
sb.append(text);
}
return sb.toString();
}
Когда вы «бросаете» исключение, выполнение метода прерывается немедленно. Если никто не обработал это исключение (с помощью блока try-catch), программа завершится с сообщением об ошибке. Это нормально — лучше пусть программа остановится с понятной ошибкой, чем продолжит работать с некорректными данными.
Подход 2: Objects.requireNonNull
Для проверки, что ссылка не является null, Java предоставляет утилитный метод Objects.requireNonNull из пакета java.util.Objects:
import java.util.Objects;
public static String greetUser(String name) {
Objects.requireNonNull(name, "Имя пользователя не может быть null");
return "Привет, " + name + "!";
}
public static int getLength(String text) {
Objects.requireNonNull(text, "Строка не может быть null");
return text.length();
}
Метод Objects.requireNonNull проверяет, что ссылка не равна null. Если она null, метод выбрасывает NullPointerException с указанным сообщением. Если ссылка не null, метод возвращает саму ссылку — это удобно для цепочек вызовов.
Преимущество Objects.requireNonNull перед ручной проверкой:
- Код короче и выразительнее
- Не нужно писать один и тот же шаблон проверки снова и снова
- Сообщение об ошибке стандартное и понятное
- Можно добавить собственное описание ошибки вторым аргументом
import java.util.Objects;
public static void processUser(String name, int age, String email) {
Objects.requireNonNull(name, "Имя не может быть null");
Objects.requireNonNull(email, "Email не может быть null");
if (age < 0 || age > 150) {
throw new IllegalArgumentException("Некорректный возраст: " + age);
}
System.out.println("Обработка пользователя: " + name);
}
Какой тип исключения использовать? Для проверки аргументов метода используется IllegalArgumentException — это стандартное исключение Java для ситуации, когда аргумент метода не соответствует ожиданиям. Для проверки null — NullPointerException (или Objects.requireNonNull).
Когда проверять, а когда доверять?
Не стоит проверять каждый аргумент каждого метода — это сделает код перегруженным. Вот общие рекомендации:
- Проверяйте аргументы публичных методов — вы не контролируете, кто и как их вызовет
- Проверяйте данные, пришедшие извне (ввод пользователя, файлы, сети, база данных)
- Не проверяйте аргументы приватных методов, если вы контролируете все вызовы
- Не проверяйте то, что уже проверено на предыдущем уровне вызова
- Всегда проверяйте деление на ноль, индексы массивов и ссылки, которые могут быть null
Методы, возвращающие boolean: isXxx, hasXxx, canXxx
Методы, возвращающие boolean, — одни из самых распространённых в Java. Они отвечают на вопрос «да или нет?» и используют именованные префиксы для ясности:
- is — проверяет состояние или свойство (isEmpty, isPositive, isEven)
- has — проверяет наличие чего-либо (hasElements, hasDiscount, hasPermission)
- can — проверяет возможность действия (canEdit, canDelete, canDrive)
- contains — проверяет вхождение (containsWord, containsNumber)
- matches — проверяет соответствие шаблону (matchesPattern, matchesRegex)
public static boolean isEven(int number) {
return number % 2 == 0;
}
public static boolean isPositive(double number) {
return number > 0;
}
public static boolean isEmpty(String text) {
return text == null || text.length() == 0;
}
public static boolean hasPermission(String role, String action) {
if (role.equals("admin")) return true;
if (role.equals("editor") && action.equals("read")) return true;
if (role.equals("editor") && action.equals("write")) return true;
return false;
}
public static boolean canDivide(int a, int b) {
return b != 0;
}
public static boolean containsWord(String text, String word) {
if (text == null || word == null) return false;
return text.toLowerCase().contains(word.toLowerCase());
}
Методы, возвращающие boolean, идеально подходят для условных конструкций и фильтрации:
int[] numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
System.out.println("Чётные числа:");
for (int num : numbers) {
if (isEven(num)) {
System.out.println(num);
}
}
String userInput = " ";
if (isEmpty(userInput)) {
System.out.println("Пожалуйста, введите данные");
}
String role = "editor";
if (hasPermission(role, "write")) {
System.out.println("Вы можете редактировать");
} else {
System.out.println("Недостаточно прав");
}
Преимущество таких методов в том, что они делают код самодокументируемым. Вместо того чтобы писать if (numbers[i] % 2 == 0), вы пишете if (isEven(numbers[i])) — и сразу понятно, что проверяется.
Вот ещё несколько полезных методов, возвращающих boolean:
public static boolean isPalindrome(String text) {
if (text == null) return false;
String clean = text.toLowerCase().replaceAll("\\s+", "");
int left = 0;
int right = clean.length() - 1;
while (left < right) {
if (clean.charAt(left) != clean.charAt(right)) {
return false;
}
left++;
right--;
}
return true;
}
public static boolean isLeapYear(int year) {
return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
}
public static boolean isPrime(int number) {
if (number < 2) return false;
for (int i = 2; i * i <= number; i++) {
if (number % i == 0) return false;
}
return true;
}
System.out.println(isPalindrome("А роза упала на лапу Азора")); // true
System.out.println(isLeapYear(2024)); // true
System.out.println(isPrime(17)); // true
Методы, возвращающие String, int и double: множество примеров
Давайте рассмотрим множество практических примеров методов, возвращающих разные типы данных. Это поможет вам увидеть, как return используется в реальных задачах.
Методы, возвращающие String
public static String formatPrice(double price) {
if (price == (int) price) {
return (int) price + " руб.";
}
return String.format("%.2f руб.", price);
}
public static String getDayOfWeek(int dayNumber) {
switch (dayNumber) {
case 1: return "Понедельник";
case 2: return "Вторник";
case 3: return "Среда";
case 4: return "Четверг";
case 5: return "Пятница";
case 6: return "Суббота";
case 7: return "Воскресенье";
default: return "Некорректный день";
}
}
public static String censorWord(String text, String badWord) {
if (text == null || badWord == null) return text;
String replacement = "";
for (int i = 0; i < badWord.length(); i++) {
replacement = replacement + "*";
}
return text.replaceAll(badWord, replacement);
}
public static String reverseString(String text) {
if (text == null) return null;
return new StringBuilder(text).reverse().toString();
}
public static String getInitials(String firstName, String lastName) {
return "" + firstName.charAt(0) + ". " + lastName.charAt(0) + ".";
}
System.out.println(formatPrice(1500.0)); // "1500 руб."
System.out.println(formatPrice(1500.50)); // "1500.50 руб."
System.out.println(getDayOfWeek(3)); // "Среда"
System.out.println(censorWord("Какой кошмар", "кошмар")); // "Какой *****"
System.out.println(reverseString("Hello")); // "olleH"
System.out.println(getInitials("Иван", "Петров")); // "И.П."
Методы, возвращающие int
public static int countVowels(String text) {
if (text == null) return 0;
int count = 0;
String lower = text.toLowerCase();
for (int i = 0; i < lower.length(); i++) {
char ch = lower.charAt(i);
if (ch == 'а' || ch == 'е' || ch == 'ё' || ch == 'и' ||
ch == 'о' || ch == 'у' || ch == 'ы' || ch == 'э' ||
ch == 'ю' || ch == 'я') {
count++;
}
}
return count;
}
public static int factorial(int n) {
if (n < 0) {
throw new IllegalArgumentException("Факториал отрицательного числа");
}
if (n == 0 || n == 1) return 1;
int result = 1;
for (int i = 2; i <= n; i++) {
result = result * i;
}
return result;
}
public static int sumOfDigits(int number) {
number = Math.abs(number);
int sum = 0;
while (number > 0) {
sum = sum + number % 10;
number = number / 10;
}
return sum;
}
public static int maxValue(int[] numbers) {
if (numbers == null || numbers.length == 0) {
throw new IllegalArgumentException("Массив пуст");
}
int max = numbers[0];
for (int i = 1; i < numbers.length; i++) {
if (numbers[i] > max) {
max = numbers[i];
}
}
return max;
}
public static int gcd(int a, int b) {
a = Math.abs(a);
b = Math.abs(b);
while (b != 0) {
int temp = b;
b = a % b;
a = temp;
}
return a;
}
System.out.println(countVowels("Привет, мир!")); // 3
System.out.println(factorial(5)); // 120
System.out.println(sumOfDigits(12345)); // 15
System.out.println(maxValue(new int[]{3, 7, 1, 9})); // 9
System.out.println(gcd(12, 8)); // 4
Методы, возвращающие double
public static double calculateAverage(int[] numbers) {
if (numbers == null || numbers.length == 0) {
throw new IllegalArgumentException("Массив пуст");
}
int sum = 0;
for (int num : numbers) {
sum = sum + num;
}
return (double) sum / numbers.length;
}
public static double celsiusToFahrenheit(double celsius) {
return celsius * 9.0 / 5.0 + 32;
}
public static double calculateTip(double billAmount, int percent) {
return billAmount * percent / 100.0;
}
public static double standardDeviation(double[] values) {
if (values == null || values.length == 0) {
throw new IllegalArgumentException("Массив пуст");
}
double sum = 0;
for (double v : values) {
sum = sum + v;
}
double mean = sum / values.length;
double sumSquaredDiff = 0;
for (double v : values) {
double diff = v - mean;
sumSquaredDiff = sumSquaredDiff + diff * diff;
}
return Math.sqrt(sumSquaredDiff / values.length);
}
public static double distance(double x1, double y1, double x2, double y2) {
double dx = x2 - x1;
double dy = y2 - y1;
return Math.sqrt(dx * dx + dy * dy);
}
int[] grades = {4, 5, 3, 4, 5, 4};
System.out.println("Среднее: " + calculateAverage(grades)); // 4.166666666666667
System.out.println("36°C = " + celsiusToFahrenheit(36) + "°F"); // 96.8°F
double bill = 2500.0;
System.out.println("Чаевые: " + calculateTip(bill, 15)); // 375.0
double[] data = {2.0, 4.0, 4.0, 4.0, 5.0, 5.0, 7.0, 9.0};
System.out.println("Ст. откл.: " + standardDeviation(data)); // 2.0
System.out.println("Расстояние: " + distance(0, 0, 3, 4)); // 5.0
Комбинирование: параметры и return в реальных задачах
Давайте посмотрим, как параметры и return работают вместе в реальных, практических задачах. Эти примеры покажут, как методы могут обрабатывать входные данные и возвращать полезные результаты.
Задача 1: Калькулятор скидки
public static double calculateDiscountedPrice(
double originalPrice,
int discountPercent,
int loyaltyYears
) {
if (originalPrice <= 0) {
throw new IllegalArgumentException("Цена должна быть положительной");
}
if (discountPercent < 0 || discountPercent > 100) {
throw new IllegalArgumentException("Скидка должна быть от 0 до 100%");
}
double discount = discountPercent / 100.0;
if (loyaltyYears >= 5) {
discount = discount + 0.10;
} else if (loyaltyYears >= 3) {
discount = discount + 0.05;
}
if (discount > 0.50) {
discount = 0.50;
}
return originalPrice * (1 - discount);
}
double price1 = calculateDiscountedPrice(10000, 15, 2);
System.out.println("Цена: " + price1); // 8500.0
double price2 = calculateDiscountedPrice(10000, 15, 4);
System.out.println("Цена: " + price2); // 8000.0 (скидка 15% + 5% = 20%)
double price3 = calculateDiscountedPrice(10000, 40, 6);
System.out.println("Цена: " + price3); // 5000.0 (макс. скидка 50%)
Задача 2: Валидация пароля
public static boolean isStrongPassword(String password) {
if (password == null || password.length() < 8) {
return false;
}
boolean hasUpper = false;
boolean hasLower = false;
boolean hasDigit = false;
boolean hasSpecial = false;
for (int i = 0; i < password.length(); i++) {
char ch = password.charAt(i);
if (Character.isUpperCase(ch)) hasUpper = true;
else if (Character.isLowerCase(ch)) hasLower = true;
else if (Character.isDigit(ch)) hasDigit = true;
else hasSpecial = true;
}
return hasUpper && hasLower && hasDigit && hasSpecial;
}
System.out.println(isStrongPassword("Pass123!")); // true
System.out.println(isStrongPassword("pass123!")); // false (нет заглавной)
System.out.println(isStrongPassword("PASSWORD1!")); // false (нет строчной)
System.out.println(isStrongPassword("Passw0rd")); // false (нет спецсимвола)
System.out.println(isStrongPassword("Ab1!")); // false (меньше 8 символов)
Задача 3: Поиск в массиве
public static int findIndex(int[] array, int target) {
if (array == null) return -1;
for (int i = 0; i < array.length; i++) {
if (array[i] == target) {
return i;
}
}
return -1;
}
public static int[] findAllIndices(int[] array, int target) {
int count = 0;
for (int num : array) {
if (num == target) count++;
}
if (count == 0) return new int[0];
int[] indices = new int[count];
int index = 0;
for (int i = 0; i < array.length; i++) {
if (array[i] == target) {
indices[index] = i;
index++;
}
}
return indices;
}
int[] numbers = {10, 20, 30, 20, 40, 20};
int pos = findIndex(numbers, 20);
System.out.println("Первый индекс: " + pos); // 1
int[] allPos = findAllIndices(numbers, 20);
System.out.print("Все индексы: ");
for (int p : allPos) {
System.out.print(p + " ");
}
// Выведет: 1 3 5
Задача 4: Генератор отчётов
public static String generateReport(String productName, int sold, double price) {
if (productName == null || productName.isEmpty()) {
return "Ошибка: название товара не указано";
}
if (sold < 0) {
return "Ошибка: количество продаж не может быть отрицательным";
}
if (price < 0) {
return "Ошибка: цена не может быть отрицательной";
}
double revenue = sold * price;
double avgPrice = sold > 0 ? revenue / sold : 0;
StringBuilder report = new StringBuilder();
report.append("=== ОТЧЁТ ===\n");
report.append("Товар: ").append(productName).append("\n");
report.append("Продано: ").append(sold).append(" шт.\n");
report.append("Цена: ").append(String.format("%.2f", price)).append(" руб.\n");
report.append("Выручка: ").append(String.format("%.2f", revenue)).append(" руб.\n");
report.append("Средняя цена: ").append(String.format("%.2f", avgPrice)).append(" руб.\n");
report.append("=============");
return report.toString();
}
String report = generateReport("Ноутбук", 15, 54999.00);
System.out.println(report);
Задача 5: Конвертер единиц измерения
public static double convert(double value, String from, String to) {
if (from == null || to == null) {
throw new IllegalArgumentException("Единицы измерения не могут быть null");
}
from = from.toLowerCase();
to = to.toLowerCase();
double valueInMeters;
if (from.equals("мм")) {
valueInMeters = value / 1000.0;
} else if (from.equals("см")) {
valueInMeters = value / 100.0;
} else if (from.equals("м")) {
valueInMeters = value;
} else if (from.equals("км")) {
valueInMeters = value * 1000.0;
} else {
throw new IllegalArgumentException("Неизвестная единица: " + from);
}
if (to.equals("мм")) {
return valueInMeters * 1000.0;
} else if (to.equals("см")) {
return valueInMeters * 100.0;
} else if (to.equals("м")) {
return valueInMeters;
} else if (to.equals("км")) {
return valueInMeters / 1000.0;
} else {
throw new IllegalArgumentException("Неизвестная единица: " + to);
}
}
System.out.println(convert(1, "км", "м")); // 1000.0
System.out.println(convert(100, "см", "м")); // 1.0
System.out.println(convert(2.5, "км", "мм")); // 2500000.0
System.out.println(convert(500, "мм", "м")); // 0.5
Частые ошибки и как их избежать
При работе с параметрами и return начинающие программисты часто допускают одни и те же ошибки. Давайте разберём наиболее распространённые из них.
Ошибка 1: Забыли return
// ОШИБКА: метод объявлен как возвращающий int, но не все пути имеют return
public static int getMax(int a, int b) {
if (a > b) {
return a;
}
// Забыли else return b!
}
// ПРАВИЛЬНО:
public static int getMax(int a, int b) {
if (a > b) {
return a;
}
return b;
}
Комплятор Java требует, чтобы метод, возвращающий значение, имел return на каждом возможном пути выполнения. Если хотя бы один путь не завершается return, вы получите ошибку компиляции.
Ошибка 2: Путаница параметров по порядку
public static String formatName(String firstName, String lastName) {
return lastName + " " + firstName;
}
// ОШИБКА: перепутаны аргументы
String name = formatName("Петров", "Иван");
// Результат: "Иван Петров" — НЕПРАВИЛЬНО!
// Ожидалось: "Петров Иван"
// ПРАВИЛЬНО:
String name2 = formatName("Иван", "Петров");
// Результат: "Петров Иван"
Ошибка 3: Деление на ноль
// ОШИБКА: не проверили делитель
public static double divide(double a, double b) {
return a / b; // Если b == 0, будет Infinity (для double) или исключение (для int)
}
// ПРАВИЛЬНО:
public static double divide(double a, double b) {
if (b == 0) {
throw new IllegalArgumentException("Деление на ноль");
}
return a / b;
}
Ошибка 4: Изменение примитивного параметра и ожидание результата
// ОШИБКА: думаем, что метод изменит число
public static void doubleValue(int number) {
number = number * 2;
}
int x = 5;
doubleValue(x);
System.out.println(x); // 5, а не 10!
// ПРАВИЛЬНО: метод должен ВЕРНУТЬ результат
public static int doubleValue(int number) {
return number * 2;
}
int x2 = 5;
int result = doubleValue(x2);
System.out.println(result); // 10
Ошибка 5: Отсутствие проверки null
// ОШИБКА: не проверили null
public static int getLength(String text) {
return text.length(); // NullPointerException если text == null
}
// ПРАВИЛЬНО:
public static int getLength(String text) {
if (text == null) {
throw new IllegalArgumentException("Строка не может быть null");
}
return text.length();
}
// Или ещё проще:
import java.util.Objects;
public static int getLength(String text) {
Objects.requireNonNull(text, "Строка не может быть null");
return text.length();
}
Практика: решите задачи самостоятельно
Прежде чем перейти к итогам, попробуйте решить эти задачи самостоятельно. Затем сверьтесь с решениями.
Задача A: Определение возраста
Напишите метод getAgeCategory, который принимает возраст (int) и возвращает строку: «ребёнок» (0-12), «подросток» (13-17), «взрослый» (18-64), «пожилой» (65+). Для некорректного возраста возвращайте «ошибка».
public static String getAgeCategory(int age) {
if (age < 0 || age > 150) return "ошибка";
if (age <= 12) return "ребёнок";
if (age <= 17) return "подросток";
if (age <= 64) return "взрослый";
return "пожилой";
}
Задача B: Подсчёт букв
Напишите метод countChar, который принимает строку и символ, и возвращает количество вхождений этого символа в строку (без учёта регистра).
public static int countChar(String text, char target) {
if (text == null) return 0;
int count = 0;
String lowerText = text.toLowerCase();
char lowerTarget = Character.toLowerCase(target);
for (int i = 0; i < lowerText.length(); i++) {
if (lowerText.charAt(i) == lowerTarget) {
count++;
}
}
return count;
}
System.out.println(countChar("Привет Мир", 'р')); // 2
System.out.println(countChar("Hello World", 'l')); // 3
Задача C: Безопасное деление
Напишите метод safeDivide, который принимает два double и возвращает результат деления. Если делитель равен 0, метод должен вернуть 0 (не выбрасывать исключение).
public static double safeDivide(double a, double b) {
if (b == 0) return 0;
return a / b;
}
System.out.println(safeDivide(10, 3)); // 3.3333333333333335
System.out.println(safeDivide(10, 0)); // 0.0
Итоги урока
- ✅ Параметр — это переменная в объявлении метода, аргумент — это значение при вызове. Не путайте термины!
- ✅ Количество и типы аргументов при вызове должны точно совпадать с параметрами в объявлении метода
- ✅ Порядок параметров важен — аргументы привязываются позиционно
- ✅ Java всегда передаёт по значению: для примитивных типов — копия значения, для ссылочных — копия ссылки
- ✅ Метод не может изменить примитивную переменную, переданную ему в качестве аргумента
- ✅ Метод может изменить содержимое объекта, на который ссылается переданная ссылка
- ✅
returnвозвращает значение из метода и немедленно завершает его выполнение - ✅ Методы с
voidне возвращают значение, но могут использоватьreturnбез значения для раннего выхода - ✅ Ранний return (guard clause) упрощает код, убирая лишнюю вложенность
- ✅ Проверяйте аргументы в публичных методах с помощью
if + throwилиObjects.requireNonNull - ✅ Методы, возвращающие
boolean, используют префиксы is/has/can для ясности - ✅ Каждый путь выполнения метода, возвращающего значение, должен содержать
return
Тест по уроку
10 вопросов