Menu
CoddyTech

Xử lý ngoại lệ là gì?

Xử lý ngoại lệ (exception handling) là cách để chương trình phản ứng với các lỗi xảy ra trong lúc chạy. Code có thể thất bại được đặt trong một khối được bảo vệ, như try, và khi một lỗi được ném ra, quyền điều khiển nhảy tới một trình xử lý, như except hoặc catch, thay vì crash.

Tác giả Kevin Spektor, Co-founder & CTO

Cập nhật 24 tháng 9, 2026

Một chương trình hỏi tuổi người dùng sớm muộn cũng sẽ nhận được "twenty" thay vì 20. Chuyển đổi đoạn văn bản đó là một lỗi runtime: int("twenty") ném ra ValueError, và nếu không có phương án nào cho nó, chương trình dừng lại. Xử lý ngoại lệ chính là phương án đó. Bạn đánh dấu đoạn code có thể thất bại và viết đoạn code sẽ chạy nếu nó thất bại thật.

Next year you will be 35
Not a number: twenty
Next year you will be 20
Done

Câu trả lời sai được báo ra, và vòng lặp tiếp tục với câu trả lời kế tiếp. Không có try, chương trình sẽ in ra một dòng rồi đến một traceback.

Xử lý ngoại lệ hoạt động thế nào

  1. Code bên trong try chạy bình thường.
  2. Một thao tác thất bại, và runtime ném ra một ngoại lệ (exception): một đối tượng có kiểu (ValueError) và thông báo (invalid literal for int() with base 10: 'twenty').
  3. Khối try dừng lại ở dòng đó. Các dòng còn lại bị bỏ qua, vì thế "Next year you will be" không được in ra với "twenty".
  4. Runtime so sánh kiểu của ngoại lệ với từng mệnh đề except, từ trên xuống dưới, và chạy mệnh đề đầu tiên khớp.
  5. Sau khi trình xử lý chạy xong, chương trình tiếp tục từ dòng đầu tiên sau toàn bộ câu lệnh try.
  6. Nếu không mệnh đề nào khớp, ngoại lệ rời khỏi hàm hiện tại và việc tìm kiếm tiếp tục ở hàm đã gọi nó, rồi ở nơi gọi hàm đó. Quá trình này gọi là tháo call stack (unwinding). Nếu việc tìm kiếm lên tới đỉnh chương trình mà vẫn không khớp, chương trình kết thúc kèm một traceback.

Bước 6 nghĩa là trình xử lý không nhất thiết phải nằm trong hàm nơi lỗi xảy ra. Ở đây lỗi xảy ra sâu hai tầng lời gọi, và trình xử lý ở cuối vẫn bắt được:

parsing 4.50
parsing free
Could not add the prices: could not convert string to float: 'free'

float("free") thất bại bên trong parse_price. Cả parse_price lẫn total đều không có trình xử lý, nên cả hai bị bỏ dở, và "3.20" không bao giờ được phân tích. Đây là lựa chọn thiết kế chính mà xử lý ngoại lệ trao cho bạn: xử lý lỗi ở nơi bạn biết phải làm gì với nó, thường là cách xa nơi nó xảy ra.

try, except, else và finally

Câu lệnh try của Python còn hai phần tùy chọn nữa. else chỉ chạy khi khối try kết thúc mà không có ngoại lệ. finally chạy trong mọi trường hợp: sau khi thành công, sau một ngoại lệ đã được xử lý, và cả khi hàm return từ bên trong try.

no error
finally runs either way
2.0
b was zero
finally runs either way
None

finally là nơi đặt code dọn dẹp: đóng tệp, nhả khóa, đóng kết nối cơ sở dữ liệu. Trong Python, câu lệnh with tự động làm việc dọn dẹp này với tệp, và đó là lựa chọn thông thường cho tệp.

Tự ném ngoại lệ

Ngoại lệ không chỉ dành cho những lỗi mà ngôn ngữ phát hiện. Khi hàm của bạn nhận đầu vào nó không xử lý được, nó có thể ném một ngoại lệ để từ chối, và nơi gọi sẽ quyết định việc tiếp theo. Định nghĩa một lớp ngoại lệ nhỏ cho lỗi một cái tên mô tả đúng nó.

70
Refused: cannot take 250, balance is 100

Python dùng raise. Java, C++ và JavaScript dùng throw cho cùng việc đó.

Xử lý ngoại lệ trong các ngôn ngữ khác

Ngôn ngữTừ khóaĐiều nên biết
Pythontry, except, else, finally, raiseMọi ngoại lệ là một đối tượng kế thừa từ BaseException
Javatry, catch, finally, throw, throwsChecked exception như IOException phải được bắt hoặc khai báo bằng throws
C++try, catch, throwKhông có finally; hàm hủy dọn dẹp khi stack được tháo dần
JavaScripttry, catch, finally, throwLỗi trong code async được bắt bằng try bao quanh await
Ckhông cóHàm trả về mã lỗi, và nhiều hàm đặt giá trị cho errno
Gokhông có cho lỗi thông thườngHàm trả về một giá trị error để nơi gọi kiểm tra

