1. Винятки як частина API
Чому винятки — це частина «контракту» методу?
Коли ви пишете метод, ви визначаєте не лише параметри й повернене значення, а й які винятки він може викидати. Це частина «контракту» між вашим методом і тими, хто його використовуватиме. Якщо метод може викинути виняток, про це мають знати всі, хто його викликає, — щоб коректно обробити помилку (або принаймні не дивуватися, коли програма раптово завершиться з помилкою).
Приклад:
public void readFile(String filename) throws IOException {
// ... читання файлу
}
Тут явно зазначено, що метод може викинути IOException. Це сигнал для користувача методу: «Будьте готові обробити помилку читання файлу!»
Документування винятків: анотація @throws у Javadoc
Щоб інші розробники розуміли, які винятки може викидати ваш метод, використовуйте анотацію @throws (або @exception) у Javadoc.
Приклад:
/**
* Зчитує вміст файлу.
*
* @param filename імʼя файлу
* @return вміст файлу у вигляді рядка
* @throws IOException якщо сталася помилка читання файлу
*/
public String readFile(String filename) throws IOException {
// ...
}
Навіщо це потрібно?
- Допомагає іншим розробникам зрозуміти, які помилки потрібно обробляти.
- IDE та генератори документації (наприклад, Javadoc) показують ці винятки прямо у підказках.
- Підвищує надійність і передбачуваність коду.
Checked і unchecked‑винятки в API
Checked‑винятки (нащадки Exception, але не RuntimeException) — частина контракту методу. Їх потрібно або обробити, або явно оголосити у сигнатурі (throws).
Unchecked‑винятки (RuntimeException і нащадки) зазвичай сигналізують про програмні помилки (наприклад, NullPointerException, IllegalArgumentException). Їх не обов’язково вказувати у сигнатурі, але якщо ваш метод може викинути таку помилку (наприклад, через некоректні аргументи), її також варто описати у Javadoc.
Приклад:
/**
* Ділить a на b.
* @param a ділене
* @param b дільник
* @return результат ділення
* @throws IllegalArgumentException якщо b == 0
*/
public int divide(int a, int b) {
if (b == 0) throw new IllegalArgumentException("Дільник не може бути нулем");
return a / b;
}
Винятки та проєктування API
- Думайте про користувача вашого методу: які помилки він може й повинен обробити? Які — це баги, а які — «робочі» ситуації?
- Не зловживайте checked‑винятками: якщо помилка — це баг (наприклад, некоректний аргумент), краще викидати unchecked‑виняток.
- Документуйте всі винятки, які можуть передаватися «вгору».
2. Конструкція try‑with‑resources
Проблема: як безпечно закривати ресурси?
У багатьох завданнях доводиться працювати з ресурсами, які обов’язково потрібно закривати після використання: файли, мережеві з’єднання, бази даних тощо. Якщо забути закрити ресурс — можна отримати витік пам’яті, блокування файлу або інші неприємності.
Раніше:
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader("data.txt"));
String line = reader.readLine();
// ...
} catch (IOException e) {
// обробка помилки
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
// обробка помилки під час закриття
}
}
}
Коду багато: легко помилитися й забути закрити ресурс.
Рішення: try‑with‑resources
Починаючи з Java 7 з’явилася конструкція try‑with‑resources, яка автоматично закриває всі ресурси, навіть якщо у блоці виник виняток.
Синтаксис:
try (ResourceType resource = new ResourceType(...)) {
// робота з ресурсом
} catch (ExceptionType e) {
// обробка помилки
}
Приклад:
try (BufferedReader reader = new BufferedReader(new FileReader("data.txt"))) {
String line = reader.readLine();
System.out.println(line);
} catch (IOException e) {
System.out.println("Помилка під час читання файлу: " + e.getMessage());
}
// reader.close() буде викликано автоматично!
Як це працює?
- У дужках після try оголошуються ресурси, які потрібно закрити.
- Після виходу з блоку try (навіть якщо виник виняток!) для кожного ресурсу викликається метод close().
- Це працює лише для ресурсів, що реалізують інтерфейс AutoCloseable (або його нащадка Closeable).
Інтерфейс AutoCloseable:
public interface AutoCloseable {
void close() throws Exception;
}
Усі стандартні ресурси Java (файли, потоки, з’єднання з БД) реалізують цей інтерфейс.
Можна оголошувати кілька ресурсів
try (
BufferedReader reader = new BufferedReader(new FileReader("input.txt"));
BufferedWriter writer = new BufferedWriter(new FileWriter("output.txt"))
) {
String line;
while ((line = reader.readLine()) != null) {
writer.write(line);
writer.newLine();
}
}
Обидва ресурси будуть закриті автоматично, навіть якщо виникне виняток.
Переваги try‑with‑resources
- Безпека: ресурси завжди закриваються, навіть у разі помилок.
- Стислість: менше коду, менше шансів помилитися.
- Читабельність: одразу видно, які ресурси використовуються і коли вони закриваються.
3. Практика: пишемо безпечний код із try‑with‑resources
Приклад: читання файлу
public static void printFirstLine(String filename) {
try (BufferedReader reader = new BufferedReader(new FileReader(filename))) {
String line = reader.readLine();
System.out.println("Перший рядок: " + line);
} catch (IOException e) {
System.out.println("Помилка: " + e.getMessage());
}
}
Приклад: запис у файл
public static void writeToFile(String filename, String text) {
try (BufferedWriter writer = new BufferedWriter(new FileWriter(filename))) {
writer.write(text);
} catch (IOException e) {
System.out.println("Помилка під час запису: " + e.getMessage());
}
}
Приклад: власний ресурс
Якщо ви пишете свій клас, який потрібно закривати, просто реалізуйте AutoCloseable:
public class MyResource implements AutoCloseable {
@Override
public void close() {
System.out.println("Ресурс закрито!");
}
}
Тепер його можна використовувати у try‑with‑resources:
try (MyResource res = new MyResource()) {
// робота з ресурсом
}
4. Типові помилки та найкращі практики
Помилка № 1: забули закрити ресурс (без try‑with‑resources).
Якщо не використовувати try‑with‑resources, легко забути закрити файл або потік — це призводить до витоків ресурсів.
Помилка № 2: спроба використовувати try‑with‑resources з об’єктом, який не реалізує AutoCloseable.
Якщо ваш клас не реалізує цей інтерфейс, компілятор не дозволить використовувати його в try‑with‑resources.
Помилка № 3: ви не документуєте винятки в API.
Якщо ваш метод може викинути виняток — обов’язково вказуйте це у сигнатурі (throws) і у Javadoc (@throws). Це допоможе іншим правильно використовувати ваш код.
Помилка № 4: перехоплюєте Exception замість конкретних помилок.
Краще перехоплювати лише ті винятки, які справді очікуєте й умієте обробляти.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