Java for Beginner
870 subscribers
1.01K photos
275 videos
14 files
1.69K links
Канал от новичков для новичков!
Изучайте Java вместе с нами!
Здесь мы обмениваемся опытом и постоянно изучаем что-то новое!

Наш YouTube канал - https://www.youtube.com/@Java_Beginner-Dev

Наш канал на RUTube - https://rutube.ru/channel/37896292/
Download Telegram
Выбор между Exception и RuntimeException

Это архитектурное решение, определяющее контракт метода и обязательства вызывающего кода. Современная практика существенно сдвинулась в сторону RuntimeException, но понимание обоих подходов необходимо.

Checked-исключения: наследование от Exception

Checked-исключения форсируют явную обработку на уровне вызывающего кода. Компилятор гарантирует, что каждый potential failure path либо перехвачен, либо проброшен дальше. Это делает checked-исключения подходящими для сценариев, где восстановление не просто возможно, но и требуется по бизнес-логике.

Пример корректного использования checked-исключения:

/**
* Исключение, сигнализирующее о необходимости ручного подтверждения
* платежа из-за превышения лимита. Вызывающий код должен инициировать
* процедуру двухфакторной аутентификации.
*/
public class ManualApprovalRequiredException extends Exception {
private final String transactionId;
private final BigDecimal amount;
private final String requiredApprovalLevel;

public ManualApprovalRequiredException(String transactionId, BigDecimal amount,
String requiredApprovalLevel) {
super(String.format("Transaction %s requires %s approval for amount %s",
transactionId, requiredApprovalLevel, amount));
this.transactionId = transactionId;
this.amount = amount;
this.requiredApprovalLevel = requiredApprovalLevel;
}

public String getTransactionId() { return transactionId; }
public BigDecimal getAmount() { return amount; }
public String getRequiredApprovalLevel() { return requiredApprovalLevel; }
}


Это checked-исключение оправдано, потому что:
— Восстановление требует специфических действий от вызывающего кода (инициация MFA)
— Исключение является частью доменной модели и бизнес-процесса
— Пропуск обработки приведет к некорректному поведению системы

Unchecked-исключения: наследование от RuntimeException

Unchecked-исключения предпочтительны для подавляющего большинства сценариев в современной Java-разработке. Они не загромождают сигнатуры методов, совместимы с функциональным стилем (Stream API, лямбды), и соответствуют философии Spring, Kotlin и других современных фреймворков.

Пример доменного unchecked-исключения:
/**
* Исключение, сигнализирующее о нарушении бизнес-правила.
* Восстановление невозможно без изменения входных данных.
*/
public class BusinessRuleViolationException extends RuntimeException {
private final String ruleCode;
private final String entityType;
private final String entityId;

public BusinessRuleViolationException(String ruleCode, String entityType,
String entityId, String message) {
super(message);
this.ruleCode = ruleCode;
this.entityType = entityType;
this.entityId = entityId;
}

public String getRuleCode() { return ruleCode; }
public String getEntityType() { return entityType; }
public String getEntityId() { return entityId; }
}


Использование в доменном коде:
public class Order {
private OrderStatus status;
private List<OrderItem> items;

public void cancel() {
if (status == OrderStatus.SHIPPED) {
throw new BusinessRuleViolationException(
"ORDER_ALREADY_SHIPPED",
"Order",
this.id,
"Cannot cancel order that has already been shipped"
);
}
if (status == OrderStatus.CANCELLED) {
throw new BusinessRuleViolationException(
"ORDER_ALREADY_CANCELLED",
"Order",
this.id,
"Order is already cancelled"
);
}
this.status = OrderStatus.CANCELLED;
}
}



#Java #для_новичков #beginner #exception #custom_exception
👍4
Антипаттерны при проектировании исключений

Антипаттерн "God Exception"

Создание единого кастомного исключения с enum-кодами ошибок вместо иерархии классов — распространенный, но вредный подход. Разработчик создает один класс ApplicationException с полем ErrorCode, и использует enum для различения сценариев:
// Антипаттерн: God Exception с enum
public class ApplicationException extends RuntimeException {
private final ErrorCode errorCode;

public ApplicationException(ErrorCode errorCode, String message) {
super(message);
this.errorCode = errorCode;
}
}

