← Назад к вопросам

Приведи пример использования 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
}

Ключевые выводы

  1. finally гарантирует выполнение кода независимо от исключений
  2. Используй для cleanup: файлы, соединения, locks, resources
  3. try-with-resources лучше, чем finally для большинства случаев (Java 7+)
  4. Не полагайся на finally для бизнес-логики — используй для cleanup
  5. Закрывай ресурсы в finally или try-with-resources, никогда не оставляй открытыми
Приведи пример использования finally | PrepBro