Throws Clause: Đẩy trách nhiệm, không lo bị 'cháy' code!
Java – OOP

Throws Clause: Đẩy trách nhiệm, không lo bị 'cháy' code!

Author

Admin System

@root

Ngày xuất bản

21 Mar, 2026

Lượt xem

2 Lượt

throws clause

Throws Clause: Ai là người chịu trách nhiệm khi code 'cháy'?

Chào các Gen Z, hôm nay anh Creyt sẽ giải mã một khái niệm mà nhiều bạn hay nhầm lẫn với try-catch: đó là throws clause. Nghe có vẻ hàn lâm nhưng thực ra nó là một "cơ chế báo động" cực kỳ cool ngầu trong Java, giúp code của chúng ta bền vững hơn.

1. Throws Clause là gì? Để làm gì? (Theo phong cách Gen Z)

Nói đơn giản thế này, throws clause giống như việc bạn đi dự một buổi concert rock vậy. Trước khi vào cửa, BTC (tức là Java) đưa cho bạn một cái tờ giấy ghi rõ: "Cảnh báo: Có thể có tiếng ồn lớn, đèn flash liên tục, và mosh pit có thể diễn ra. Tự chịu trách nhiệm với sự an toàn của bản thân."

Trong lập trình, khi bạn viết một phương thức (method), và phương thức đó có khả năng gây ra một "sự cố" (exception) mà bạn không muốn hoặc không thể xử lý ngay tại chỗ, bạn sẽ dùng throws để "dán nhãn cảnh báo" lên chữ ký của phương thức đó. Điều này có nghĩa là bạn đang nói với bất kỳ ai muốn dùng phương thức của bạn rằng: "Ê, cái method này có khả năng 'nổ banh xác' đấy nhé! Ai dùng thì tự chuẩn bị phương án phòng hờ đi!"

Mục đích chính của throws là:

  • Khai báo trách nhiệm: Phương thức này không tự xử lý exception mà đẩy trách nhiệm cho phương thức gọi nó (caller method).
  • Buộc người dùng phải lưu tâm: Java sẽ ép buộc phương thức gọi phải hoặc try-catch để xử lý, hoặc throws tiếp để đẩy trách nhiệm đi xa hơn.
  • Làm rõ ý định: Giúp người đọc code hiểu ngay những rủi ro tiềm ẩn của một phương thức.

throws thường được dùng với các Checked Exceptions – những loại ngoại lệ mà Java bắt bạn phải xử lý rõ ràng (ví dụ: IOException, SQLException). Còn với Unchecked Exceptions (như NullPointerException, ArrayIndexOutOfBoundsException), bạn không bắt buộc phải khai báo throws vì chúng thường là lỗi lập trình và nên được sửa ngay từ đầu.

2. Code Ví Dụ Minh Họa Rõ Ràng

Anh Creyt sẽ cho các bạn xem một ví dụ kinh điển với việc đọc file. Đọc file là một hoạt động tiềm ẩn nhiều rủi ro (file không tồn tại, không có quyền đọc,...) nên nó sinh ra FileNotFoundException (một loại IOException – Checked Exception).

Ví dụ 1: Phương thức docFileAnToan khai báo throws FileNotFoundException

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;

public class CreytClass {

    // Phương thức này khai báo rằng nó CÓ THỂ ném ra FileNotFoundException
    // Nó không tự xử lý, mà đẩy trách nhiệm cho phương thức gọi nó.
    public void docFileAnToan(String tenFile) throws FileNotFoundException {
        File file = new File(tenFile);
        Scanner scanner = new Scanner(file); // Dòng này có thể ném FileNotFoundException
        System.out.println("Đọc file thành công: " + tenFile);
        while (scanner.hasNextLine()) {
            System.out.println(scanner.nextLine());
        }
        scanner.close();
    }

    public static void main(String[] args) {
        CreytClass creyt = new CreytClass();
        
        // Khi gọi docFileAnToan, Java BẮT BUỘC chúng ta phải xử lý ngoại lệ
        // Ở đây, chúng ta dùng try-catch để "bắt" ngoại lệ nếu nó xảy ra.
        try {
            creyt.docFileAnToan("data.txt"); // Giả sử file này không tồn tại
            System.out.println("Tiếp tục chạy sau khi đọc file.");
        } catch (FileNotFoundException e) {
            System.err.println("Ôi không, không tìm thấy file rồi! " + e.getMessage());
            System.err.println("Kiểm tra lại đường dẫn hoặc tên file đi bạn ơi.");
            // e.printStackTrace(); // Dùng cái này để xem stack trace đầy đủ
        } catch (Exception e) { // Bắt các ngoại lệ khác nếu có
            System.err.println("Có lỗi gì đó không mong muốn: " + e.getMessage());
        }

        System.out.println("Chương trình kết thúc.");
    }
}