// Использование
throw new ApplicationException(ErrorCode.INVALID_DATA_FORMAT, "...");
throw new ApplicationException(ErrorCode.MISSING_REQUIRED_FIELD, "...");



Проблемы этого подхода:

— Клиентский код вынужден использовать if-else или switch по error code вместо полиморфизма catch-блоков
— Каждое добавление нового сценария требует модификации enum, нарушая Open/Closed Principle
— Невозможно использовать специфичные catch-блоки для разных типов ошибок
— Type safety теряется — компилятор не может проверить корректность обработки

Правильный подход — использовать иерархию классов для сценариев, требующих разной обработки :
public abstract class PaymentException extends RuntimeException {
protected PaymentException(String message, Throwable cause) {
super(message, cause);
}
}

public class InsufficientFundsException extends PaymentException {
private final BigDecimal available;
private final BigDecimal requested;

public InsufficientFundsException(BigDecimal available, BigDecimal requested) {
super(String.format("Insufficient funds: available %s, requested %s",
available, requested), null);
this.available = available;
this.requested = requested;
}
}

public class PaymentGatewayTimeoutException extends PaymentException {
private final String gatewayId;
private final int timeoutMillis;

public PaymentGatewayTimeoutException(String gatewayId, int timeoutMillis) {
super(String.format("Gateway %s timed out after %dms", gatewayId, timeoutMillis), null);
this.gatewayId = gatewayId;
this.timeoutMillis = timeoutMillis;
}
}


Теперь клиентский код может использовать полиморфизм:
try {
paymentService.process(payment);
} catch (InsufficientFundsException e) {
// Специфическая логика: предложить пополнение счета
return ResponseEntity.status(402)
.body(new PaymentErrorResponse("INSUFFICIENT_FUNDS", e.getAvailable()));
} catch (PaymentGatewayTimeoutException e) {
// Специфическая логика: retry с другим шлюзом
return retryWithAlternativeGateway(payment);
} catch (PaymentException e) {
// Общая обработка для всех платежных ошибок
return ResponseEntity.status(500)
.body(new PaymentErrorResponse("PAYMENT_FAILED", e.getMessage()));
}


#Java #для_новичков #beginner #exception #custom_exception
👍4
Антипаттерн "Destructive Wrapping"

Потеря стектрейса при оборачивании исключений — критическая ошибка, делающая отладку невозможной:
// Антипаттерн: потеря cause
try {
processFile(file);
} catch (IOException e) {
throw new FileProcessingException("Failed to process file"); // Cause потерян!
}
Правильный подход — всегда передавать оригинальное исключение в конструктор:
java
Copy
// Правильно: сохранение цепочки причин
try {
processFile(file);
} catch (IOException e) {
throw new FileProcessingException("Failed to process file: " + file.getName(), e);
}



Проектирование иерархии исключений

Хорошо спроектированная иерархия исключений отражает структуру предметной области и уровни абстракции. Рекомендуется создавать базовые классы для каждого домена или слоя приложения, от которых наследуются конкретные исключения.
// Базовое исключение для всего приложения
public abstract class LibraryException extends RuntimeException {
protected LibraryException(String message, Throwable cause) {
super(message, cause);
}
}

// Домен: каталог книг
public abstract class CatalogException extends LibraryException {
protected CatalogException(String message, Throwable cause) {
super(message, cause);
}
}

public class BookNotFoundException extends CatalogException {
private final String isbn;

public BookNotFoundException(String isbn) {
super("Book not found: " + isbn, null);
this.isbn = isbn;
}

public String getIsbn() { return isbn; }
}

public class DuplicateIsbnException extends CatalogException {
private final String isbn;
private final Long existingBookId;

public DuplicateIsbnException(String isbn, Long existingBookId) {
super(String.format("ISBN %s already exists for book %d", isbn, existingBookId), null);
this.isbn = isbn;
this.existingBookId = existingBookId;
}
}

// Домен: пользователи
public abstract class UserException extends LibraryException {
protected UserException(String message, Throwable cause) {
super(message, cause);
}
}