Cùng ví dụ phân tích dữ liệu đó trong Java trông như sau:

try {
    int age = Integer.parseInt("twenty");
    System.out.println(age + 1);
} catch (NumberFormatException e) {
    System.out.println("Not a number: " + e.getMessage());
} finally {
    System.out.println("done");
}

Java chia ngoại lệ thành hai nhóm. Checked exception, như IOException, mô tả những vấn đề nằm ngoài tầm kiểm soát của chương trình, và trình biên dịch từ chối build code không bắt cũng không khai báo chúng. Unchecked exception, các lớp con của RuntimeException như NullPointerException, thường có nghĩa là có bug, và trình biên dịch không bắt bạn phải xử lý chúng.

Lỗi thường gặp

  • Bắt mọi thứ. Một except: trơn trong Python bắt luôn cả KeyboardInterrupt, khiến Ctrl+C không còn tác dụng, còn except Exception: bắt cả những bug bạn chưa từng tính đến. Hãy bắt đúng kiểu cụ thể mà bạn biết cách xử lý.
  • Nuốt mất lỗi. except ValueError: pass giấu đi vấn đề. Ít nhất hãy in ra hoặc ghi log điều gì đã sai.
  • Khối try quá lớn. Chỉ bao quanh những dòng có thể ném ra ngoại lệ bạn đang bắt, để một lỗi không liên quan không bị xử lý nhầm.
  • Bắt bug thay vì sửa bug. Một IndexError từ vòng lặp chạy quá một bước là lỗi trong code. Xử lý nó chỉ giữ cho đoạn code sai tiếp tục tồn tại.
  • Mong catch ngăn được crash trong C hoặc C++. Segmentation fault là một tín hiệu từ hệ điều hành, không phải ngoại lệ, và trên Linux và macOS một catch (...) của C++ không bao giờ thấy nó.

Học tiếp ở đâu

Những lỗi mà xử lý ngoại lệ phản ứng được mô tả ở trang lỗi runtime, còn lỗi crash mà nó không bắt được nằm ở trang segmentation fault. Để xem đầy đủ cú pháp, hãy đọc các hướng dẫn về ngoại lệ trong Python, try và catch trong Java, ngoại lệ trong C++ và try và catch trong JavaScript. Khóa học Java và khóa học Python đều có bài tập thực tế để luyện chúng.

Câu hỏi thường gặp

5 từ khóa xử lý ngoại lệ trong Java là gì?
try, catch, finally, throw và throws. try đánh dấu đoạn code cần theo dõi, catch xử lý một kiểu ngoại lệ cụ thể, còn finally chạy code dọn dẹp dù có ngoại lệ xảy ra hay không. throw ném ra một ngoại lệ, còn throws trong chữ ký phương thức khai báo những checked exception mà phương thức có thể chuyển lên cho nơi gọi nó.
Xử lý ngoại lệ trong C++ là gì?
C++ dùng try, catch và throw. Kiểu nào cũng ném được, nhưng theo quy ước bạn ném một đối tượng kế thừa từ std::exception, như std::runtime_error, và bắt nó bằng tham chiếu: catch (const std::exception& e). C++ không có finally; việc dọn dẹp diễn ra trong hàm hủy, vốn tự động chạy khi call stack được tháo dần.
Lỗi và ngoại lệ khác nhau thế nào?
Trong cách dùng hằng ngày, lỗi là bất cứ thứ gì trục trặc, còn ngoại lệ là đối tượng mà ngôn ngữ dùng để báo cáo nó. Java phân biệt rạch ròi hơn: Exception bao gồm những vấn đề chương trình có thể xử lý một cách hợp lý, còn Error, như OutOfMemoryError hay StackOverflowError, báo hiệu một sự cố của JVM mà chương trình thường không nên cố bắt. Trong Python, ValueError và ZeroDivisionError đều là ngoại lệ dù tên gọi có chữ Error.
throw và throws trong Java khác nhau thế nào?
throw là một câu lệnh ném ra một ngoại lệ ngay tại chỗ đó: throw new IllegalArgumentException("negative age");. throws là một phần của khai báo phương thức, liệt kê những checked exception mà phương thức có thể chuyển lên cho nơi gọi: void load() throws IOException. Khi đó trình biên dịch buộc mọi nơi gọi phải bắt ngoại lệ đó hoặc cũng khai báo nó.
Xử lý ngoại lệ có làm code chạy chậm hơn không?
Vào một khối try rất rẻ ở hầu hết các ngôn ngữ hiện đại. Từ Python 3.11, một try gần như không tốn gì khi không có ngoại lệ nào được ném ra, và các trình biên dịch C++ lớn không thêm chi phí lúc chạy cho nhánh không có gì bị ném. Ném và bắt một ngoại lệ chậm hơn nhiều so với một phép kiểm tra if, nên trong các vòng lặp cần hiệu năng cao, hãy kiểm tra các trường hợp thường gặp thay vì dựa vào ngoại lệ.
Hình minh họa các ngôn ngữ lập trình của Coddy

Học lập trình cùng Coddy

Bắt đầu