← Назад к вопросам
Приведи пример использования finally
2.3 Middle🔥 211 комментариев
#Docker, Kubernetes и DevOps#REST API и микросервисы
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Блок finally: Гарантированное выполнение кода
finally — один из ключевых механизмов Java для гарантирования выполнения кода, независимо от того, произойдет ли исключение или нет. Это критично для освобождения ресурсов и cleanup операций.
Базовый синтаксис
try {
// Code that might throw exception
} catch (SpecificException e) {
// Handle specific exception
} finally {
// ALWAYS executes, whether exception or not
// Cleanup code
}
Когда finally выполняется
public class FinallyExecutionExample {
// Случай 1: Нет исключения
public static void noException() {
try {
System.out.println("1. In try block");
return; // finally выполнится ДО return!
} finally {
System.out.println("2. In finally block");
}
}
// Случай 2: С исключением
public static void withException() {
try {
System.out.println("1. In try block");
throw new RuntimeException("Error!");
} catch (RuntimeException e) {
System.out.println("2. In catch block: " + e.getMessage());
} finally {
System.out.println("3. In finally block");
}
}
// Случай 3: Исключение перебрасывается
public static void rethrowException() {
try {
System.out.println("1. In try block");
throw new RuntimeException("Error!");
} catch (RuntimeException e) {
System.out.println("2. In catch block, rethrowing");
throw e; // finally выполнится ДО повторного броска
} finally {
System.out.println("3. In finally block");
}
}
public static void main(String[] args) {
System.out.println("=== No Exception ===");
noException(); // Output: 1, 2
System.out.println("\n=== With Exception ===");
withException(); // Output: 1, 2, 3
System.out.println("\n=== Rethrow Exception ===");
try {
rethrowException(); // Output: 1, 2, 3, then exception thrown
} catch (RuntimeException e) {
System.out.println("4. Exception caught in main");
}
}
}
/* Output:
=== No Exception ===
1. In try block
2. In finally block
=== With Exception ===
1. In try block
2. In catch block: Error!
3. In finally block
=== Rethrow Exception ===
1. In try block
2. In catch block, rethrowing
3. In finally block
4. Exception caught in main
*/
Практические примеры finally
1. Закрытие файлов и ресурсов
// ❌ ПЛОХО: Ресурс может не закрыться
public String readFile(String filename) throws IOException {
FileReader reader = new FileReader(filename);
BufferedReader br = new BufferedReader(reader);
String line = br.readLine(); // Если здесь exception, ресурс не закроется
return line;
}
// ✅ ХОРОШО: finally гарантирует закрытие
public String readFileWithFinally(String filename) throws IOException {
FileReader reader = null;
BufferedReader br = null;
try {
reader = new FileReader(filename);
br = new BufferedReader(reader);
return br.readLine();
} finally {
// Закрываем в обратном порядке
if (br != null) {
try {
br.close();
} catch (IOException e) {
System.err.println("Error closing BufferedReader: " + e);
}
}
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
System.err.println("Error closing FileReader: " + e);
}
}
}
}
// ✅ ЛУЧШЕ: try-with-resources (Java 7+)
public String readFileWithTryWithResources(String filename) throws IOException {
// Ресурсы автоматически закроются
try (FileReader reader = new FileReader(filename);
BufferedReader br = new BufferedReader(reader)) {
return br.readLine();
}
// finally выполнится автоматически, закрыв оба ресурса
}
2. Работа с базой данных
public class DatabaseTransaction {
private Connection connection;
// ❌ ПЛОХО: Connection может остаться открытым
public User getUserById(Long id) {
try {
connection = getConnection();
String sql = "SELECT * FROM users WHERE id = ?";
PreparedStatement stmt = connection.prepareStatement(sql);
stmt.setLong(1, id);
ResultSet rs = stmt.executeQuery();
if (rs.next()) {
return mapToUser(rs);
}
return null;
} catch (SQLException e) {
throw new DataAccessException("Failed to get user", e);
}
}
// ✅ ХОРОШО: finally закроет ресурсы
public User getUserByIdWithFinally(Long id) {
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
try {
conn = getConnection();
String sql = "SELECT * FROM users WHERE id = ?";
stmt = conn.prepareStatement(sql);
stmt.setLong(1, id);
rs = stmt.executeQuery();
if (rs.next()) {
return mapToUser(rs);
}
return null;
} catch (SQLException e) {
throw new DataAccessException("Failed to get user", e);
} finally {
// Закрываем в обратном порядке
closeResource(rs);
closeResource(stmt);
closeResource(conn);
}
}
private void closeResource(AutoCloseable resource) {
if (resource != null) {
try {
resource.close();
} catch (Exception e) {
System.err.println("Error closing resource: " + e);
}
}
}
// ✅ ЛУЧШЕ: Spring Data JPA управляет ресурсами
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
// Ресурсы управляются Spring'ом
}
}
3. Lock'и и синхронизация
public class ReentrantLockExample {
private final Lock lock = new ReentrantLock();
private int counter = 0;
// ❌ ПЛОХО: Lock может остаться заблокированным
public void incrementCounterBad() {
lock.lock();
counter++; // Если здесь exception, lock не разблокируется
System.out.println("Counter: " + counter);
lock.unlock();
}
// ✅ ХОРОШО: finally разблокирует гарантированно
public void incrementCounterWithFinally() {
lock.lock();
try {
counter++;
System.out.println("Counter: " + counter);
} finally {
lock.unlock(); // ВСЕГДА выполнится
}
}
// ✅ ЛУЧШЕ: try-with-resources для Lock'ов (Java 9+)
public void incrementCounterWithTryWithResources() {
try (LockCloseable lock = new LockCloseable(this.lock)) {
counter++;
System.out.println("Counter: " + counter);
}
}
}
// Helper класс для try-with-resources
class LockCloseable implements AutoCloseable {
private final Lock lock;
public LockCloseable(Lock lock) {
this.lock = lock;
this.lock.lock();
}
@Override
public void close() {
lock.unlock();
}
}
4. Временные файлы и temp ресурсы
public class TempFileManager {
// ❌ ПЛОХО: Временный файл может остаться
public void processTempFile() throws IOException {
File tempFile = File.createTempFile("temp", ".txt");
FileOutputStream fos = new FileOutputStream(tempFile);
fos.write("Some data".getBytes()); // Может бросить exception
fos.close();
tempFile.delete();
}
// ✅ ХОРОШО: finally удалит файл
public void processTempFileWithFinally() throws IOException {
File tempFile = File.createTempFile("temp", ".txt");
FileOutputStream fos = null;
try {
fos = new FileOutputStream(tempFile);
fos.write("Some data".getBytes());
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
System.err.println("Error closing stream");
}
}
if (tempFile.exists()) {
tempFile.delete();
}
}
}
// ✅ ЛУЧШЕ: Java NIO Files
public void processTempFileNio() throws IOException {
Path tempPath = Files.createTempFile("temp", ".txt");
try {
Files.write(tempPath, "Some data".getBytes());
} finally {
Files.deleteIfExists(tempPath); // Удаляет если существует
}
}
}
5. Логирование и аудит
@Service
public class AuditService {
private static final Logger LOGGER = LoggerFactory.getLogger(AuditService.class);
private final AuditRepository auditRepository;
// ✅ finally гарантирует логирование
public void performAuditedAction(String userId, String action) {
AuditLog log = new AuditLog();
log.setUserId(userId);
log.setAction(action);
log.setStartTime(LocalDateTime.now());
long startTime = System.currentTimeMillis();
try {
// Выполняем действие
System.out.println("Performing action: " + action);
// Может бросить exception
if (action.equals("dangerous")) {
throw new SecurityException("Dangerous action!");
}
log.setStatus("SUCCESS");
} catch (Exception e) {
LOGGER.error("Action failed: {} for user {}", action, userId, e);
log.setStatus("FAILED");
log.setErrorMessage(e.getMessage());
throw e;
} finally {
// ВСЕГДА логируем
long duration = System.currentTimeMillis() - startTime;
log.setEndTime(LocalDateTime.now());
log.setDurationMs(duration);
auditRepository.save(log);
LOGGER.info("Audit log saved: {} - {} ms", action, duration);
}
}
}
6. Сложный пример: HTTP запрос
public class HttpClientExample {
private static final HttpClient httpClient = HttpClient.newHttpClient();
// ❌ ПЛОХО: HttpResponse Body может не быть прочитана
public void makeBadRequest(String url) throws IOException, InterruptedException {
HttpRequest request = HttpRequest.newBuilder(URI.create(url))
.GET()
.build();
HttpResponse<String> response = httpClient.send(
request,
HttpResponse.BodyHandlers.ofString()
);
System.out.println(response.body());
}
// ✅ ХОРОШО: finally обеспечивает cleanup
public String makeGoodRequest(String url) throws IOException, InterruptedException {
HttpRequest request = HttpRequest.newBuilder(URI.create(url))
.timeout(java.time.Duration.ofSeconds(10))
.GET()
.build();
try {
HttpResponse<String> response = httpClient.send(
request,
HttpResponse.BodyHandlers.ofString()
);
if (response.statusCode() >= 400) {
throw new HttpException("HTTP error: " + response.statusCode());
}
return response.body();
} finally {
// Логируем запрос для аудита
System.out.println("Request completed: " + url);
}
}
}
Важные правила для finally
1. finally вызывается даже при return
public int getValue() {
try {
return 42;
} finally {
System.out.println("finally executes before return");
}
// Вывод: "finally executes before return", потом return 42
}
2. Если finally бросает exception, original exception потеряется
public void problematicFinally() {
try {
throw new RuntimeException("Original error");
} finally {
throw new RuntimeException("Error in finally"); // ЗАМЕНЯЕТ original!
}
}
3. Используй try-with-resources (Java 7+)
// Автоматический cleanup для AutoCloseable ресурсов
try (FileReader fr = new FileReader("file.txt");
BufferedReader br = new BufferedReader(fr)) {
String line = br.readLine();
System.out.println(line);
} catch (IOException e) {
System.err.println("Error: " + e);
}
// Ресурсы закроются автоматически, даже при exception
4. Порядок закрытия ресурсов важен
// Правильный порядок: открыли A, потом B -> закрываем B, потом A
FileReader reader = new FileReader(filename);
BufferedReader br = new BufferedReader(reader);
try {
// ...
} finally {
br.close(); // Сначала BufferedReader
reader.close(); // Потом FileReader
}
Ключевые выводы
- finally гарантирует выполнение кода независимо от исключений
- Используй для cleanup: файлы, соединения, locks, resources
- try-with-resources лучше, чем finally для большинства случаев (Java 7+)
- Не полагайся на finally для бизнес-логики — используй для cleanup
- Закрывай ресурсы в finally или try-with-resources, никогда не оставляй открытыми