public class UserNotActiveException extends UserException {
private final String userId;
private final UserStatus currentStatus;

public UserNotActiveException(String userId, UserStatus currentStatus) {
super(String.format("User %s is not active, current status: %s", userId, currentStatus), null);
this.userId = userId;
this.currentStatus = currentStatus;
}
}


Эта иерархия позволяет:
— Перехватывать все ошибки каталога через catch (CatalogException e)
— Перехватывать все ошибки приложения через catch (LibraryException e)
— Обрабатывать специфичные сценарии через конкретные типы
— Добавлять новые исключения без модификации существующего кода


#Java #для_новичков #beginner #exception #custom_exception
👍4
Конструкторы и структурированные данные

Каждый кастомный класс исключения должен предоставлять набор конструкторов, соответствующий стандартным паттернам Java:
public class LibraryProcessingException extends RuntimeException {
private final String operation;
private final String entityId;
private final Instant timestamp;

// Базовый конструктор
public LibraryProcessingException(String operation, String entityId, String message) {
this(operation, entityId, message, null);
}

// Конструктор с cause для цепочки исключений
public LibraryProcessingException(String operation, String entityId,
String message, Throwable cause) {
super(message, cause);
this.operation = operation;
this.entityId = entityId;
this.timestamp = Instant.now();
}

// Геттеры для структурированного доступа к данным
public String getOperation() { return operation; }
public String getEntityId() { return entityId; }
public Instant getTimestamp() { return timestamp; }
}


Наличие конструктора с Throwable cause критично для поддержки exception chaining — механизма, позволяющего сохранять полный контекст ошибки при трансляции через слои приложения.


Интеграция с Spring и глобальной обработкой

В современных Spring-приложениях кастомные исключения интегрируются с глобальной обработкой ошибок через @ControllerAdvice, что устраняет необходимость в checked-исключениях и распределенных try-catch блоках:
@ControllerAdvice
public class LibraryExceptionHandler {

@ExceptionHandler(BookNotFoundException.class)
public ResponseEntity<ErrorResponse> handleBookNotFound(BookNotFoundException e) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(new ErrorResponse(
"BOOK_NOT_FOUND",
e.getMessage(),
Map.of("isbn", e.getIsbn())
));
}

@ExceptionHandler(DuplicateIsbnException.class)
public ResponseEntity<ErrorResponse> handleDuplicateIsbn(DuplicateIsbnException e) {
return ResponseEntity.status(HttpStatus.CONFLICT)
.body(new ErrorResponse(
"DUPLICATE_ISBN",
e.getMessage(),
Map.of("isbn", e.getIsbn(), "existingBookId", e.getExistingBookId())
));
}

@ExceptionHandler(LibraryException.class)
public ResponseEntity<ErrorResponse> handleGenericLibrary(LibraryException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(new ErrorResponse(
"LIBRARY_ERROR",
"An unexpected library error occurred",
Map.of()
));
}
}


#Java #для_новичков #beginner #exception #custom_exception
👍5
Что выведет код?

class MyCheckedException extends Exception {
public MyCheckedException() { super(); }
}

class MyUncheckedException extends RuntimeException {
public MyUncheckedException() { super(); }
}

public class Task220426 {
public static void main(String[] args) {
try {
throw new MyUncheckedException();
} catch (Exception e) {
System.out.print("1");
}

try {
throw new MyCheckedException();
} catch (RuntimeException e) {
System.out.print("2");
} catch (Exception e) {
System.out.print("3");
}

try {
throw new MyUncheckedException();
} finally {
System.out.print("4");
}
}
}


#Tasks
🔥2👍1
👍2
Что такое CDI (Contexts and Dependency Injection)? 🤓

Ответ:

CDI
— это стандарт внедрения зависимостей для Java EE / Jakarta EE (спецификация JSR-365).

Он предоставляет мощный механизм для управления зависимостями с учетом контекста (например, запрос, сессия, приложение).

CDI поддерживает типовую безопасность, события, декораторы, перехватчики и продвинутые возможности жизненного цикла.

В мире Spring аналогичный функционал реализован в Spring Framework, но CDI — это именно спецификация, которую можно реализовать (например, Weld — реализация CDI).


#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
История технологии сегодня — 23 апреля

