Mẫu try-with-resources
Thuộc phần Lập trình hướng đối tượng trong hành trình Java của Coddy. Bài học 62 trên 87.
Khi làm việc với các tài nguyên như luồng tệp hoặc kết nối cơ sở dữ liệu, bạn luôn phải đóng chúng khi dùng xong. Nếu không, bạn có nguy cơ gây rò rỉ bộ nhớ. Cách làm truyền thống dùng khối lệnh finally, nhưng cách này dài dòng và dễ gây lỗi.
Câu lệnh try-with-resources của Java tự động đóng các tài nguyên khi khối lệnh kết thúc. Bạn có thể dùng cú pháp này với bất kỳ lớp nào triển khai giao diện AutoCloseable:
// Cách truyền thống: dài dòng và dễ gặp rủi ro
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader("file.txt"));
String line = reader.readLine();
} finally {
if (reader != null) {
reader.close(); // Lệnh này cũng có thể ném ra ngoại lệ!
}
}
// Try-with-resources: gọn gàng và an toàn
try (BufferedReader reader = new BufferedReader(new FileReader("file.txt"))) {
String line = reader.readLine();
} // Tự động đóng tại đâyBạn có thể quản lý nhiều tài nguyên bằng cách phân tách chúng bằng dấu chấm phẩy. Chúng được đóng theo thứ tự ngược với thứ tự khai báo:
try (FileReader fr = new FileReader("input.txt");
BufferedReader br = new BufferedReader(fr)) {
// Sử dụng cả hai tài nguyên
} // Đóng br trước, rồi đến frĐể tạo lớp tự động đóng của riêng mình, hãy triển khai giao diện AutoCloseable và phương thức duy nhất close() của giao diện đó:
public class DatabaseConnection implements AutoCloseable {
public DatabaseConnection() {
System.out.println("Connection opened");
}
@Override
public void close() {
System.out.println("Connection closed");
}
}Mẫu này đảm bảo tài nguyên luôn được giải phóng đúng cách, kể cả khi xảy ra ngoại lệ trong quá trình xử lý.
Thử thách
DễHãy cùng xây dựng một hệ thống quản lý tài nguyên để thấy sức mạnh của try-with-resources! Bạn sẽ tạo các lớp tự đóng tùy chỉnh mô phỏng việc quản lý kết nối và phiên, đồng thời bảo đảm chúng luôn được dọn dẹp đúng cách.
Bạn sẽ sắp xếp code trong ba tệp:
Connection.java: Tạo một lớp mô phỏng kết nối cơ sở dữ liệu. LớpConnectioncần triển khaiAutoCloseable. Lớp này cần có một trường private lưu tên kết nối (String), một hàm khởi tạo nhận tên và in ra[name] connection opened, một phương thứcquery(String sql)in raExecuting on [name]: [sql], và phương thứcclose()bắt buộc in ra[name] connection closed.Session.java: Tạo một lớp tự đóng khác mô phỏng phiên người dùng. LớpSessioncũng cần triển khaiAutoCloseable. Thêm một trường private lưu tên người dùng (String), một hàm khởi tạo nhận tên người dùng và in raSession started for [username], một phương thứcperformAction(String action)in ra[username] performed: [action], và một phương thứcclose()in raSession ended for [username].Main.java: Kết hợp các phần để hoàn thành hệ thống quản lý tài nguyên! Bạn sẽ nhận ba đầu vào: tên kết nối, tên người dùng và hành động cần thực hiện.Trước tiên, hãy minh họa cách quản lý một tài nguyên. In ra
=== Single Resource ===, sau đó dùng try-with-resources để tạo mộtConnectionvới tên đã cho. Bên trong khối try, gọiquery("SELECT * FROM users"). Kết nối sẽ tự động đóng khi khối lệnh kết thúc.Tiếp theo, hãy minh họa cách quản lý nhiều tài nguyên. In ra một dòng trống, sau đó in ra
=== Multiple Resources ===. Dùng try-with-resources với cảConnection(dùng cùng tên đó) vàSession(dùng tên người dùng), khai báo theo thứ tự này và ngăn cách bằng dấu chấm phẩy. Bên trong khối try, trước tiên gọiquery("INSERT INTO logs")trên kết nối, sau đó gọiperformActionvới hành động đầu vào trên phiên. Hãy chú ý thứ tự đóng tài nguyên sẽ đảo ngược!
Bạn sẽ lần lượt nhận ba đầu vào: tên kết nối (String), tên người dùng (String) và hành động (String).
Hãy chú ý cách try-with-resources loại bỏ nhu cầu gọi close tường minh và dùng các khối finally. Tài nguyên chắc chắn sẽ đóng ngay cả khi xảy ra ngoại lệ, và nhiều tài nguyên sẽ đóng theo thứ tự ngược với thứ tự khai báo!
Tự làm thử
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: In ra tiêu đề
// TODO: Dùng try-with-resources để tạo một Connection
// TODO: Trong khối try, gọi query("SELECT * FROM users")
// === Multiple Resources ===
// TODO: In ra một dòng trống và tiêu đề
// TODO: Dùng try-with-resources với cả Connection và Session
// TODO: Trong khối try:
// - Gọi query("INSERT INTO logs") trên kết nối
// - Gọi performAction trên phiên với action từ đầu vào
}
}
Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.
Tất cả bài học trong Lập trình hướng đối tượng
1Kiến thức cơ bản về lập trình hướng đối tượng
Tệp bên ngoàiGiới thiệu về lập trình hướng đối tượngLớp và đối tượngTừ khóa thisPhương thứcTrường (thuộc tính)Phương thức khởi tạoNạp chồng phương thức khởi tạoÔn tập: Máy tính đơn giản4Kế thừa
Kế thừa cơ bản (extends)Từ khóa superGhi đè phương thức (@Override)Chuỗi gọi hàm khởi tạoLớp ObjectKế thừa đơn và đa cấpVì sao không hỗ trợ kế thừa nhiều lớpÔn tập: Phân cấp lớp Employee7Phương thức đặc biệt và lớp Object
Phương thức toString()equals() và hashCode()Phương thức clone()compareTo() và ComparableGiao diện ComparatorÔn tập: sắp xếp tùy chỉnh10Xử lý ngoại lệ trong OOP
Phân cấp lớp ngoại lệNgoại lệ tùy chỉnhLỗi có kiểm tra và không kiểm traMẫu try-with-resourcesÔn tập: người dùng đã xác thực2Bộ điều chỉnh quyền truy cập và đóng gói
Tổng quan về các mức truy cậpPhương thức getter và setterẨn thông tinTừ khóa finalÔn tập: Trình quản lý tài khoản ngân hàng5Tính đa hình
Kiến thức cơ bản về nạp chồng phương thứcGhi đè phương thức (thời gian chạy)Chuyển kiểu lên và chuyển kiểu xuốngToán tử instanceofLớp và phương thức trừu tượngÔn tập: máy tính hình học8Khái niệm OOP nâng cao
Kết hợp và kế thừaKết tập và hợp thànhLớp bên trong, lớp lồng nhau và lớp ẩn danhKiểu enum và phương thức enumBản ghi (Java 16+)Lớp sealed (Java 17+)3Thuộc tính lớp và thành viên tĩnh
Biến của instance và biến tĩnhPhương thức tĩnhKhối tĩnhHằng số (static final)Ôn tập: bộ đếm và tiện ích6Giao diện và lớp trừu tượng
Giới thiệu về giao diệnTriển khai giao diệnTriển khai nhiều giao diệnPhương thức mặc định và tĩnh trong giao diệnLớp trừu tượng và giao diệnGiao diện hàmÔn tập: hệ thống thanh toán9Kiểu tổng quát
Giới thiệu về kiểu tổng quátLớp tổng quátPhương thức tổng quátTham số kiểu có giới hạnKý tự đại diện (?, extends, super)Ôn tập: vùng chứa tổng quát12Mẫu thiết kế, phần 2
Mẫu CommandMẫu AdapterMẫu DecoratorMẫu Template MethodMẫu StateMẫu CompositeMẫu IteratorTự luyện tập: Trình biên dịch Java online