Раздел 9. Исключения, логирование, отладка
Глава 1. Иерархия исключений (Exceptions)
Практика: Защитное программирование в «Библиотеке»
Подготовка: обновление модели данных
К этому моменту в проекте «Библиотека» должны существовать:
Класс Book с полями id (String или UUID), title, author, year, genres
Класс Library с коллекцией книг и методами поиска
Функциональность импорта/экспорта
Добавьте поле publishedDate типа LocalDate в Book для задачи с парсингом дат.
Часть 1. Автоматическое управление ресурсами
Проблема: ручное закрытие ресурсов
Проблемы: многословно, finally выполняется всегда (даже если ресурс не создан), исключение при close может затереть основное исключение.
Решение: try-with-resources
Ключевые моменты:
Ресурсы в скобках после try должны реализовывать AutoCloseable
Закрываются в обратном порядке создания: buffered, затем reader
Если исключение в try и при закрытии — основное сохраняется, вторичное прицепляется через Suppressed
Можно объявлять ресурс вне try, если он effectively final
Несколько ресурсов
Часть 2. Собственное исключение
Зачем нужно своё исключение
Семантическая ясность: LibraryException говорит о проблеме в предметной области
Единая точка обработки в верхних слоях
Возможность добавить контекст (ID книги, имя файла)
Реализация: unchecked-исключение
Создайте класс LibraryException:
Почему unchecked:
Ошибки библиотеки обычно не восстановимы на месте
Не загромождает сигнатуры методов throws
В Stream API и лямбдах не требует обёртки (в отличие от checked)
Использование в проекте
#Java #для_новичков #beginner #exception #практика
Глава 1. Иерархия исключений (Exceptions)
Практика: Защитное программирование в «Библиотеке»
Подготовка: обновление модели данных
К этому моменту в проекте «Библиотека» должны существовать:
Класс Book с полями id (String или UUID), title, author, year, genres
Класс Library с коллекцией книг и методами поиска
Функциональность импорта/экспорта
Добавьте поле publishedDate типа LocalDate в Book для задачи с парсингом дат.
Часть 1. Автоматическое управление ресурсами
Проблема: ручное закрытие ресурсов
// Плохо: ресурс может остаться открытым при исключении
public List<Book> importFromJsonManual(String filename) {
FileReader reader = null;
try {
reader = new FileReader(filename);
// ... парсинг ...
return parseBooks(reader);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (reader != null) {
try {
reader.close(); // Может бросить исключение, подавляя основное
} catch (IOException e) {
// игнорируем
}
}
}
}
Проблемы: многословно, finally выполняется всегда (даже если ресурс не создан), исключение при close может затереть основное исключение.
Решение: try-with-resources
// Хорошо: автоматическое закрытие, правильная обработка исключений
public List<Book> importFromJson(String filename) {
try (FileReader reader = new FileReader(filename);
BufferedReader buffered = new BufferedReader(reader)) {
StringBuilder json = new StringBuilder();
String line;
while ((line = buffered.readLine()) != null) {
json.append(line);
}
return parseBooks(json.toString());
} catch (IOException e) {
throw new LibraryException("Не удалось прочитать файл: " + filename, e);
}
}
Ключевые моменты:
Ресурсы в скобках после try должны реализовывать AutoCloseable
Закрываются в обратном порядке создания: buffered, затем reader
Если исключение в try и при закрытии — основное сохраняется, вторичное прицепляется через Suppressed
Можно объявлять ресурс вне try, если он effectively final
Несколько ресурсов
public void exportToJson(String inputFilename, String outputFilename) {
try (BufferedReader reader = Files.newBufferedReader(Path.of(inputFilename));
BufferedWriter writer = Files.newBufferedWriter(Path.of(outputFilename))) {
String json = reader.readLine();
List<Book> books = parseBooks(json);
String formatted = formatBooks(books);
writer.write(formatted);
} catch (IOException e) {
throw new LibraryException("Ошибка при копировании данных", e);
}
}Часть 2. Собственное исключение
Зачем нужно своё исключение
Семантическая ясность: LibraryException говорит о проблеме в предметной области
Единая точка обработки в верхних слоях
Возможность добавить контекст (ID книги, имя файла)
Реализация: unchecked-исключение
Создайте класс LibraryException:
public class LibraryException extends RuntimeException {
public LibraryException(String message) {
super(message);
}
public LibraryException(String message, Throwable cause) {
super(message, cause);
}
public LibraryException(String message, String bookId, Throwable cause) {
super(message + " [книга: " + bookId + "]", cause);
}
}Почему unchecked:
Ошибки библиотеки обычно не восстановимы на месте
Не загромождает сигнатуры методов throws
В Stream API и лямбдах не требует обёртки (в отличие от checked)
Использование в проекте
public Book findBookOrThrow(String bookId) {
return findById(bookId)
.orElseThrow(() -> new LibraryException("Книга не найдена", bookId, null));
}#Java #для_новичков #beginner #exception #практика
👍6
Часть 3. Многоцелевой перехват
Сценарий: разные I/O-ошибки — одинаковая реакция
Правила multi-catch:
Типы должны быть несовместимыми (нет иерархии)
Переменная e effectively final внутри блока
Нельзя вызвать метод, который есть не во всех типах (без приведения)
Часть 4. Checked-исключения в Stream API
Проблема: Stream.map() не принимает throws
Уточнение: DateTimeParseException на самом деле unchecked (наследник DateTimeException). Возьмём более показательный пример с реальным checked-исключением — чтение из файла внутри stream:
Решение 1: обёртка внутри лямбды
Недостаток: загромождает код, повторяется в каждой лямбде.
Решение 2: вспомогательный метод-обёртка
Использование:
Решение 3: специализированный обёртчик для Optional
Часть 5. Защитные проверки аргументов
Objects.requireNonNull
#Java #для_новичков #beginner #exception #практика
Сценарий: разные I/O-ошибки — одинаковая реакция
public List<Book> loadFromMultipleSources(String jsonPath, String csvPath) {
List<Book> result = new ArrayList<>();
try {
result.addAll(loadJson(jsonPath));
} catch (FileNotFoundException | NoSuchFileException e) {
// Файла нет — не критично, продолжаем с CSV
System.err.println("JSON не найден, пробуем CSV: " + e.getMessage());
} catch (IOException e) {
throw new LibraryException("Ошибка чтения JSON", e);
}
try {
result.addAll(loadCsv(csvPath));
} catch (FileNotFoundException | NoSuchFileException e) {
System.err.println("CSV не найден: " + e.getMessage());
} catch (IOException e) {
throw new LibraryException("Ошибка чтения CSV", e);
}
if (result.isEmpty()) {
throw new LibraryException("Не удалось загрузить книги ни из одного источника");
}
return result;
}Правила multi-catch:
Типы должны быть несовместимыми (нет иерархии)
Переменная e effectively final внутри блока
Нельзя вызвать метод, который есть не во всех типах (без приведения)
Часть 4. Checked-исключения в Stream API
Проблема: Stream.map() не принимает throws
// Не компилируется: parseDate бросает checked DateTimeParseException
public List<LocalDate> parsePublicationDates(List<String> dateStrings) {
return dateStrings.stream()
.map(this::parseDate) // Ошибка: unreported exception
.collect(Collectors.toList());
}
private LocalDate parseDate(String s) throws DateTimeParseException {
return LocalDate.parse(s); // checked в Java 8, но...
}
Уточнение: DateTimeParseException на самом деле unchecked (наследник DateTimeException). Возьмём более показательный пример с реальным checked-исключением — чтение из файла внутри stream:
// Реальная проблема: Files.readString бросает IOException
public List<String> loadDescriptions(List<Path> paths) {
return paths.stream()
.map(Files::readString) // Ошибка: IOException не обработана
.collect(Collectors.toList());
}
Решение 1: обёртка внутри лямбды
public List<String> loadDescriptionsWrapped(List<Path> paths) {
return paths.stream()
.map(path -> {
try {
return Files.readString(path);
} catch (IOException e) {
throw new LibraryException("Не удалось прочитать: " + path, e);
}
})
.collect(Collectors.toList());
}Недостаток: загромождает код, повторяется в каждой лямбде.
Решение 2: вспомогательный метод-обёртка
// Утилитный метод для оборачивания checked в unchecked
@FunctionalInterface
public interface ThrowingFunction<T, R> {
R apply(T t) throws Exception;
}
public static <T, R> Function<T, R> wrap(ThrowingFunction<T, R> throwing) {
return t -> {
try {
return throwing.apply(t);
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new LibraryException("Ошибка в потоковой операции", e);
}
};
}
Использование:
public List<String> loadDescriptionsClean(List<Path> paths) {
return paths.stream()
.map(wrap(Files::readString))
.collect(Collectors.toList());
}Решение 3: специализированный обёртчик для Optional
public Optional<Book> parseBookFromJson(String json) {
return Optional.ofNullable(json)
.map(wrap(this::parseBook))
.orElseThrow(() -> new LibraryException("Пустой JSON"));
}
private Book parseBook(String json) throws JsonParseException {
// парсинг...
}Часть 5. Защитные проверки аргументов
Objects.requireNonNull
public void addBook(Book book) {
Objects.requireNonNull(book, "Книга не может быть null");
Objects.requireNonNull(book.getTitle(), "Название не может быть null");
if (book.getYear() < 1450 || book.getYear() > Year.now().getValue() + 1) {
throw new IllegalArgumentException("Недопустимый год: " + book.getYear());
}
books.add(book);
}#Java #для_новичков #beginner #exception #практика
👍5
Преимущества:
Стандартный метод — читается как утверждение
Сообщение об ошибке встроено в исключение
Компактнее ручной проверки if (x == null) throw ...
Вариант с кастомным сообщением
Часть 6. Optional и явные исключения
Поиск с гарантией результата
Альтернативы:
Цепочка с промежуточными проверками
Интеграция в проект «Библиотека»
Обновлённый класс Library с защитными приёмами
Практические задания
Задача 1: try-with-resources для базы данных
Предположим, что Library теперь использует Connection для JDBC. Обёрните операции в try-with-resources. Обработайте SQLException через LibraryException.
Задача 2: иерархия исключений
Расширьте LibraryException:
BookNotFoundException — для findById
DuplicateBookException — для addBook
ImportException — для операций импорта
Все наследуют LibraryException. Покажите multi-catch для разных типов импорта.
Задача 3: обёртка для Stream с результатом
Создайте метод parseDatesWithFallback:
Задача 4: защита от null во всём API
Проверьте все публичные методы Library. Добавьте Objects.requireNonNull где аргументы не должны быть null. Документируйте в Javadoc, какие методы принимают null (например, findById(null) → LibraryException vs orElse(null)).
#Java #для_новичков #beginner #exception #практика
Стандартный метод — читается как утверждение
Сообщение об ошибке встроено в исключение
Компактнее ручной проверки if (x == null) throw ...
Вариант с кастомным сообщением
public Book findById(String id) {
Objects.requireNonNull(id, () -> "ID книги не может быть null, доступные: " + listIds());
// ленивое вычисление сообщения — вызывается только при ошибке
return internalFind(id);
}Часть 6. Optional и явные исключения
Поиск с гарантией результата
public Book getBook(String bookId) {
return findById(bookId)
.orElseThrow(() -> new LibraryException("Книга не найдена", bookId, null));
}Альтернативы:
Цепочка с промежуточными проверками
public LocalDate getPublicationDate(String bookId) {
return findById(bookId)
.map(Book::getPublishedDate)
.filter(Objects::nonNull)
.orElseThrow(() -> new LibraryException("Дата неизвестна", bookId, null));
}Интеграция в проект «Библиотека»
Обновлённый класс Library с защитными приёмами
public class Library {
private final List<Book> books = new ArrayList<>();
public void addBook(Book book) {
Objects.requireNonNull(book, "Книга не может быть null");
Objects.requireNonNull(book.getId(), "ID книги не может быть null");
if (findById(book.getId()).isPresent()) {
throw new LibraryException("Книга с ID " + book.getId() + " уже существует");
}
books.add(book);
}
public Book getBook(String id) {
Objects.requireNonNull(id, "ID не может быть null");
return findById(id)
.orElseThrow(() -> new LibraryException("Книга не найдена", id, null));
}
public List<Book> importFromJson(String filename) {
Objects.requireNonNull(filename, "Имя файла не может быть null");
try (BufferedReader reader = Files.newBufferedReader(Path.of(filename))) {
String json = reader.lines().collect(Collectors.joining());
return parseBooks(json);
} catch (FileNotFoundException | NoSuchFileException e) {
throw new LibraryException("Файл не найден: " + filename, e);
} catch (IOException e) {
throw new LibraryException("Ошибка чтения файла: " + filename, e);
}
}
public List<Book> importFromMultipleSources(List<String> filenames) {
Objects.requireNonNull(filenames, "Список файлов не может быть null");
return filenames.stream()
.filter(Objects::nonNull)
.map(wrap(this::importFromJson))
.flatMap(List::stream)
.collect(Collectors.toList());
}
private Optional<Book> findById(String id) {
return books.stream()
.filter(b -> b.getId().equals(id))
.findFirst();
}
private List<Book> parseBooks(String json) {
// реализация парсинга
}
}Практические задания
Задача 1: try-with-resources для базы данных
Предположим, что Library теперь использует Connection для JDBC. Обёрните операции в try-with-resources. Обработайте SQLException через LibraryException.
Задача 2: иерархия исключений
Расширьте LibraryException:
BookNotFoundException — для findById
DuplicateBookException — для addBook
ImportException — для операций импорта
Все наследуют LibraryException. Покажите multi-catch для разных типов импорта.
Задача 3: обёртка для Stream с результатом
Создайте метод parseDatesWithFallback:
public List<LocalDate> parseDatesWithFallback(List<String> inputs) {
// Парсит даты, при ошибке возвращает LocalDate.MIN вместо исключения
// Используйте обёртку, возвращающую Optional<LocalDate>
}Задача 4: защита от null во всём API
Проверьте все публичные методы Library. Добавьте Objects.requireNonNull где аргументы не должны быть null. Документируйте в Javadoc, какие методы принимают null (например, findById(null) → LibraryException vs orElse(null)).
#Java #для_новичков #beginner #exception #практика
👍7
Что выведет код?
#Tasks
class Parent040526 {
static void print() {
System.out.println("Parent");
}
}
class Child040526 extends Parent040526 {
static void print() {
System.out.println("Child");
}
}
public class Task040526 {
public static void main(String[] args) {
Parent040526 obj1 = new Parent040526();
Parent040526 obj2 = new Child040526();
Child040526 obj3 = new Child040526();
obj1.print();
obj2.print();
obj3.print();
}
}#Tasks
👍3
Варианты ответа:
Anonymous Quiz
50%
Parent Parent Child
35%
Parent Child Child
5%
Parent Parent Parent
10%
Child Child Child
👍4
Новая статья на Хабр, на вашу оценку - https://habr.com/ru/articles/1031336/
Пишите мнение и комменты))
Ну лайков там и кармы накидайте чтоли)))💃
Пишите мнение и комменты))
Ну лайков там и кармы накидайте чтоли)))
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥9👍1
Что такое Math класс? Какие полезные методы есть? 🤓
Ответ:
java.lang.Math — это финальный класс с статическими методами для математических операций.
Полезные методы:
abs() (модуль), min(), max() (минимум/максимум), round(), ceil(), floor() (округление), random() (псевдослучайное число от 0 до 1), sqrt(), pow(), sin(), cos(), tan(), log(), exp().
Также содержит константы PI и E. Для криптостойких случайных чисел используют java.security.SecureRandom.
#собеседование
Ответ:
Полезные методы:
abs() (модуль), min(), max() (минимум/максимум), round(), ceil(), floor() (округление), random() (псевдослучайное число от 0 до 1), sqrt(), pow(), sin(), cos(), tan(), log(), exp().
Также содержит константы PI и E. Для криптостойких случайных чисел используют java.security.SecureRandom.
#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6
История технологии сегодня — 05 мая
ℹ️ Кто родился в этот день
Бори́с Льво́вич Ро́зинг (23 апреля [5 мая] 1869, Санкт-Петербург, Российская империя — 20 апреля 1933, Архангельск, СССР) — русский физик, учёный, педагог, изобретатель телевидения, автор первых опытов по телевидению, за которые Русское техническое общество в 1912 г. присудило ему золотую медаль и премию имени К. Г. Сименса. Создал более 120 схем и систем телевизионных устройств.
Артур Леонард Ша́влов (англ. Arthur Leonard Schawlow; 5 мая 1921 — 28 апреля 1999) — американский физик, лауреат Нобелевской премии по физике (1981). Предложил ряд методов лазерной спектроскопии сверхвысокого разрешения, в частности внутридоплеровскую двухфотонную оптогальваническую спектроскопию (1979 г.), поляризационно-интермодуляционный метод (1981 г.) и другие.
Карл Ге́нрих Маркс (нем. Karl Heinrich Marx; 5 мая 1818, Трир, Рейнская провинция, Пруссия — 14 марта 1883, Лондон, Англия, Великобритания) — немецкий философ, социолог, экономист, писатель, поэт, политический журналист, общественный деятель, историк. Наиболее известными его трудами являются «Манифест Коммунистической партии» (1848 год в соавторстве с Фридрихом Энгельсом) и «Капитал. Критика политической экономии» (1867—1883). Политическая и философская мысль Маркса оказала огромное влияние на последующую интеллектуальную, экономическую и политическую историю.
🌐 Знаковые события
1927 — на Волховской ГЭС прошёл пуск первого советского генератора.
1951 — в Великобритании представлен игровой компьютер «Nimrod».
2025 — конец работы Skype.
#Biography #Birth_Date #Events #05мая
Бори́с Льво́вич Ро́зинг (23 апреля [5 мая] 1869, Санкт-Петербург, Российская империя — 20 апреля 1933, Архангельск, СССР) — русский физик, учёный, педагог, изобретатель телевидения, автор первых опытов по телевидению, за которые Русское техническое общество в 1912 г. присудило ему золотую медаль и премию имени К. Г. Сименса. Создал более 120 схем и систем телевизионных устройств.
Артур Леонард Ша́влов (англ. Arthur Leonard Schawlow; 5 мая 1921 — 28 апреля 1999) — американский физик, лауреат Нобелевской премии по физике (1981). Предложил ряд методов лазерной спектроскопии сверхвысокого разрешения, в частности внутридоплеровскую двухфотонную оптогальваническую спектроскопию (1979 г.), поляризационно-интермодуляционный метод (1981 г.) и другие.
Карл Ге́нрих Маркс (нем. Karl Heinrich Marx; 5 мая 1818, Трир, Рейнская провинция, Пруссия — 14 марта 1883, Лондон, Англия, Великобритания) — немецкий философ, социолог, экономист, писатель, поэт, политический журналист, общественный деятель, историк. Наиболее известными его трудами являются «Манифест Коммунистической партии» (1848 год в соавторстве с Фридрихом Энгельсом) и «Капитал. Критика политической экономии» (1867—1883). Политическая и философская мысль Маркса оказала огромное влияние на последующую интеллектуальную, экономическую и политическую историю.
1927 — на Волховской ГЭС прошёл пуск первого советского генератора.
1951 — в Великобритании представлен игровой компьютер «Nimrod».
2025 — конец работы Skype.
#Biography #Birth_Date #Events #05мая
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
[Совет по Java #033]
Тема: Collections.emptyList() и List.of() возвращают иммутабельные списки.
Проблема: Методы Collections.emptyList(), Collections.singletonList(), а также фабричные методы List.of(), Set.of(), Map.of() (Java 9+) возвращают полностью неизменяемые (immutable) коллекции.
Они не поддерживают никакие модификации: ни изменение размера (add, remove, clear), ни замену элементов (set). Разработчики, привыкшие к изменяемым коллекциям, часто пытаются добавлять элементы в такие списки, получая UnsupportedOperationException во время выполнения.
Решение: Используйте иммутабельные коллекции только для read-only операций. Если нужна возможность модификации, всегда создавайте изменяемую коллекцию: new ArrayList<>() для пустого списка, new ArrayList<>(List.of(...)) или new ArrayList<>(Arrays.asList(...)) для предзаполненного. Для защиты API возвращайте иммутабельные коллекции, чтобы гарантировать, что вызывающий код не изменит внутреннее состояние объекта.
Объяснение: Иммутабельные коллекции (не путать с неизменяемыми обертками Collections.unmodifiableList()) спроектированы для максимальной производительности и безопасности памяти.
List.of() возвращает специализированные компактные реализации: для 0-2 элементов используются поля без массива, для большего количества — массив, но всегда без поддержки модификаций.
Все методы, изменяющие коллекцию, наследуются от AbstractList (в случае emptyList) или явно переопределены на выбрасывание исключения.
#Java #советы
Тема: Collections.emptyList() и List.of() возвращают иммутабельные списки.
Проблема: Методы Collections.emptyList(), Collections.singletonList(), а также фабричные методы List.of(), Set.of(), Map.of() (Java 9+) возвращают полностью неизменяемые (immutable) коллекции.
Они не поддерживают никакие модификации: ни изменение размера (add, remove, clear), ни замену элементов (set). Разработчики, привыкшие к изменяемым коллекциям, часто пытаются добавлять элементы в такие списки, получая UnsupportedOperationException во время выполнения.
Решение: Используйте иммутабельные коллекции только для read-only операций. Если нужна возможность модификации, всегда создавайте изменяемую коллекцию: new ArrayList<>() для пустого списка, new ArrayList<>(List.of(...)) или new ArrayList<>(Arrays.asList(...)) для предзаполненного. Для защиты API возвращайте иммутабельные коллекции, чтобы гарантировать, что вызывающий код не изменит внутреннее состояние объекта.
Объяснение: Иммутабельные коллекции (не путать с неизменяемыми обертками Collections.unmodifiableList()) спроектированы для максимальной производительности и безопасности памяти.
List.of() возвращает специализированные компактные реализации: для 0-2 элементов используются поля без массива, для большего количества — массив, но всегда без поддержки модификаций.
Все методы, изменяющие коллекцию, наследуются от AbstractList (в случае emptyList) или явно переопределены на выбрасывание исключения.
#Java #советы
👍7
import java.util.*;
public class ImmutableCollections {
public static void main(String[] args) {
//Антипаттерн: попытка модифицировать иммутабельные коллекции
// Collections.emptyList()
List<String> empty = Collections.emptyList();
try {
empty.add("item"); // UnsupportedOperationException
} catch (UnsupportedOperationException e) {
System.out.println("Cannot add to emptyList: " + e);
}
// List.of() (Java 9+)
List<String> of = List.of("A", "B", "C");
try {
of.add("D"); // UnsupportedOperationException
} catch (UnsupportedOperationException e) {
System.out.println("Cannot add to List.of: " + e);
}
try {
of.set(0, "Z"); // Тоже исключение! Даже замена элемента запрещена
} catch (UnsupportedOperationException e) {
System.out.println("Cannot set in List.of: " + e);
}
// Collections.singletonList()
List<String> singleton = Collections.singletonList("only");
try {
singleton.add("second"); // UnsupportedOperationException
} catch (UnsupportedOperationException e) {
System.out.println("Cannot add to singletonList: " + e);
}
//Правильное использование: read-only операции
List<String> readOnly = List.of("Red", "Green", "Blue");
for (String color : readOnly) {
System.out.print(color + " ");
}
System.out.println();
String first = readOnly.get(0);
boolean contains = readOnly.contains("Green");
//Решение 1: создание изменяемой копии
List<String> mutable = new ArrayList<>(readOnly);
mutable.add("Yellow");
mutable.set(0, "Cyan");
System.out.println("Mutable copy: " + mutable);
//Решение 2: создание изменяемой пустой коллекции
List<String> mutableEmpty = new ArrayList<>();
mutableEmpty.add("item"); // Работает
//Решение 3: защитное копирование в API
public static List<String> getInternalList() {
// Возвращаем иммутабельный список, чтобы защитить внутреннее состояние
return List.of("internal1", "internal2");
}
// Получатель, который хочет модифицировать:
List<String> fromApi = getInternalList();
List<String> myWorkingCopy = new ArrayList<>(fromApi);
myWorkingCopy.add("myItem");
// Различия между видами иммутабельных списков
List<String> oldStyleEmpty = Collections.emptyList(); // Пустой, иммутабельный
List<String> newStyleEmpty = List.of(); // То же самое, но короче
List<String> oldStyleSingle = Collections.singletonList("one");
List<String> newStyleSingle = List.of("one");
// List.of() не допускает null
try {
List.of("a", null, "b"); // NullPointerException
} catch (NullPointerException e) {
System.out.println("List.of() rejects nulls");
}
// Collections.emptyList() допускает null? Нет, при добавлении, но сам список может содержать null?
// Сам список пуст, но методы типа contains(null) работают без исключения
System.out.println("emptyList contains null: " + Collections.emptyList().contains(null)); // false
}
}
Объяснение: Иммутабельные коллекции (не путать с неизменяемыми обертками Collections.unmodifiableList()) спроектированы для максимальной производительности и безопасности памяти.
List.of() возвращает специализированные компактные реализации: для 0-2 элементов используются поля без массива, для большего количества — массив, но всегда без поддержки модификаций.
Все методы, изменяющие коллекцию, наследуются от AbstractList (в случае emptyList) или явно переопределены на выбрасывание исключения.
#Java #советы
👍8
Что выведет код?
#Tasks
import java.util.*;
public class Task050526 {
public static void main(String[] args) {
List<String> list1 = Collections.emptyList();
List<String> list2 = List.of();
List<String> list3 = new ArrayList<>(List.of("a", "b"));
List<String> list4 = list3.subList(0, 2);
list1.add("x");
list2.add("x");
list3.add("x");
list4.add("x");
System.out.println("Done");
}
}
#Tasks
👍4
Что такое Random класс? 🤓
Ответ:
java.util.Random — это класс для генерации псевдослучайных чисел с более гибкими возможностями, чем Math.random().
Позволяет создавать экземпляры с заданным seed'ом (при одинаковом seed последовательность будет одинаковой).
Основные методы: nextInt() (любое целое), nextInt(int bound) (от 0 до bound-1), nextLong(), nextDouble(), nextBoolean(), nextBytes().
Для многопоточных сценариев есть потокобезопасный ThreadLocalRandom.
#собеседование
Ответ:
Позволяет создавать экземпляры с заданным seed'ом (при одинаковом seed последовательность будет одинаковой).
Основные методы: nextInt() (любое целое), nextInt(int bound) (от 0 до bound-1), nextLong(), nextDouble(), nextBoolean(), nextBytes().
Для многопоточных сценариев есть потокобезопасный ThreadLocalRandom.
#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7
История технологии сегодня — 06 мая
ℹ️ Кто родился в этот день
Рольф Максимилиан Зиверт ( швед. [ˈrɔlf maksɪˈmǐːlɪan ˈsǐːvɛʈ] ; 6 мая 1896 — 3 октября 1966) — шведский медицинский физик, основной вклад которого был в изучение биологических эффектов ионизирующего излучения. Зиверт (Zv), единица СИ, представляющая собой стохастический риск для здоровья, связанный с ионизирующим излучением, названа в его честь. Его называют «отцом радиационной защиты».
Андре Вейль ( / v eɪ / ;французский: [ɑ̃dʁe vɛj] ; 6 мая 1906 — 6 августа 1998) — французский математик, известный своими основополагающими работами в области теории чисел и алгебраической геометрии. Он был одним из самых влиятельных математиков двадцатого века. Его влияние обусловлено как его оригинальным вкладом в удивительно широкий спектр математических теорий, так и следом, который он оставил в математической практике и стиле, как через некоторые из своих собственных работ, так и через группу Бурбаки, одним из главных основателей которой он был.
🌐 Знаковые события
1949 – EDSAC, первый практический электронный цифровой компьютер с хранимой программой, совершает свою первую операцию.
1998 – Стив Джобс из Apple Inc. представляет первый iMac.
2002 – Основание SpaceX .
#Biography #Birth_Date #Events #06мая
Рольф Максимилиан Зиверт ( швед. [ˈrɔlf maksɪˈmǐːlɪan ˈsǐːvɛʈ] ; 6 мая 1896 — 3 октября 1966) — шведский медицинский физик, основной вклад которого был в изучение биологических эффектов ионизирующего излучения. Зиверт (Zv), единица СИ, представляющая собой стохастический риск для здоровья, связанный с ионизирующим излучением, названа в его честь. Его называют «отцом радиационной защиты».
Андре Вейль ( / v eɪ / ;французский: [ɑ̃dʁe vɛj] ; 6 мая 1906 — 6 августа 1998) — французский математик, известный своими основополагающими работами в области теории чисел и алгебраической геометрии. Он был одним из самых влиятельных математиков двадцатого века. Его влияние обусловлено как его оригинальным вкладом в удивительно широкий спектр математических теорий, так и следом, который он оставил в математической практике и стиле, как через некоторые из своих собственных работ, так и через группу Бурбаки, одним из главных основателей которой он был.
1949 – EDSAC, первый практический электронный цифровой компьютер с хранимой программой, совершает свою первую операцию.
1998 – Стив Джобс из Apple Inc. представляет первый iMac.
2002 – Основание SpaceX .
#Biography #Birth_Date #Events #06мая
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
Раздел 9. Исключения, логирование, отладка
Глава 2. Логирование (Logging)
Почему System.out.println — антипаттерн для production-приложений
System.out.println — это комбинация трех элементов стандартной библиотеки Java: статического поля out класса java.lang.System, экземпляра java.io.PrintStream, и метода println этого экземпляра. Разберем каждый компонент.
Класс System содержит три статических поля для стандартных потоков ввода-вывода: in типа InputStream, out и err типа PrintStream. Поле out инициализируется JVM при старте виртуальной машины и представляет собой поток вывода, обычно связанный с консолью терминала или процесса-родителя. Поле err аналогично, но предназначено для вывода ошибок и обычно направлено в тот же физический поток, что и out, хотя может быть перенаправлено операционной системой отдельно.
PrintStream — это класс-обертка над байтовым потоком OutputStream, добавляющий удобные методы для вывода примитивных типов, строк и объектов. Ключевая особенность: PrintStream никогда не выбрасывает IOException. Вместо этого он устанавливает внутренний флаг ошибки trouble, который можно проверить методом checkError(). Это магическое подавление исключений делает System.out.println непригодным для сценариев, где критична надежность доставки сообщения — например, при записи в файл логов, где переполнение диска должно быть явно обнаружено и обработано.
Как работает под капотом
Инициализация потока
При старте JVM вызывается нативный метод initializeSystemClass(), который создает файловые дескрипторы для стандартных потоков через java.io.FileDescriptor. Для System.out создается FileOutputStream, обернутый в BufferedOutputStream, затем в PrintStream. Эта цепочка оберток обеспечивает буферизацию вывода и удобный API.
Параметр autoFlush = true означает, что PrintStream будет принудительно сбрасывать буфер после каждого вызова println, printf или format, а также при записи массива байт. Это гарантирует немедленный вывод, но создает значительные накладные расходы на синхронизацию и системные вызовы.
Механика println
Метод println(String x) выполняет следующую последовательность:
Блокировка потока через synchronized(this) — PrintStream синхронизирован, что обеспечивает потокобезопасность, но создает contention при высокой конкуренции.
Вызов print(x), который преобразует строку в байты с использованием кодировки платформы по умолчанию (обычно UTF-8).
Запись байт в underlying BufferedOutputStream.
Вызов newLine() для записи разделителя строки, зависящего от платформы (\n для Unix, \r\n для Windows).
Если autoFlush включен, принудительный сброс буфера через flush().
Каждый вызов println порождает минимум один системный вызов write на уровне ОС после сброса буфера. При тысячах запросов в секунду это становится узким местом.
#Java #для_новичков #beginner #logging #System_out_println
Глава 2. Логирование (Logging)
Почему System.out.println — антипаттерн для production-приложений
System.out.println — это комбинация трех элементов стандартной библиотеки Java: статического поля out класса java.lang.System, экземпляра java.io.PrintStream, и метода println этого экземпляра. Разберем каждый компонент.
Класс System содержит три статических поля для стандартных потоков ввода-вывода: in типа InputStream, out и err типа PrintStream. Поле out инициализируется JVM при старте виртуальной машины и представляет собой поток вывода, обычно связанный с консолью терминала или процесса-родителя. Поле err аналогично, но предназначено для вывода ошибок и обычно направлено в тот же физический поток, что и out, хотя может быть перенаправлено операционной системой отдельно.
PrintStream — это класс-обертка над байтовым потоком OutputStream, добавляющий удобные методы для вывода примитивных типов, строк и объектов. Ключевая особенность: PrintStream никогда не выбрасывает IOException. Вместо этого он устанавливает внутренний флаг ошибки trouble, который можно проверить методом checkError(). Это магическое подавление исключений делает System.out.println непригодным для сценариев, где критична надежность доставки сообщения — например, при записи в файл логов, где переполнение диска должно быть явно обнаружено и обработано.
Как работает под капотом
Инициализация потока
При старте JVM вызывается нативный метод initializeSystemClass(), который создает файловые дескрипторы для стандартных потоков через java.io.FileDescriptor. Для System.out создается FileOutputStream, обернутый в BufferedOutputStream, затем в PrintStream. Эта цепочка оберток обеспечивает буферизацию вывода и удобный API.
// Упрощенный псевдокод инициализации из OpenJDK
FileOutputStream fdOut = new FileOutputStream(FileDescriptor.out);
BufferedOutputStream bufOut = new BufferedOutputStream(fdOut, 128);
System.out = new PrintStream(bufOut, true, charset); // autoFlush = true
Параметр autoFlush = true означает, что PrintStream будет принудительно сбрасывать буфер после каждого вызова println, printf или format, а также при записи массива байт. Это гарантирует немедленный вывод, но создает значительные накладные расходы на синхронизацию и системные вызовы.
Механика println
Метод println(String x) выполняет следующую последовательность:
Блокировка потока через synchronized(this) — PrintStream синхронизирован, что обеспечивает потокобезопасность, но создает contention при высокой конкуренции.
Вызов print(x), который преобразует строку в байты с использованием кодировки платформы по умолчанию (обычно UTF-8).
Запись байт в underlying BufferedOutputStream.
Вызов newLine() для записи разделителя строки, зависящего от платформы (\n для Unix, \r\n для Windows).
Если autoFlush включен, принудительный сброс буфера через flush().
Каждый вызов println порождает минимум один системный вызов write на уровне ОС после сброса буфера. При тысячах запросов в секунду это становится узким местом.
#Java #для_новичков #beginner #logging #System_out_println
👍2🔥2
Кодировка и платформенная зависимость
PrintStream использует кодировку, определенную при создании. Для System.out JVM выбирает кодировку на основе системных свойств, переменных окружения или параметров запуска. Это создает платформенную зависимость: один и тот же символ может быть закодирован по-разному на Windows и Linux, что приводит к искажению логов при агрегации из разных источников. Профессиональные логгеры явно указывают UTF-8 и гарантируют консистентность кодировки независимо от платформы.
Проблема отсутствия уровней логирования
System.out.println выводит всё в одном потоке без разделения по важности. В production-системе разработчикам нужно различать рутинную информацию, детальную отладку, предупреждения и критические ошибки. Без уровней невозможно отфильтровать шум: при включении отладочного вывода консоль заполняется мегабайтами данных, среди которых теряются реальные проблемы.
Профессиональные фреймворки логирования предоставляют иерархию уровней — TRACE, DEBUG, INFO, WARN, ERROR — каждый из которых включает себя и все последующие. Это позволяет в development выводить DEBUG, в staging — INFO, а в production — только WARN и выше, без модификации кода. Конфигурация уровней выполняется внешним файлом, что позволяет оперативно изменять детализацию без перекомпиляции или перезапуска приложения.
С System.out.println единственный способ "отключить" отладку — удалить или закомментировать строки. Это нарушает принцип единственной ответственности: код смешивает бизнес-логику и управление выводом. При необходимости временно включить отладку для диагностики проблемы в production требуется redeploy, что недопустимо для высокодоступных систем.
Невозможность отключения вывода и накладные расходы
Каждый вызов System.out.println неизбежно выполняется. Даже если вывод перенаправлен в /dev/null, форматирование строки происходит в памяти JVM. Конкатенация строк через оператор + создает промежуточные объекты StringBuilder и String, нагружая garbage collector.
При отключенном логировании профессиональные фреймворки полностью исключают накладные расходы. SLF4J использует guard-метод logger.isDebugEnabled(), а параметризованные сообщения logger.debug("User {} not found", userId) выполняют подстановку только при активном уровне. Это критично для горячих путей выполнения, где лишние аллокации приводят к stop-the-world паузам сборщика мусора.
Отсутствие структурированного вывода
Вывод System.out.println — плоский текст без метаданных. Production-системы требуют временной метки с миллисекундной точностью, имени потока, имени класса и метода, номера строки, уровня серьезности. Ручное добавление этих полей к каждому вызову создает неподдерживаемый код, где бизнес-логика утопает в шаблонном форматировании.
Без структуры логи невозможно эффективно анализировать инструментами агрегации. ELK (Elasticsearch, Logstash, Kibana), Splunk, Grafana Loki ожидают машиночитаемый формат, обычно JSON, с предсказуемыми полями. Плоский текст требует сложного парсинга регулярными выражениями, что медленно, ненадежно и создает технический долг при изменении формата.
#Java #для_новичков #beginner #logging #System_out_println
PrintStream использует кодировку, определенную при создании. Для System.out JVM выбирает кодировку на основе системных свойств, переменных окружения или параметров запуска. Это создает платформенную зависимость: один и тот же символ может быть закодирован по-разному на Windows и Linux, что приводит к искажению логов при агрегации из разных источников. Профессиональные логгеры явно указывают UTF-8 и гарантируют консистентность кодировки независимо от платформы.
Проблема отсутствия уровней логирования
System.out.println выводит всё в одном потоке без разделения по важности. В production-системе разработчикам нужно различать рутинную информацию, детальную отладку, предупреждения и критические ошибки. Без уровней невозможно отфильтровать шум: при включении отладочного вывода консоль заполняется мегабайтами данных, среди которых теряются реальные проблемы.
Профессиональные фреймворки логирования предоставляют иерархию уровней — TRACE, DEBUG, INFO, WARN, ERROR — каждый из которых включает себя и все последующие. Это позволяет в development выводить DEBUG, в staging — INFO, а в production — только WARN и выше, без модификации кода. Конфигурация уровней выполняется внешним файлом, что позволяет оперативно изменять детализацию без перекомпиляции или перезапуска приложения.
С System.out.println единственный способ "отключить" отладку — удалить или закомментировать строки. Это нарушает принцип единственной ответственности: код смешивает бизнес-логику и управление выводом. При необходимости временно включить отладку для диагностики проблемы в production требуется redeploy, что недопустимо для высокодоступных систем.
Невозможность отключения вывода и накладные расходы
Каждый вызов System.out.println неизбежно выполняется. Даже если вывод перенаправлен в /dev/null, форматирование строки происходит в памяти JVM. Конкатенация строк через оператор + создает промежуточные объекты StringBuilder и String, нагружая garbage collector.
// Эта строка форматируется всегда, даже если вывод не нужен
System.out.println("User " + userId + " performed " + operation + " at " + Instant.now());
При отключенном логировании профессиональные фреймворки полностью исключают накладные расходы. SLF4J использует guard-метод logger.isDebugEnabled(), а параметризованные сообщения logger.debug("User {} not found", userId) выполняют подстановку только при активном уровне. Это критично для горячих путей выполнения, где лишние аллокации приводят к stop-the-world паузам сборщика мусора.
Отсутствие структурированного вывода
Вывод System.out.println — плоский текст без метаданных. Production-системы требуют временной метки с миллисекундной точностью, имени потока, имени класса и метода, номера строки, уровня серьезности. Ручное добавление этих полей к каждому вызову создает неподдерживаемый код, где бизнес-логика утопает в шаблонном форматировании.
// Антипаттерн: ручное форматирование метаданных
System.out.println("[" + Thread.currentThread().getName() + "] [" +
LocalDateTime.now() + "] [INFO] [" + getClass().getName() +
"] User " + userId + " not found");
Без структуры логи невозможно эффективно анализировать инструментами агрегации. ELK (Elasticsearch, Logstash, Kibana), Splunk, Grafana Loki ожидают машиночитаемый формат, обычно JSON, с предсказуемыми полями. Плоский текст требует сложного парсинга регулярными выражениями, что медленно, ненадежно и создает технический долг при изменении формата.
#Java #для_новичков #beginner #logging #System_out_println
👍3🔥1
Проблемы с производительностью
System.out направлен в PrintStream, который синхронизирован на уровне экземпляра. Каждый вызов println захватывает монитор объекта, блокируя конкурентные потоки. При высокой нагурке это создает серьезный contention: десятки потоков ожидают освобождения монитора для записи одного сообщения.
Профессиональные логгеры используют асинхронные appenders, которые декопируют поток бизнес-логики от потока записи на диск. Сообщение помещается в lock-free очередь (RingBuffer в Log4j2, LinkedBlockingQueue в Logback), и поток немедленно продолжает выполнение. Отдельный поток-потребитель асинхронно сбрасывает очередь на диск. Это устраняет блокировки и повышает пропускную способность на порядки.
Кроме того, PrintStream с autoFlush = true выполняет системный вызов write после каждого сообщения. Системные вызовы дороги: они требуют переключения контекста из userspace в kernelspace, инвалидации кэшей TLB и ожидания завершения I/O. Буферизованные appenders логгеров накапливают данные и сбрасывают их пачками, минимизируя количество системных вызовов.
Нет маршрутизации и политик хранения
System.out направлен единственным потоком. Невозможно разделить потоки: ошибки в один файл для оперативного мониторинга, бизнес-события в другой для аудита, отладка во временный буфер с ограниченным размером. Нет ротации файлов по размеру или времени — логи растут бесконечно, заполняя диск и приводя к отказу системы.
Профессиональные логгеры предоставляют RollingFileAppender с политиками TimeBasedRollingPolicy и SizeBasedTriggeringPolicy. Файлы автоматически архивируются, сжимаются и удаляются по достижении возраста или общего объема. Это критично для долгоживущих production-систем, где логи за год могут занимать терабайты.
Нет возможности динамической маршрутизации на основе содержимого сообщения. Нельзя направить логи аутентификации в security-мониторинг, а логи производительности в APM-систему. Маркеры (Markers) в SLF4J и фильтры в Logback/Log4j2 решают эту задачу декларативно.
Нет контекста и корреляции
В распределенных системах критична возможность отследить запрос через все компоненты — от API-шлюза до базы данных. Это требует привязки контекстной информации к каждому лог-сообщению: request ID, user ID, session ID, trace ID в распределенной трассировке.
System.out.println не предоставляет механизма для автоматической вставки контекста. Ручное добавление к каждому вызову невозможно в многопоточной среде, где контекст различается между потоками. MDC (Mapped Diagnostic Context) в SLF4J использует ThreadLocal для неявной привязки контекста к потоку выполнения, что позволяет декларативно включать поля контекста в каждое сообщение через шаблон конфигурации.
#Java #для_новичков #beginner #logging #System_out_println
System.out направлен в PrintStream, который синхронизирован на уровне экземпляра. Каждый вызов println захватывает монитор объекта, блокируя конкурентные потоки. При высокой нагурке это создает серьезный contention: десятки потоков ожидают освобождения монитора для записи одного сообщения.
Профессиональные логгеры используют асинхронные appenders, которые декопируют поток бизнес-логики от потока записи на диск. Сообщение помещается в lock-free очередь (RingBuffer в Log4j2, LinkedBlockingQueue в Logback), и поток немедленно продолжает выполнение. Отдельный поток-потребитель асинхронно сбрасывает очередь на диск. Это устраняет блокировки и повышает пропускную способность на порядки.
Кроме того, PrintStream с autoFlush = true выполняет системный вызов write после каждого сообщения. Системные вызовы дороги: они требуют переключения контекста из userspace в kernelspace, инвалидации кэшей TLB и ожидания завершения I/O. Буферизованные appenders логгеров накапливают данные и сбрасывают их пачками, минимизируя количество системных вызовов.
Нет маршрутизации и политик хранения
System.out направлен единственным потоком. Невозможно разделить потоки: ошибки в один файл для оперативного мониторинга, бизнес-события в другой для аудита, отладка во временный буфер с ограниченным размером. Нет ротации файлов по размеру или времени — логи растут бесконечно, заполняя диск и приводя к отказу системы.
Профессиональные логгеры предоставляют RollingFileAppender с политиками TimeBasedRollingPolicy и SizeBasedTriggeringPolicy. Файлы автоматически архивируются, сжимаются и удаляются по достижении возраста или общего объема. Это критично для долгоживущих production-систем, где логи за год могут занимать терабайты.
Нет возможности динамической маршрутизации на основе содержимого сообщения. Нельзя направить логи аутентификации в security-мониторинг, а логи производительности в APM-систему. Маркеры (Markers) в SLF4J и фильтры в Logback/Log4j2 решают эту задачу декларативно.
Нет контекста и корреляции
В распределенных системах критична возможность отследить запрос через все компоненты — от API-шлюза до базы данных. Это требует привязки контекстной информации к каждому лог-сообщению: request ID, user ID, session ID, trace ID в распределенной трассировке.
System.out.println не предоставляет механизма для автоматической вставки контекста. Ручное добавление к каждому вызову невозможно в многопоточной среде, где контекст различается между потоками. MDC (Mapped Diagnostic Context) в SLF4J использует ThreadLocal для неявной привязки контекста к потоку выполнения, что позволяет декларативно включать поля контекста в каждое сообщение через шаблон конфигурации.
#Java #для_новичков #beginner #logging #System_out_println
👍6
Что выведет код?
#Tasks
public class Task060526 {
private static String getValue() {
try {
return "try";
} finally {
System.out.print("finally ");
}
}
public static void main(String[] args) {
System.out.print(getValue() + " ");
System.out.println("main");
}
}#Tasks
👍2
Варианты ответа:
Anonymous Quiz
14%
try main
68%
finally try main
14%
finally main
5%
finally try main try
👍2
Что такое BigDecimal? Когда его использовать? 🤓
Ответ:
BigDecimal — класс для работы с десятичными числами с произвольной точностью.
Используется, когда критична точность вычислений (финансовые расчеты, налоги, валюты). float и double имеют проблемы с точным представлением дробей (например, 0.1 + 0.2).
BigDecimal позволяет задать масштаб (количество знаков после запятой) и режим округления. Операции создают новый объект (immutable).
Минус: медленнее примитивов. Создавать через строки: new BigDecimal("0.1"), а не new BigDecimal(0.1).
#собеседование
Ответ:
Используется, когда критична точность вычислений (финансовые расчеты, налоги, валюты). float и double имеют проблемы с точным представлением дробей (например, 0.1 + 0.2).
BigDecimal позволяет задать масштаб (количество знаков после запятой) и режим округления. Операции создают новый объект (immutable).
Минус: медленнее примитивов. Создавать через строки: new BigDecimal("0.1"), а не new BigDecimal(0.1).
#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