ℹ️ Кто родился в этот день

Макс Карл Эрнст Людвиг Планк (нем. Max Karl Ernst Ludwig Planck; 23 апреля 1858, Киль, королевство Пруссия — 4 октября 1947, Гёттинген, американская зона оккупации Германии) — немецкий физик-теоретик, основоположник квантовой физики. Лауреат Нобелевской премии по физике (1918) и других наград, член Прусской академии наук (1894), ряда иностранных научных обществ и академий наук. На протяжении многих лет один из руководителей немецкой науки.

Научные труды Планка посвящены термодинамике, теории теплового излучения, квантовой теории, специальной теории относительности, оптике. Он сформулировал второе начало термодинамики в виде принципа возрастания энтропии и использовал его для решения различных задач физической химии. Применив к проблеме равновесного теплового излучения методы электродинамики и термодинамики, Планк получил закон распределения энергии в спектре абсолютно чёрного тела (формула Планка) и обосновал этот закон, введя представление о квантах энергии и кванте действия. Это достижение положило начало развитию квантовой физики, разработкой различных аспектов которой он занимался в последующие годы («вторая теория» Планка, проблема структуры фазового пространства, статистическая механика квантовых систем и так далее). Планк впервые вывел уравнения динамики релятивистской частицы и заложил основы релятивистской термодинамики.


🌐 Знаковые события

1965 — в космос запущен первый советский спутник связи «Молния-1».

1982 — выход компьютера ZX Spectrum.



