Ngoại lệ là một số tình huống đặc biệt, ngoài kế hoạch xảy ra trong quá trình vận hành chương trình. Có thể có nhiều ví dụ về trường hợp ngoại lệ trong Java. Ví dụ: bạn đã viết mã đọc văn bản từ một tệp và hiển thị dòng đầu tiên trên bảng điều khiển.
Nhưng một tập tin như vậy không tồn tại! Kết quả của chương trình sẽ là một ngoại lệ -
8. Phần kết luận:
Mỗi ngoại lệ được biểu diễn bằng một lớp riêng biệt trong Java. Tất cả các lớp ngoại lệ đều đến từ một “tổ tiên” chung - lớp cha
9. Tên của lớp ngoại lệ thường phản ánh ngắn gọn lý do xuất hiện của nó:
Có gần 400 lớp như vậy trong Java! Tại sao nhiều như vậy? Chính xác là để giúp các lập trình viên làm việc với họ thuận tiện hơn. Hãy tưởng tượng: bạn đã viết một chương trình và khi chạy, nó sẽ đưa ra một ngoại lệ trông như thế này:
Uh-uh :/ Không có gì rõ ràng cả. Đó là loại lỗi gì và nó đến từ đâu thì không rõ. Không có thông tin hữu ích. Nhưng nhờ có nhiều lớp khác nhau như vậy, lập trình viên có được điều chính cho mình - loại lỗi và nguyên nhân có thể xảy ra của nó, được chứa trong tên của lớp. Rốt cuộc, đó là một điều hoàn toàn khác để thấy trong bảng điều khiển: Nó ngay lập tức trở nên rõ ràng vấn đề có thể là gì và “đào theo hướng nào” để giải quyết vấn đề! Các ngoại lệ, giống như bất kỳ trường hợp nào của lớp, là các đối tượng. Bắt và xử lý ngoại lệĐể làm việc với các ngoại lệ trong Java, có các khối mã đặc biệt:
3,
4và
5. Đoạn mã mà người lập trình mong muốn xảy ra ngoại lệ được đặt trong một khối
3. Điều này không có nghĩa là một ngoại lệ nhất thiết sẽ xảy ra ở vị trí này. Điều này có nghĩa là nó có thể xảy ra ở đó và người lập trình biết được điều đó. Loại lỗi bạn mong nhận được sẽ được đặt trong một khối
4(“bắt”). Đây cũng là nơi đặt tất cả các mã cần được thực thi nếu xảy ra ngoại lệ. Đây là một ví dụ:
Phần kết luận:
Chúng tôi đặt mã của mình thành hai khối. Trong khối đầu tiên, chúng tôi cho rằng có thể xảy ra lỗi “Không tìm thấy tệp”. Đây là một khối
3. Trong phần thứ hai, chúng tôi cho chương trình biết phải làm gì nếu xảy ra lỗi. Hơn nữa, có một loại lỗi cụ thể -
8. Nếu chúng ta chuyển
4một lớp ngoại lệ khác vào dấu ngoặc khối, nó sẽ không bị bắt.
Phần kết luận:
Mã trong khối
4không hoạt động vì chúng tôi đã “cấu hình” khối này để chặn
1và mã trong khối
3đã loại bỏ một loại khác -
8. Chúng tôi không viết tập lệnh cho
8, vì vậy chương trình hiển thị trong bảng điều khiển thông tin được hiển thị theo mặc định cho
8. Ở đây bạn cần chú ý đến 3 điều. Đầu tiên. Ngay khi một ngoại lệ xảy ra ở bất kỳ dòng mã nào trong khối thử, mã sau nó sẽ không còn được thực thi nữa. Việc thực thi chương trình sẽ ngay lập tức “nhảy” vào khối
4. Ví dụ:
Phần kết luận:
Trong khối
3ở dòng thứ hai, chúng tôi đã cố chia một số cho 0, dẫn đến một ngoại lệ
1. Sau đó, các dòng 6-10 của khối
3sẽ không còn được thực thi nữa. Như chúng tôi đã nói, chương trình ngay lập tức bắt đầu thực thi khối
4. Thứ hai. Có thể có một số khối
4. Nếu mã trong một khối
3có thể đưa ra không chỉ một mà nhiều loại ngoại lệ, bạn có thể viết khối của riêng mình cho từng loại ngoại lệ đó
4.
0 Trong ví dụ này chúng tôi đã viết hai khối
4. Nếu ,
3xảy ra trong khối
8, khối đầu tiên sẽ được thực thi
4. Nếu xảy ra
1, cái thứ hai sẽ được thực thi. Bạn có thể viết ít nhất 50 khối
4. Nhưng tất nhiên, tốt hơn hết là bạn không nên viết mã có thể gây ra 50 loại lỗi khác nhau :) Thứ ba. Làm thế nào để bạn biết mã của bạn có thể đưa ra những ngoại lệ nào? Tất nhiên, bạn có thể đoán về một số điều, nhưng không thể giữ mọi thứ trong đầu. Do đó, trình biên dịch Java biết về các ngoại lệ phổ biến nhất và biết chúng có thể xảy ra trong những tình huống nào. Ví dụ: nếu bạn viết mã và trình biên dịch biết rằng 2 loại ngoại lệ có thể xảy ra trong quá trình hoạt động, mã của bạn sẽ không biên dịch cho đến khi bạn xử lý chúng. Chúng ta sẽ xem các ví dụ về điều này dưới đây. Bây giờ liên quan đến việc xử lý ngoại lệ. Có 2 cách để xử lý chúng. Chúng ta đã gặp trường hợp đầu tiên - phương thức có thể xử lý ngoại lệ một cách độc lập trong khối
1. Có một tùy chọn thứ hai - phương thức này có thể đưa ra một ngoại lệ lên ngăn xếp cuộc gọi. Nó có nghĩa là gì? Ví dụ: trong lớp của chúng ta, chúng ta có một phương thức - cũng giống như vậy
2- đọc một tệp và hiển thị dòng đầu tiên của nó trên bảng điều khiển:
1 Hiện tại mã của chúng tôi không biên dịch được vì nó có các ngoại lệ chưa được xử lý. Ở dòng 1 bạn chỉ đường dẫn tới file. Trình biên dịch biết rằng mã như vậy có thể dễ dàng dẫn đến các tệp
8. Trên dòng 3 bạn đọc văn bản từ tập tin.
4Trong quá trình này , lỗi trong quá trình nhập-xuất (Input-Output) có thể dễ dàng xảy ra . Bây giờ trình biên dịch sẽ nói với bạn: “Anh bạn, tôi sẽ không phê duyệt mã này hoặc biên dịch nó cho đến khi bạn cho tôi biết tôi nên làm gì nếu một trong những trường hợp ngoại lệ này xảy ra. Và chúng chắc chắn có thể xảy ra dựa trên đoạn mã bạn đã viết!” . Không có nơi nào để đi, bạn cần phải xử lý cả hai! Tùy chọn xử lý đầu tiên đã quen thuộc với chúng ta: chúng ta cần đặt mã của mình vào một khối
3và thêm hai khối
4:
2 Nhưng đây không phải là lựa chọn duy nhất. Chúng ta có thể tránh viết tập lệnh cho lỗi bên trong phương thức và chỉ cần đưa ngoại lệ lên trên cùng. Việc này được thực hiện bằng cách sử dụng từ khóa
7, được viết trong phần khai báo phương thức:
3 Sau từ này,
7chúng tôi liệt kê, phân tách bằng dấu phẩy, tất cả các loại ngoại lệ mà phương pháp này có thể đưa ra trong quá trình hoạt động. Tại sao việc này lại được thực hiện? Bây giờ, nếu ai đó trong chương trình muốn gọi phương thức này
2, anh ta sẽ phải tự mình thực hiện việc xử lý ngoại lệ. Ví dụ: trong một phần khác của chương trình, một trong những đồng nghiệp của bạn đã viết một phương thức trong đó nó gọi phương thức của bạn
2:
4 Lỗi, mã không biên dịch được!
2Chúng tôi không viết tập lệnh xử lý lỗi trong phương thức . Vì vậy, nhiệm vụ đặt lên vai những người sẽ sử dụng phương pháp này. Nghĩa là, phương thức
2hiện phải đối mặt với 2 tùy chọn giống nhau: nó phải xử lý cả hai ngoại lệ đã “bay” đến nó bằng cách sử dụng
3hoặc chuyển tiếp chúng thêm.
5 Trong trường hợp thứ hai, việc xử lý sẽ thuộc về phương thức tiếp theo trên ngăn xếp - phương thức sẽ gọi
2. Đó là lý do tại sao cơ chế như vậy được gọi là “ném một ngoại lệ lên trên” hoặc “chuyển lên trên cùng”. Khi bạn đưa ra các ngoại lệ bằng cách sử dụng
7, mã sẽ biên dịch. Tại thời điểm này, trình biên dịch dường như nói: “Được rồi, được rồi. Mã của bạn chứa rất nhiều ngoại lệ tiềm ẩn, nhưng tôi vẫn sẽ biên dịch nó. Chúng ta sẽ quay lại cuộc trò chuyện này! Và khi bạn gọi một phương thức ở đâu đó trong chương trình mà chưa xử lý các ngoại lệ của nó, trình biên dịch sẽ thực hiện lời hứa của nó và nhắc bạn về chúng một lần nữa. Cuối cùng, chúng ta sẽ nói về khối
5(xin lỗi vì chơi chữ). Đây là phần cuối cùng của quy trình xử lý ngoại lệ triumvirate
7. Điểm đặc biệt của nó là nó được thực thi trong bất kỳ kịch bản vận hành chương trình nào.
6 Trong ví dụ này, mã bên trong khối
5được thực thi trong cả hai trường hợp. Nếu mã trong khối
3được thực thi hoàn toàn và không đưa ra ngoại lệ, khối sẽ kích hoạt ở cuối
5. Nếu mã bên trong
3bị gián đoạn và chương trình nhảy vào khối
4, sau khi mã bên trong được thực thi
4, khối đó vẫn sẽ được chọn
5. Tại sao nó lại cần thiết? Mục đích chính của nó là thực thi phần mã được yêu cầu; phần đó phải được hoàn thành bất kể hoàn cảnh nào. Ví dụ, nó thường giải phóng một số tài nguyên được chương trình sử dụng. Trong mã của chúng tôi, chúng tôi mở một luồng để đọc thông tin từ một tệp và chuyển nó tới tệp
5. Chúng ta
6cần phải đóng cửa và giải phóng tài nguyên. Điều này phải được thực hiện trong mọi trường hợp: không quan trọng chương trình có hoạt động như mong đợi hay đưa ra một ngoại lệ hay không. Thật thuận tiện để làm điều này trong một khối
5:
7 Bây giờ chúng tôi hoàn toàn chắc chắn rằng chúng tôi đã xử lý các tài nguyên bị chiếm dụng, bất kể điều gì xảy ra khi chương trình đang chạy :) Đó không phải là tất cả những gì bạn cần biết về các trường hợp ngoại lệ. Xử lý lỗi là một chủ đề rất quan trọng trong lập trình: có nhiều bài viết viết về nó. Trong bài học tiếp theo chúng ta sẽ tìm hiểu có những loại ngoại lệ nào và cách tạo ngoại lệ của riêng bạn :) Hẹn gặp bạn ở đó! |