Nếu bạn không thêm throws FileNotFoundException vào chữ ký của docFileAnToan, trình biên dịch Java sẽ la làng ngay lập tức vì Scanner scanner = new Scanner(file); có khả năng ném FileNotFoundException (một Checked Exception) mà bạn lại không xử lý hoặc khai báo!

Ví dụ 2: Phương thức gọi cũng throws tiếp

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;

public class CreytClass2 {

    public void docFileGoc(String tenFile) throws FileNotFoundException {
        File file = new File(tenFile);
        Scanner scanner = new Scanner(file);
        System.out.println("Đọc file gốc thành công: " + tenFile);
        while (scanner.hasNextLine()) {
            System.out.println(scanner.nextLine());
        }
        scanner.close();
    }

    // Phương thức này gọi docFileGoc, và nó cũng KHÔNG xử lý ngoại lệ
    // mà đẩy trách nhiệm lên cho phương thức gọi nó (ở đây là main).
    public void xuLyDuLieuTuFile(String duongDan) throws FileNotFoundException {
        System.out.println("Đang xử lý dữ liệu từ: " + duongDan);
        docFileGoc(duongDan); // Gọi phương thức có throws
        System.out.println("Xử lý dữ liệu hoàn tất.");
    }

    public static void main(String[] args) {
        CreytClass2 creyt = new CreytClass2();
        
        // Cuối cùng, main phải là nơi xử lý hoặc khai báo throws tiếp (nhưng main thì không nên)
        try {
            creyt.xuLyDuLieuTuFile("nonexistent.txt");
            System.out.println("Chương trình chạy ngon lành.");
        } catch (FileNotFoundException e) {
            System.err.println("Lỗi nghiêm trọng: Không tìm thấy file ở bất kỳ đâu! " + e.getMessage());
        } catch (Exception e) {
            System.err.println("Lỗi tổng quát: " + e.getMessage());
        }
        System.out.println("Chương trình kết thúc.");
    }
}

Ở ví dụ 2, docFileGoc đẩy FileNotFoundException cho xuLyDuLieuTuFile, và xuLyDuLieuTuFile lại đẩy tiếp cho main. Đây là một chuỗi đẩy trách nhiệm, và cuối cùng main (hoặc một lớp xử lý ngoại lệ tập trung) sẽ là nơi "đỡ" exception.

Illustration

3. Một Vài Mẹo (Best Practices) Từ Anh Creyt

  • Đừng lạm dụng throws Exception: Việc khai báo throws Exception chung chung giống như bạn dán cái biển "Cẩn thận, có thể có mọi thứ nguy hiểm!" vậy. Nó quá rộng và làm người dùng không biết chính xác rủi ro là gì. Hãy cụ thể hóa: throws IOException, throws SQLException, v.v...
  • throws hay try-catch?
    • Dùng try-catch khi bạn biết cách xử lý ngoại lệ ngay tại chỗ (ví dụ: ghi log lỗi, hiển thị thông báo thân thiện cho người dùng, thử lại thao tác).
    • Dùng throws khi bạn không biết cách xử lý hoặc thấy rằng phương thức gọi có thông tin tốt hơn để xử lý (ví dụ: một thư viện tiện ích sẽ throws để người dùng thư viện tự quyết định).
  • Tài liệu hóa (Document) rõ ràng: Khi bạn throws một ngoại lệ, hãy thêm Javadoc để giải thích tại sao phương thức đó lại ném ra ngoại lệ đó và trong trường hợp nào (@throws FileNotFoundException if the specified file does not exist.).
  • Thận trọng với main method: Hạn chế throws trong main method. main thường là điểm vào của ứng dụng, nếu nó throws thì coi như chương trình crash luôn. main nên là nơi cuối cùng để try-catch và hiển thị thông báo lỗi thân thiện.

4. Ứng Dụng Thực Tế