#Biography #Birth_Date #Events #23апреля
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
[Совет по Java #029]

Тема: Class.forName() загружает класс и инициализирует статические блоки.

Проблема: Метод Class.forName(String className) по умолчанию выполняет полную инициализацию загружаемого класса.

Это означает, что все статические поля инициализируются, а статические блоки (static initializers) выполняются. Такое поведение может быть опасным: инициализация может быть тяжелой (подключение к БД, чтение конфигураций, запуск потоков) или даже нежелательной (например, при загрузке вредоносного кода или классов, вызывающих системные вызовы).

Кроме того, статическая инициализация может выбрасывать исключения, которые не планировалось обрабатывать. Во многих сценариях (проверка наличия класса, загрузка класса для рефлексии без выполнения побочных эффектов) требуется только загрузка и связывание (linking), но не инициализация.

Решение: Для загрузки класса без инициализации используйте ClassLoader.loadClass(String name).

Этот метод загружает класс и выполняет связывание, но не инициализирует его (статические блоки не выполняются).
public class ClassLoadingExample {

private static final String HEAVY_CLASS = "com.example.DatabaseDriver";

//Антипаттерн: Class.forName() с нежелательной инициализацией
public static void dangerousLoad() throws ClassNotFoundException {
// Выполнит статические блоки класса DatabaseDriver
// Может открыть соединение, зарегистрировать драйвер и т.д.
Class<?> clazz = Class.forName(HEAVY_CLASS);
System.out.println("Класс загружен и инициализирован: " + clazz.getName());
}

//Решение 1: загрузка без инициализации через ClassLoader
public static Class<?> safeLoadWithoutInit(String className) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
try {
// loadClass() загружает и связывает, но не инициализирует
return classLoader.loadClass(className);
} catch (ClassNotFoundException e) {
System.err.println("Класс не найден: " + className);
return null;
}
}

//Решение 2: Class.forName() с отключенной инициализацией
public static Class<?> safeForName(String className) throws ClassNotFoundException {
ClassLoader loader = Thread.currentThread().getContextClassLoader();
// initialize = false — статические блоки НЕ выполняются
return Class.forName(className, false, loader);
}

//Демонстрация разницы
static class DemoClass {
static {
System.out.println("Статический блок DemoClass выполнен!");
// Здесь может быть тяжелая операция
}

static int value = computeValue();

private static int computeValue() {
System.out.println("Статический метод computeValue() вызван");
return 42;
}
}

Объяснение: Процесс загрузки класса в Java состоит из трех фаз: загрузка (loading) — поиск и чтение байт-кода, связывание (linking) — верификация, подготовка статических полей с значениями по умолчанию, и инициализация (initialization) — выполнение статических блоков и инициализаторов.

Class.forName() по умолчанию выполняет все три фазы. ClassLoader.loadClass() останавливается после связывания. Разница критична для классов с тяжелой статической инициализацией (регистрация драйверов, создание пулов соединений, загрузка native-библиотек). Кроме того, статическая инициализация может выбросить ExceptionInInitializerError, что трудно предсказать при динамической загрузке.

#Java #советы
👍4
Что выведет код?

public class Task230426 {
static class TestClass {
static {
System.out.println("Static block");
}
}

public static void main(String[] args) throws Exception {
System.out.println("Start");

Class<?> clazz1 = Class.forName(Task230426.class.getName() + "$TestClass");
System.out.println("After forName");

Class<?> clazz2 = Task230426.class.getClassLoader()
.loadClass(Task230426.class.getName() + "$TestClass");
System.out.println("After loadClass");
}
}


#Tasks
👍2
Что такое Java Module System (Project Jigsaw)? 🤓

Ответ:

Java Module System
— это новая система модулей, появившаяся в Java 9.

Она добавляет уровень абстракции выше пакетов. Модуль описывается в файле
module-info.java, где указывается, какие пакеты он экспортирует (делает доступными для других модулей) и от каких модулей зависит (requires).

Это позволяет создавать надежные, хорошо инкапсулированные приложения, бороться с classpath hell и улучшать безопасность. JDK сам теперь разбит на модули, что позволяет создавать образы JRE только с нужными модулями (jlink).


#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
История технологии сегодня — 24 апреля

ℹ️ Кто родился в этот день

Дэвид Гарольд Блэквелл (англ. David Harold Blackwell; 24 апреля 1919 — 8 июля 2010)американский статистик, внесший значительный вклад в теорию игр, теорию вероятностей, теорию информации и статистику. Доктор, эмерит-профессор статистики в Калифорнийском университете в Беркли (после преподавания там в 1954-1989), и является одним из авторов теоремы Рао — Блэквелла — Колмогорова. Стал первым афроамериканцем, избранным в Национальную академию наук США (1965) и первым тёмнокожим штатным преподавателем в Калифорнийском университете в Беркли. В марте 2024 года компания Nvidia представила семейство графических ускорителей на базе новой архитектуры Blackwell, названную в его честь.

Гидео́н (Гедео́н) Сундбэк (или Сандбек; нем. и швед. Gideon Sundbäck, англ. Gideon Sundback; 24 апреля 1880, Эдестугу, лен Йёнчёпинг, Швеция — 21 июня 1954, Мидвилл, Пенсильвания)
— шведско-американский инженер и изобретатель. Его самым известным изобретением является современная застёжка-молния, созданная им в 1913 году после года работы над ней. Ему удалось усовершенствовать застёжку Джадсона, лишив её крючков и увеличив количество скрепляющего элемента на единицу длины. Этот вариант застёжки имел два ряда зубчиков, которые цеплялись друг за друга. В 1914 году Сундбек изобрёл другой вариант «молнии без крючков», в которой зубчики имели углубление снизу и небольшой конический выступ сверху. Это изобретение было запатентовано в 1917 году.

Э́дмунд Ка́ртрайт (англ. Edmund Cartwright; 24 апреля 1743 — 30 октября 1823) — английский изобретатель. 4 апреля 1785 года Картрайт получил патент на механический ткацкий станок с ножным приводом, который в 40 раз увеличил производительность труда. По ходатайству 50 фабрикантов за свои изобретения Картрайт получил от парламента вознаграждение в 10 000 фунтов стерлингов. Картрайт известен также как поэт и агроном.


🌐 Знаковые события

1990 — запущен орбитальный телескоп Хаббл.

2003 — выход Windows Server 2003.

2005 — в видеохостинге YouTube опубликован первый видеоролик «Me at the zoo».


#Biography #Birth_Date #Events #24апреля
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥5
Раздел 9. Исключения, логирование, отладка

Глава 1. Иерархия исключений (Exceptions)

Несколько ресурсов в try-with-resources: порядок закрытия и effectively final переменные (Java 9+)

Конструкция try-with-resources, появившаяся в Java 7, поддерживает объявление нескольких ресурсов в одном блоке, разделенных точкой с запятой. Спецификация языка Java (JLS, раздел 14.20.3) однозначно определяет порядок их закрытия: ресурсы закрываются в обратном порядке относительно порядка их инициализации.


Архитектурная логика обратного порядка

Рассмотрим типичный сценарий копирования данных:
try (FileInputStream fis = new FileInputStream("source.txt");
BufferedInputStream bis = new BufferedInputStream(fis);
FileOutputStream fos = new FileOutputStream("dest.txt");
BufferedOutputStream bos = new BufferedOutputStream(fos)) {

byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = bis.read(buffer)) != -1) {
bos.write(buffer, 0, bytesRead);
}
}


