Menu
Coddy logo textTech

Wzorzec try-with-resources

Część sekcji Programowanie obiektowe ścieżki Java w Coddy. Lekcja 62 z 87.

Podczas pracy z zasobami takimi jak strumienie plików lub połączenia z bazą danych musisz zawsze je zamykać po zakończeniu pracy. W przeciwnym razie ryzykujesz wycieki pamięci. Tradycyjne podejście wykorzystuje blok finally, ale jest rozwlekłe i podatne na błędy.

Instrukcja try-with-resources w Javie automatycznie zamyka zasoby po zakończeniu bloku. Każda klasa implementująca interfejs AutoCloseable może być używana z tą składnią:

// Tradycyjne podejście – rozwlekłe i ryzykowne
BufferedReader reader = null;
try {
    reader = new BufferedReader(new FileReader("file.txt"));
    String line = reader.readLine();
} finally {
    if (reader != null) {
        reader.close();  // To również może zgłosić wyjątek!
    }
}

// Try-with-resources – zwięzłe i bezpieczne
try (BufferedReader reader = new BufferedReader(new FileReader("file.txt"))) {
    String line = reader.readLine();
}  // Tutaj następuje automatyczne zamknięcie

Możesz zarządzać wieloma zasobami, oddzielając je średnikami. Są zamykane w odwrotnej kolejności niż zostały zadeklarowane:

try (FileReader fr = new FileReader("input.txt");
     BufferedReader br = new BufferedReader(fr)) {
    // Użyj obu zasobów
}  // Najpierw zamykany jest br, a potem fr

Aby utworzyć własną klasę, którą można automatycznie zamykać, zaimplementuj interfejs AutoCloseable i jego jedyną metodę close():

public class DatabaseConnection implements AutoCloseable {
    public DatabaseConnection() {
        System.out.println("Connection opened");
    }
    
    @Override
    public void close() {
        System.out.println("Connection closed");
    }
}

Ten wzorzec zapewnia prawidłowe zwalnianie zasobów, nawet jeśli podczas przetwarzania wystąpią wyjątki.

challenge icon

Wyzwanie

Łatwy

Stwórzmy system zarządzania zasobami, który pokaże możliwości konstrukcji try-with-resources! Utworzysz własne klasy automatycznie zamykające się, które symulują zarządzanie połączeniami i sesjami, zapewniając ich prawidłowe zamknięcie.

Rozmieścisz kod w trzech plikach:

  • Connection.java: Utwórz klasę symulującą połączenie z bazą danych. Twoja klasa Connection powinna implementować AutoCloseable. Powinna zawierać prywatne pole przechowujące nazwę połączenia (String), konstruktor, który przyjmuje nazwę i wyświetla [name] connection opened, metodę query(String sql), która wyświetla Executing on [name]: [sql], oraz wymaganą metodę close(), która wyświetla [name] connection closed.
  • Session.java: Utwórz kolejną klasę automatycznie zamykającą się, która symuluje sesję użytkownika. Twoja klasa Session również powinna implementować AutoCloseable. Dodaj prywatne pole przechowujące nazwę użytkownika (String), konstruktor, który przyjmuje nazwę użytkownika i wyświetla Session started for [username], metodę performAction(String action), która wyświetla [username] performed: [action], oraz metodę close(), która wyświetla Session ended for [username].
  • Main.java: Połącz elementy systemu zarządzania zasobami! Otrzymasz trzy dane wejściowe: nazwę połączenia, nazwę użytkownika i akcję do wykonania.

    Najpierw zademonstruj zarządzanie pojedynczym zasobem. Wyświetl === Single Resource ===, a następnie użyj konstrukcji try-with-resources, aby utworzyć obiekt Connection z podaną nazwą. Wewnątrz bloku try wywołaj query("SELECT * FROM users"). Połączenie powinno zamknąć się automatycznie po zakończeniu bloku.

    Następnie zademonstruj zarządzanie wieloma zasobami. Wyświetl pusty wiersz, a potem === Multiple Resources ===. Użyj konstrukcji try-with-resources z obiektami Connection (używając tej samej nazwy) oraz Session (używając nazwy użytkownika), zadeklarowanymi w tej kolejności i rozdzielonymi średnikami. Wewnątrz bloku try najpierw wywołaj na połączeniu metodę query("INSERT INTO logs"), a następnie wywołaj na sesji metodę performAction, przekazując jej akcję z danych wejściowych. Zwróć uwagę, że zasoby zamykają się w odwrotnej kolejności!

Otrzymasz trzy dane wejściowe w następującej kolejności: nazwę połączenia (String), nazwę użytkownika (String) i akcję (String).

Zauważ, że konstrukcja try-with-resources eliminuje potrzebę jawnego wywoływania metod close i używania bloków finally. Zasoby na pewno zostaną zamknięte, nawet jeśli wystąpią wyjątki, a wiele zasobów zamyka się w odwrotnej kolejności ich deklaracji!

Spróbuj swoich sił

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String connectionName = scanner.nextLine();
        String username = scanner.nextLine();
        String action = scanner.nextLine();
        
        // === Single Resource ===
        // TODO: Wypisz nagłówek
        // TODO: Użyj try-with-resources, aby utworzyć Connection
        // TODO: Wewnątrz bloku try wywołaj query("SELECT * FROM users")
        
        // === Multiple Resources ===
        // TODO: Wypisz pusty wiersz i nagłówek
        // TODO: Użyj try-with-resources zarówno dla Connection, jak i Session
        // TODO: Wewnątrz bloku try:
        //       - Wywołaj query("INSERT INTO logs") na połączeniu
        //       - Wywołaj performAction na sesji, przekazując wczytaną akcję
        
    }
}
quiz iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator Java online