throws clause xuất hiện nhan nhản trong các ứng dụng/website mà bạn dùng hàng ngày, đặc biệt là ở những nơi liên quan đến:

  • Thao tác I/O (Input/Output): Đọc/ghi file (java.io.*), kết nối mạng (java.net.*). Hầu hết các phương thức này đều throws IOException hoặc các ngoại lệ con của nó.
    • Ví dụ: Khi bạn upload ảnh lên Facebook, hay lưu một bài viết trên Notion, các hàm xử lý file phía server sẽ dùng throws để báo hiệu nếu có vấn đề về quyền truy cập hay dung lượng ổ đĩa.
  • Kết nối Database: Khi bạn truy vấn dữ liệu từ MySQL, PostgreSQL, các phương thức của JDBC (Java Database Connectivity) như Connection.createStatement(), Statement.executeQuery() đều throws SQLException.
    • Ví dụ: Khi bạn login vào ứng dụng ngân hàng, nếu có lỗi kết nối database, các method xử lý sẽ throws SQLException để tầng nghiệp vụ biết và hiển thị lỗi "Hệ thống đang bảo trì" thay vì crash.
  • Gọi API (Web Services): Khi ứng dụng của bạn gọi đến một API của bên thứ ba (ví dụ: API thời tiết, API thanh toán), các thư viện HTTP client thường throws các ngoại lệ liên quan đến mạng hoặc phản hồi không hợp lệ.
    • Ví dụ: Khi app đặt đồ ăn gọi API thanh toán, nếu mạng chập chờn, API client sẽ throws SocketException hoặc IOException, và app sẽ báo "Không thể kết nối thanh toán, vui lòng thử lại".

5. Thử Nghiệm Đã Từng và Hướng Dẫn Nên Dùng Cho Case Nào

Anh Creyt từng thấy nhiều bạn newbie khi gặp lỗi "unhandled exception" thì auto throws Exception lên main để cho qua. Đây là một sai lầm lớn! Nó giống như bạn thấy nhà cháy mà không dập lửa, chỉ la làng lên và chạy ra đường mặc kệ nhà mình cháy trụi vậy.

Nên dùng throws khi:

  1. Bạn viết thư viện hoặc API: Khi bạn tạo ra các phương thức mà người khác sẽ sử dụng. Bạn không thể biết cách người dùng muốn xử lý lỗi, vì vậy throws là cách tốt nhất để thông báo và đẩy trách nhiệm cho họ.
    • Ví dụ: Một thư viện xử lý ảnh có hàm resizeImage(File originalFile, int width, int height) throws IOException, ImageFormatException. Thư viện không biết người dùng muốn làm gì nếu file không tồn tại hay định dạng ảnh sai, nên nó throws để người dùng tự try-catch hoặc throws tiếp.
  2. Phương thức của bạn là một phần của một chuỗi xử lý lớn hơn: Và ở tầng hiện tại, bạn không có đủ thông tin hoặc ngữ cảnh để xử lý lỗi một cách ý nghĩa. Bạn muốn lỗi được đẩy lên tầng cao hơn (nơi có nhiều thông tin hơn) để được xử lý tập trung.
    • Ví dụ: Hàm docCauHinh() chỉ có nhiệm vụ đọc file cấu hình. Nếu file không tồn tại, nó throws FileNotFoundException. Hàm khoiTaoUngDung() gọi docCauHinh(). Hàm này có thể try-catch và hiển thị thông báo lỗi "Không thể khởi động ứng dụng vì thiếu file cấu hình" và thoát chương trình một cách an toàn.
  3. Khi bạn muốn ép buộc người dùng phải chú ý đến lỗi: Đây là bản chất của Checked Exceptions. Java muốn bạn phải chú ý đến chúng.

Nhớ nhé Gen Z, throws clause không phải là cái cớ để trốn tránh trách nhiệm, mà là một công cụ mạnh mẽ để phân chia trách nhiệm xử lý lỗi một cách rõ ràng, giúp code của bạn minh bạch và dễ bảo trì hơn rất nhiều. Hãy dùng nó một cách khôn ngoan!

Thuộc Series: Java – OOP

Bài giảng này được tự động xuất bản ngẫu nhiên từ thư viện kiến thức. Đừng quên đón xem các Từ khoá Hướng Dẫn tiếp theo nhé!

#tech #cyberpunk #laravel
Chỉnh sửa bài viết

Bình luận (0)

Vui lòng Đăng Nhập để Bình luận

Hỗ trợ Markdown cơ bản
Nguyễn Văn A
1 ngày trước

Tính năng này đỉnh quá ad ơi, chờ mãi mới thấy một blog Tiếng Việt có UI/UX xịn như vầy!