В этом примере создается цепочка зависимостей: BufferedInputStream оборачивает FileInputStream, BufferedOutputStream оборачивает FileOutputStream. При закрытии необходимо соблюдать иерархию зависимостей — внешний обертчик должен быть закрыт до внутреннего ресурса, чтобы корректно сбросить буферизованные данные и освободить высокоуровневые ресурсы до низкоуровневых.

Согласно спецификации, закрытие произойдет в следующем порядке:
bos.close() — сброс буфера вывода и закрытие обертки
fos.close() — закрытие файлового потока вывода
bis.close() — закрытие обертки ввода
fis.close() — закрытие файлового потока ввода

Этот порядок гарантирует, что буферизованные данные будут корректно записаны на диск до закрытия файлового дескриптора, а ресурсы освобождаются от высокоуровневых абстракций к низкоуровневым примитивам.


Механизм компилятора

Компилятор Java трансформирует try-with-resources с несколькими ресурсами во вложенные блоки try-finally. Спецификация JLS определяет рекурсивную структуру: для двух ресурсов генерируется вложенный try, для трех — третий уровень вложенности и так далее . Каждый уровень отвечает за один ресурс и содержит логику suppressed exceptions.

Эквивалентный псевдокод для двух ресурсов:
// Псевдокод трансформации компилятором
{
final Resource r1 = new Resource("r1");
Throwable primaryExc1 = null;
try {
final Resource r2 = new Resource("r2");
Throwable primaryExc2 = null;
try {
// Основной блок
r1.work();
r2.work();
} catch (Throwable t) {
primaryExc2 = t;
throw t;
} finally {
if (r2 != null) {
if (primaryExc2 != null) {
try {
r2.close();
} catch (Throwable suppressed) {
primaryExc2.addSuppressed(suppressed);
}
} else {
r2.close();
}
}
}
} catch (Throwable t) {
primaryExc1 = t;
throw t;
} finally {
if (r1 != null) {
if (primaryExc1 != null) {
try {
r1.close();
} catch (Throwable suppressed) {
primaryExc1.addSuppressed(suppressed);
}
} else {
r1.close();
}
}
}
}


Важное следствие этой трансформации: если при создании второго ресурса возникает исключение, первый ресурс, уже успешно созданный, будет автоматически закрыт перед пробросом исключения. Это фундаментальное преимущество try-with-resources перед ручным управлением ресурсами, где при ошибке создания второго ресурса первый остался бы незакрытым.


#Java #для_новичков #beginner #exception #try_with_resources
👍3
Подводный камень: двойное закрытие и делегирование

Важно понимать, что многие обертки потоков в Java делегируют закрытие внутреннему ресурсу. BufferedReader.close() вызывает FileReader.close(), ObjectOutputStream.close() вызывает FileOutputStream.close(). В сочетании с обратным порядком закрытия try-with-resources это может привести к двойному вызову close() на одном и том же underlying ресурсе.
try (FileReader fr = new FileReader("file.txt");
BufferedReader br = new BufferedReader(fr)) {
// ...
}
// Закрытие: br.close() вызывает fr.close(), затем fr.close() вызывается повторно


Хотя большинство реализаций Closeable идемпотентны и повторный вызов close() безопасен, это не гарантируется интерфейсом AutoCloseable. При проектировании собственных ресурсов следует обеспечивать идемпотентность метода close() или отслеживать состояние закрытия.


JDBC: классический пример множественных ресурсов

