Ниже представлена подробная девятая глава, ориентированная на новичков. В предыдущих главах мы узнали о базовых конструкциях языка, освоили методы, строки, массивы, коллекции, а также познакомились с объектно-ориентированным подходом. Теперь пришло время разобраться с тем, как программа может адекватно реагировать на ошибки и непредвиденные ситуации. Для этого в Java есть механизм исключений.
Программы редко выполняются в идеальных условиях. Пользователь может ввести некорректные данные, файл может не существовать, подключение к сети может оборваться. В таких случаях возникают ошибки или исключительные ситуации.
Вместо того, чтобы программа просто "падала" или вела себя непредсказуемо, в Java предусмотрен механизм обработки ошибок через исключения. Это позволяет:
Исключение — это объект, представляющий ошибочную или нестандартную ситуацию, возникшую во время выполнения программы. Когда возникает ошибка, Java "выбрасывает" исключение (throw exception).
Если вы не перехватите это исключение и не обработаете его, ваша программа завершится с сообщением об ошибке. Но если вы примените специальный код для обработки исключений, программа может продолжить работать или хотя бы красиво завершиться.
try
, catch
, finally
Основной механизм обработки исключений в Java — блоки try
и catch
:
try {
// код, в котором может произойти ошибка
} catch (ТипИсключения e) {
// код для обработки ошибки
} finally {
// код, который выполнится в любом случае (необязателен)
}
Представим, что у нас есть массив из 3 элементов, а мы пытаемся обратиться к индексу, которого нет.
int[] numbers = {1, 2, 3};
try {
System.out.println(numbers[5]); // здесь ошибка: индекс 5 не существует
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Ой! Обратились к несуществующему элементу массива.");
}
Без блока try-catch программа бы просто упала с ошибкой ArrayIndexOutOfBoundsException
. С обработкой мы увидим понятное сообщение:
Ой! Обратились к несуществующему элементу массива.
Вы можете перехватить разные типы исключений разными способами.
try {
// код
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Индекс за пределами массива!");
} catch (NullPointerException e) {
System.out.println("Обращение к пустой ссылке!");
}
Порядок catch-блоков важен: сначала более специфичные исключения, потом более общие.
finally
Пример использования finally:
Scanner scanner = null;
try {
scanner = new Scanner(new File("input.txt"));
// читаем файл
} catch (FileNotFoundException e) {
System.out.println("Файл не найден!");
} finally {
if (scanner != null) {
scanner.close(); // закрываем файл в любом случае
}
}
Даже если файл не найден и выброшено исключение, блок finally сработает, и там мы можем безопасно освободить ресурсы.
throw
Вы можете сами "выбрасывать" исключения, если хотите сигнализировать о проблеме. Например:
public static void setAge(int age) {
if (age < 0) {
throw new IllegalArgumentException("Возраст не может быть отрицательным!");
}
// дальше идёт нормальная логика
}
Если кто-то вызовет setAge(-5)
, будет выброшено исключение IllegalArgumentException
.
throws
Если ваш метод может вызвать исключение, но вы его не обрабатываете внутри метода (например, не хотите или не можете), вы можете "предупредить" о возможном исключении с помощью throws
.
public static void readFile(String filename) throws FileNotFoundException {
Scanner scanner = new Scanner(new File(filename));
// если файл не найден, выбрасывается FileNotFoundException
// но этот метод его не ловит, просто "передаёт вверх"
}
Когда вы вызываете readFile("data.txt")
, вызывающий код должен либо обработать это исключение, либо тоже объявить throws FileNotFoundException
.
Все исключения в Java — объекты, наследники класса Throwable
.
Две основные ветки:
Error
: Серъёзные ошибки, обычно не перехватываются (например, OutOfMemoryError).Exception
: Исключения, которые можно обрабатывать.Есть так называемые Checked Exception (проверяемые исключения), которые нужно либо обработать в try-catch, либо объявить через throws, и Unchecked Exception (непроверяемые), которые вызывать так не обязательно (например, RuntimeException и его подклассы).
Напишем программу, которая просит пользователя ввести число и выводит его квадрат. Если пользователь введёт не число, программа выведет сообщение об ошибке, а затем попросит ввести число заново.
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int number = 0;
while (true) {
System.out.print("Введите число: ");
try {
String input = scanner.nextLine();
number = Integer.parseInt(input); // может выбросить NumberFormatException
break; // если успешно преобразовали, выходим из цикла
} catch (NumberFormatException e) {
System.out.println("Это не число! Попробуйте ещё раз.");
}
}
System.out.println("Квадрат числа: " + (number * number));
}
}
Здесь Integer.parseInt()
выбрасывает NumberFormatException
, если строка не является числом. Мы ловим это исключение и просим пользователя попробовать ещё раз, вместо того чтобы программа упала.
В этой главе мы:
try
, catch
, finally
.throw
для выброса собственных исключений и throws
для передачи ответственности обработки исключений вызывающему коду.Теперь вы можете защищать свой код от непредсказуемых ситуаций и писать программы, способные достойно выйти из сложных ситуаций или хотя бы информировать пользователя о том, что пошло не так.
На этом мы завершаем девятую главу. В следующей главе мы поговорим о работе с файлами и вводом/выводом, чтобы научиться сохранять данные, читать их из файлов и обрабатывать информацию вне консоли.
Просмотров: 42