Работа с JDBC демонстрирует практическую необходимость обратного порядка закрытия:
public List<User> findUsersByRole(String role) throws SQLException {
String query = "SELECT id, name, email FROM users WHERE role = ?";

try (Connection conn = dataSource.getConnection();
PreparedStatement stmt = conn.prepareStatement(query);
ResultSet rs = stmt.executeQuery()) {

stmt.setString(1, role);
List<User> users = new ArrayList<>();

while (rs.next()) {
users.add(mapRowToUser(rs));
}
return users;
}
}


Закрытие произойдет в порядке: rs → stmt → conn. Это корректно, так как ResultSet зависит от PreparedStatement, который зависит от Connection. Закрытие в прямом порядке привело бы к попытке использования закрытого Statement при закрытии ResultSet, что вызвало бы SQLException.


Effectively final переменные в try-with-resources (Java 9+)

Проблема Java 7/8: принудительное объявление новой переменной

До Java 9 ресурсы в try-with-resources должны были объявляться непосредственно внутри круглых скобок после ключевого слова try. Это создавало избыточность, когда ресурс уже существовал во внешней области видимости и был готов к использованию:
// Java 7/8: избыточное объявление новой переменной
BufferedReader br = new BufferedReader(new FileReader("data.txt"));
try (BufferedReader reader = br) { // Вынуждены создать reader, хотя br уже готов
String line = reader.readLine();
// ...
}

В этом примере br и reader ссылаются на один и тот же объект, но компилятор требует новую переменную внутри try. Это загромождает код и создает путаницу в именовании.


Решение Java 9: прямое использование существующих переменных


Java 9 (JEP 213) расширила синтаксис try-with-resources, позволяя использовать переменные, объявленные вне блока, при условии что они являются final или effectively final. Переменная считается effectively final, если ей присваивается значение ровно один раз и она не модифицируется впоследствии, даже без явного модификатора final.

Новый синтаксис:
// Java 9+: прямое использование существующей переменной
BufferedReader br = new BufferedReader(new FileReader("data.txt"));
try (br) { // Нет необходимости в новой переменной
String line = br.readLine();
// ...
}


Для нескольких ресурсов синтаксис остается лаконичным:
// Java 9+: несколько effectively final ресурсов
final Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(query); // effectively final

try (statement; resultSet) {
while (resultSet.next()) {
processRow(resultSet);
}
}


Важное ограничение: ресурс в скобках try должен быть именно переменной (expression name), а не произвольным выражением. Конструкция try (getResource()) по-прежнему недопустима.


#Java #для_новичков #beginner #exception #try_with_resources
👍3
Практическое применение: рефакторинг legacy-кода

Рассмотрим сценарий, где ресурс создается в одном методе, передается в другой, и должен быть закрыт после использования:
public class ReportGenerator {

public void generateMonthlyReport(Path outputPath) throws IOException {
// Ресурс создается здесь
BufferedWriter writer = Files.newBufferedWriter(outputPath);

try {
writeHeader(writer);
writeBody(writer);
writeFooter(writer);
} finally {
// Ручное закрытие до Java 9
if (writer != null) {
try {
writer.close();
} catch (IOException e) {
logger.warn("Failed to close writer", e);
}
}
}
}
}


С Java 9 этот код упрощается:
public void generateMonthlyReport(Path outputPath) throws IOException {
BufferedWriter writer = Files.newBufferedWriter(outputPath);

try (writer) {
writeHeader(writer);
writeBody(writer);
writeFooter(writer);
}
// writer автоматически закрыт, suppressed exceptions сохранены
}



Критическое ограничение: безопасность при инициализации

Главное архитектурное отличие между классическим try-with-resources и синтаксисом effectively final переменных заключается в моменте захвата ресурса. Когда ресурс объявляется внутри try, компилятор гарантирует, что он будет закрыт даже если исключение возникнет при создании другого ресурса в том же блоке. С effectively final переменными эта гарантия теряется, так как ресурсы создаются вне try.
// Потенциально опасный код с effectively final
Resource r1 = new Resource("r1");
Resource r2 = new Resource("r2"); // Если здесь исключение — r1 не будет закрыт!

try (r1; r2) {
// ...
}


В этом примере, если конструктор r2 выбрасывает исключение, управление передается вне try-with-resources, и r1 остается незакрытым. В классическом синтаксисе try (Resource r1 = new Resource("r1"); Resource r2 = new Resource("r2")) ресурс r1 был бы закрыт автоматически перед пробросом исключения.

Рекомендация: использовать effectively final синтаксис только когда ресурсы уже гарантированно созданы и инициализированы, или когда логика приложения сама управляет их жизненным циклом до входа в try-with-resources.


Интеграция с try-catch-finally

Расширенный синтаксис Java 9 полностью совместим с традиционными catch и finally блоками. При наличии нескольких ресурсов, объявленных вне try, закрытие происходит перед входом в catch, а finally выполняется после обработки исключений:
Connection conn = dataSource.getConnection();
PreparedStatement stmt = conn.prepareStatement(query);
ResultSet rs = stmt.executeQuery();

try (conn; stmt; rs) {
return extractResults(rs);
} catch (SQLException e) {
// Выполняется после закрытия всех ресурсов
throw new DataAccessException("Query failed: " + query, e);
} finally {
// Выполняется после catch, все ресурсы уже закрыты
metrics.recordQueryExecution(query, System.currentTimeMillis() - startTime);
}


Эволюция демонстрирует тенденцию к уменьшению шаблонного кода при сохранении безопасности. Java 7 устранила необходимость явного finally для закрытия, Java 9 устранила необходимость дублирования переменных.

#Java #для_новичков #beginner #exception #try_with_resources
👍4
Что выведет код?

import java.io.*;

public class Task240426 {
public static void main(String[] args) throws IOException {
BufferedReader reader = new BufferedReader(new StringReader("test"));

try (reader) {
System.out.print(reader.readLine());
}

reader.readLine();
}
}


#Tasks
👍3
Что такое JVM, JRE и JDK? В чем разница? 🤓

Ответ:

JVM (Java Virtual Machine)
— это виртуальная машина, которая выполняет байт-код Java. Обеспечивает кроссплатформенность.

JRE (Java Runtime Environment) — среда выполнения, включает JVM + библиотеки классов + другие компоненты для запуска Java-программ.

JDK (Java Development Kit) — комплект разработчика, включает JRE + инструменты для разработки (компилятор javac, отладчик jdb, javadoc, jar и др.).

Чтобы только запускать Java-программы, нужен JRE. Чтобы разрабатывать — JDK.


#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
История технологии сегодня — 25 апреля

ℹ️ Кто родился в этот день

Андре́й Никола́евич Колмого́ров (12 (25) апреля 1903, Тамбов — 20 октября 1987, Москва)советский математик, один из крупнейших математиков XX века. Один из основоположников современной теории вероятностей, им получены фундаментальные результаты в топологии, геометрии, математической логике, классической механике, теории турбулентности, теории сложности алгоритмов, теории информации, теории функций, теории тригонометрических рядов, теории меры, теории приближения функций, теории множеств, теории дифференциальных уравнений, теории динамических систем, функциональном анализе и в ряде других областей математики и её приложений. Автор новаторских работ по философии, истории, методологии и преподаванию математики, известны его работы в статистической физике (в частности, уравнение Джонсона — Мела — Аврами — Колмогорова).

Гулье́льмо Джова́нни Мари́я Марко́ни (итал. Guglielmo Giovanni Maria Marconi; 25 апреля 1874, Болонья — 20 июля 1937, Рим) — итальянский радиотехник, предприниматель и дипломат. Лауреат Нобелевской премии по физике за 1909 год совместно с Карлом Фердинандом Брауном «в знак признания их вклада в развитие беспроволочной телеграфии». Основатель Wireless Telegraph & Signal Company (впоследствии Marconi Company[англ.]). С именем Маркони во многих странах связывают начало практического применения радиосвязи.


🌐 Знаковые события

1826 — в Англии запатентован первый автомобиль с двигателем внутреннего сгорания.

1954 — американский исследовательский центр компании «Белл телефон» объявил о создании солнечных батарей.

2022 — Илон Маск купил «Твиттер» за 44 млрд долларов.


#Biography #Birth_Date #Events #25апреля
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3