Bài giảng Kỹ thuật phần mềm - Chương 4: Xử lý ngoại lệ - Phạm Duy Trung
Bạn đang xem 20 trang mẫu của tài liệu "Bài giảng Kỹ thuật phần mềm - Chương 4: Xử lý ngoại lệ - Phạm Duy Trung", để tải tài liệu gốc về máy bạn click vào nút DOWNLOAD ở trên
Tài liệu đính kèm:
- bai_giang_ky_thuat_phan_mem_chuong_4_xu_ly_ngoai_le_pham_duy.pdf
Nội dung text: Bài giảng Kỹ thuật phần mềm - Chương 4: Xử lý ngoại lệ - Phạm Duy Trung
- XỬ LÝ NGOẠI LỆ Exception handling PHẠM DUY TRUNG Bộ môn Kỹ thuật Phần mềm
- Nhắc lại bài cũ • Class và object • 4 chữ P : public, protected, package, private • static và non-static • Bao gói dữ liệu • Kế thừa • Lớp và phương thức trừu tượng • Đa hình: nạp chồng và ghi đè phương thức duytrung.tcu@gmail.com
- Nội dung bài học Tiếp cận với ngoại lệ Cây ngoại lệ trong java Cơ chế xử lý ngoại lệ trong java Các thao tác xử lý ngoại lệ trong java Ngoại lệ người dùng tự định nghĩa duytrung.tcu@gmail.com
- Nội dung bài học Tiếp cận với ngoại lệ Cây ngoại lệ trong java Cơ chế xử lý ngoại lệ trong java Các thao tác xử lý ngoại lệ trong java Ngoại lệ người dùng tự định nghĩa duytrung.tcu@gmail.com
- Đặt vấn đề • Khi viết một chương trình nói chung, lỗi có thể xảy ra với rất nhiều lý do Người lập trình: sử dụng đệ quy không hợp lý Người dùng nhập vào dữ liệu không hợp lệ Nguyên nhân phần cứng, hệ điều hành • Lỗi xảy ra rất đa dạng, khiến chương trình kết thúc đột ngột, cần nâng cao khả năng chịu lỗi của chương trình: Cho phép người dùng quay lại trạng thái an toàn trước đó để thực hiện các công việc khác Ghi lại dữ liệu hiện thời rồi mới kết thúc chương trình duytrung.tcu@gmail.com
- Xử lý thế nào khi gặp lỗi? • Yêu cầu: viết hàm để đọc vào nội dung của một file? readFile { open the file; determine its size; allocate that much memory; read the file into memory; close the file; } duytrung.tcu@gmail.com
- Xử lý thế nào khi gặp lỗi? • Để giải quyết các tình huống đã nêu, hàm readFile cần có thêm các đoạn code để phát hiện, thông báo và xử lý lỗi errorCodeType readFile { initialize errorCode = 0; open the file; } else { if (theFileIsOpen) { errorCode = -3; } determine the length of the file; close the file; if (gotTheFileLength) { if (theFileDidntClose && errorCode allocate that much memory; == 0) { if (gotEnoughMemory) { errorCode = -4; read the file into memory; } else { errorCode = errorCode and -4; if (readFailed) { } errorCode = -1; } else { } errorCode = -5; } else { } errorCode = -2; return errorCode; } } duytrung.tcu@gmail.com
- Xử lý thế nào khi gặp lỗi? Code phình ra, bố cục rắc Mất cấu trúc logic, khó Khó khăn khi bảo trì, chỉnh kiểm soát hành vi của rối, tính đọc hiểu kém chương trình sửa code chương trình errorCodeType readFile { initialize errorCode = 0; open the file; } else { if (theFileIsOpen) { errorCode = -3; } determine the length of the file; close the file; if (gotTheFileLength) { if (theFileDidntClose && errorCode allocate that much memory; == 0) { if (gotEnoughMemory) { errorCode = -4; read the file into memory; } else { errorCode = errorCode and -4; if (readFailed) { } errorCode = -1; } else { } errorCode = -5; } else { } errorCode = -2; return errorCode; } } duytrung.tcu@gmail.com
- Xử lý thế nào khi gặp lỗi? • Cần tách biệt phần logic của chương trình với xử lý lỗi readFile { catch (fileOpenFailed) { try doSomething; } { catch (sizeDeterminationFailed) { open the file; doSomething; determine the length of the file; } allocate that much memory; catch (memoryAllocationFailed) { read the file into memory; doSomething; } close the file; catch (readFailed) { } doSomething; } catch (fileCloseFailed) { doSomething; } } duytrung.tcu@gmail.com
- Nội dung bài học Tiếp cận với ngoại lệ Cây ngoại lệ trong java Cơ chế xử lý ngoại lệ trong java Các thao tác xử lý ngoại lệ trong java Ngoại lệ người dùng tự định nghĩa duytrung.tcu@gmail.com
- Tiếp cận với ngoại lệ • Ý tưởng: xây dựng nên các đối tượng đặc trưng cho những bất thường xảy ra khi thực thi chương trình • Tìm cách khái quát hóa, phân loại và nhóm các bất thường tương tự với nhau lại -> Keyword: Exception ~ ngoại lệ hay biệt lệ • Trong Java, lớp cao nhất xử lý lỗi là lớp Throwable, với 2 lớp con là Error và Exception • Trong Java, ngoại lệ được “ném” ra bởi các phương thức duytrung.tcu@gmail.com
- Các lớp ngoại lệ trong Java Mô tả các lỗi thuộc về chương trình của người dùng. Những lỗi này có thể được “bắt” lại và xử lý. Mô tả các lỗi hệ thống, rất hiếm khi xảy ra như lỗi JVM, lỗi tràn bộ đệm Người dùng gần như không thể làm gì hơn khi những lỗi này xảy ra và chương trình sẽ kết thúc bởi Java runtime duytrung.tcu@gmail.com
- Ví dụ 1 duytrung.tcu@gmail.com
- Ví dụ 2 duytrung.tcu@gmail.com
- Cấu trúc tên ngoại lệ trong Java + Exception IOException FileNotFoundException ArithmeticException ArrayIndexOutOfBoundsException NullPointerException IllegalArgumentException + Error StackOverflowError JVMError NoClassDefFoundError LinkageError AssertionError duytrung.tcu@gmail.com
- Nội dung bài học Tiếp cận với ngoại lệ Cây ngoại lệ trong java Cơ chế xử lý ngoại lệ trong java Các thao tác xử lý ngoại lệ trong java Ngoại lệ người dùng tự định nghĩa duytrung.tcu@gmail.com
- Ngăn xếp gọi hàm – Method call stack public class MethodCallStackDemo { public static void methodC() { public static void main(String[] args) { System.out.println("Enter methodC()"); System.out.println("Enter main()"); System.out.println("Exit methodC()"); methodA(); } } System.out.println("Exit main()"); } public static void methodA() { Enter main() Enter methodA() System.out.println("Enter methodA()"); Enter methodB() methodB(); Enter methodC() System.out.println("Exit methodA()"); Exit methodC() } Exit methodB() public static void methodB() { Exit methodA() System.out.println("Enter methodB()"); Exit main() methodC(); System.out.println("Exit methodB()"); } duytrung.tcu@gmail.com
- Truy vết gọi hàm – call stack trace public static void methodC() { System.out.println("Enter methodC()"); System.out.println(1 / 0); System.out.println("Exit methodC()"); } ArithmeticException / by zero Enter main() Enter methodA() Enter methodB() Enter methodC() Exception in thread "main" java.lang.ArithmeticException: / by zero at MethodCallStackDemo.methodC(MethodCallStackDemo.java:22) at MethodCallStackDemo.methodB(MethodCallStackDemo.java:16) at MethodCallStackDemo.methodA(MethodCallStackDemo.java:10) duytrung.tcu@gmail.com at MethodCallStackDemo.main(MethodCallStackDemo.java:4
- Một số phương thức thường dùng Throwable(): Khởi tạo một đối tượng mới với thông báo lỗi mặc định bằng null Throwable(String message): Khởi tạo một đối tượng mới và truyền vào thông báo lỗi Throwable(Stringmessage, Throwable cause):Khởi tạo một đối tượng mới, truyền vào thông báo lỗi và nguyên nhân public String getMessage() 1 Trả về thông báo lỗi của đối tượng ngoại lệ hiện tại. Thông báo này được truyền qua hàm khởi tạo kiểu Throwable public Throwable getCause() 2 Trả về nguyên nhân gây ra ngoại lệ bằng một đối tượng kiểu Throwable public String toString() 3 Trả về tên của ngoại lệ kèm thông báo lỗi public void printStackTrace() 4 In ra truy vết trên ngăn xếp gọi hàm public StackTraceElement [] getStackTrace() 5 Trả về mảng chứa các phần tử truy vết của ngăn xếp gọi hàm. Phần tử có chỉ số bằng 0 thể hiện đỉnh ngắn xếp, phần từ cuối cùng thể hiện đáy ngăn xếp duytrung.tcu@gmail.com
- Cơ chế xử lý ngoại lệ của Java • Khi xảy ra bất thường trong một phương thức Java, nó sẽ tạo ra một đối tượng Exception và gửi đến JVM -> “ném” (throw) ra môt ngoại lệ • JVM sẽ dò ngược từ đỉnh stack nhằm tìm ra “trình xử lý” ngoại lệ phù hợp -> “bắt” (catch) lấy ngoại lệ • Nếu không tìm được, JVM sẽ kết thúc chương trình ngay lập tức duytrung.tcu@gmail.com
- Phân loại ngoại lệ Unchecked Exception Checked Exception • Chỉ được xác định khi chạy chương trình • Được kiểm tra trong khi biên dịch chương trình (Runtime exceptions) (Compile time exceptions) • Không được kiểm tra bởi trình biên dịch • Bắt buộc phải có cách xử lý, hoặc khai báo -> Không yêu cầu phải “bắt” lỗi hay “ném” lỗi “ném” ngoại lệ đi tiếp, nếu không sẽ báo lỗi • Thường là các bug như lỗi logic hay sử dụng • Là các ngoại lệ có thể dự báo và khắc phục từ API không đúng cách, hay các lỗi hệ thống khi viết chương trình • Gồm lớp con RuntimeException và lớp • Là tất cả các lớp ngoại lệ còn lại Error duytrung.tcu@gmail.com
- Phân loại ngoại lệ duytrung.tcu@gmail.com
- Xác định kiểu ngoại lệ • Ta gọi đến một phương thức ở một lớp mà ta không viết • Làm thế nào để biết một phương thức có thể “ném” ngoại lệ hay không? và “ném” những ngoại lệ nào? -> tìm đến dòng khai báo phương thức, ở đó sẽ khai báo các ngoại lệ -> đọc tài liệu đặc tả phương thức duytrung.tcu@gmail.com
- Xác định kiểu ngoại lệ duytrung.tcu@gmail.com
- Nội dung bài học Tiếp cận với ngoại lệ Cây ngoại lệ trong java Cơ chế xử lý ngoại lệ trong java Các thao tác xử lý ngoại lệ trong java Ngoại lệ người dùng tự định nghĩa duytrung.tcu@gmail.com
- Các thao tác xử lý ngoại lệ trong Java • Xử lý ngoại lệ trong Java sử dụng 3 thao tác sau: 1. Đăng ký ngoại lệ cho phương thức 2. “Ném” ngoại lệ 3. “Bắt” ngoại lệ 5 từ khóa được sử dụng trong xử lý ngoại lệ: try, catch, finally, throws và throw duytrung.tcu@gmail.com
- Đăng ký ngoại lệ và từ khóa “throws” • Khi khai báo một phương thức Java, cần phải khai báo rõ các ngoại lệ mà phương thức có thể “ném” ra bằng từ khóa “throws” public void methodD() throws XxxException, YyyException { // method body throw XxxException and YyyException } -> methodD có thể phát sinh 2 ngoại lệ kiểu checked là XxxException và YyyException • Các ngoại lệ unchecked thì không cần khai báo duytrung.tcu@gmail.com
- “Ném” ngoại lệ và từ khóa “throw” • Trong phần thân hàm, khi gặp những điều kiện hay đoạn lệnh tiềm ẩn lỗi, xây dựng một đối tượng ngoại lệ phù hợp và chuyển cho JVM bằng từ khóa “throw” public void methodD() throws XxxException, YyyException { // method's signature // method's body // XxxException occurs if ( ) throw new XxxException( ); // construct an XxxException object and throw to JVM // YyyException occurs if ( ) throw new YyyException( ); // construct an YyyException object and throw to JVM } duytrung.tcu@gmail.com
- “Ném” ngoại lệ và từ khóa “throw” • Trong phần thân hàm, khi gặp những điều kiện hay đoạn lệnh tiềm ẩn lỗi, xây dựng một đối tượng ngoại lệ phù hợp và chuyển cho JVM bằng từ khóa “throw” public void methodD() throws XxxException, YyyException { // method's signature // method's body // XxxException occurs if ( ) throw new XxxException( ); // construct an XxxException object and throw to JVM // YyyException occurs if ( ) throw new YyyException( ); // construct an YyyException object and throw to JVM } duytrung.tcu@gmail.com
- “Ném” ngoại lệ và từ khóa “throw” public class Test { public static void main(String[] args) { tinhCanBacHai(-6); } static double tinhCanBacHai(double x) { if(x<0) throw new ArithmeticException("Không thể lấy căn số âm"); return Math.sqrt(x); } } duytrung.tcu@gmail.com
- “Bắt” ngoại lệ - Nguyên tắc “Bắt hoặc Né“ • Nguyên gốc theo tài liệu Oracle: Catch or Specify Requirement • Khi một đoạn code trong một phương thức phát sinh ngoại lệ, phương thức có thể xử lý theo một trong hai cách sau: 1. Bắt: Đưa code vào trong một khối try/catch và cung cấp trình xử lý ngoại lệ phù hợp với mỗi ngoại lệ 2. Né: Chỉ rõ ngoại lệ mà phương thức hiện tại sẽ ném ra tương tự như khai báo ngoại lệ bằng từ khóa throws, chứ không xử lý nó. Khi phát sinh ngoại lệ, JVM sẽ kết thúc phương thức hiện tại và chuyển ngoại lệ cấp cao hơn trong ngăn xếp gọi hàm. Hàm khởi tạo kiểu có đối số duytrung.tcu@gmail.com
- “Bắt” ngoại lệ - Nguyên tắc “Bắt hoặc Né“ duytrung.tcu@gmail.com
- “Bắt” ngoại lệ - Nguyên lý “Bắt hoặc Né” duytrung.tcu@gmail.com
- “Bắt” ngoại lệ - Nguyên lý “Bắt hoặc Né” 1 2 duytrung.tcu@gmail.com
- Khối try / catch • Để xử lý ngoại lệ được ném ra từ một đoạn mã, ta bọc đoạn mã đó trong một khối try / catch • Khối try chứa phần mã có thể phát sinh ngoại lệ • Khối catch chứa đoạn mã xử lý ngoại lệ (exception handler) trong trường hợp khối try phát sinh ngoại lệ • Cấu trúc try / catch chỉ có một khối try song có thể có nhiều hơn một khối catch để xử lý nhiều lớp ngoại lệ khác nhau duytrung.tcu@gmail.com
- Hoạt động của khối try/catch Trường hợp 1: Khi phương thức/ đoạn mã trong try thực hiện thành công, khối lệnh trong catch bị bỏ qua, lệnh đằng sau catch sẽ chạy duytrung.tcu@gmail.com
- Hoạt động của khối try / catch Khi phương thức/ đoạn mã trong try ném ra ngoại lệ và khối catch bắt được ngoại lệ đó. - Các lệnh còn lại trong khối try bị bỏ qua - Khối catch được thực hiện - Các lệnh sau khối catch được thực hiện duytrung.tcu@gmail.com
- Hoạt động của khối try / catch Khi phương thức/ đoạn mã trong try ném ra ngoại lệ mà khối catch không bắt được - Bỏ qua các lệnh còn lại trong phương thức - Trở về phương thức gọi đến phương thức hiện tại tìm trình xử lý hoặc dừng chương trình ra khỏi phương thức hiện tại duytrung.tcu@gmail.com
- Sử dụng nhiều catch duytrung.tcu@gmail.com
- Thứ tự các khối catch • Ngoại lệ cũng là đối tượng nên có tính đa hình: một khối catch bắt được ngoại lệ lớp cha thì cũng bắt được ngoại lệ lớp con • Khối catch dành cho ngoại lệ tổng quát hơn phải đặt sau khối catch cho ngoại lệ chuyên biệt hơn, nếu không Java sẽ báo lỗi public class FileNotFound_Demo { Throwable public static void main(String[] args){ File file = new File("E:\\file.txt"); try { FileReader fr = new FileReader(file); Error Exception } catch (FileNotFoundException ex) { System.out.println("File không tồn tại"); } catch (IOException ex) { IOException System.out.println("Có lỗi vào / ra"); } catch (Exception ex) { System.out.println("Có lỗi gì đó :("); } FileNotFoundException } } duytrung.tcu@gmail.com
- Khối finally – những việc thế nào cũng phải làm • Là khối tùy chọn, thêm vào khối try / catch • Để thêm những công việc phải làm bất kể ngoại lệ có xảy ra hay không, kể cả khi trong try catch có từ khóa return • Một cách dùng phổ biến là chứa code làm nhiệm vụ “dọn dẹp”: đóng file, đóng stream duytrung.tcu@gmail.com
- Ví dụ try/catch/finally import java.io.File; import java.io.FileReader; import java.io.IOException; public class ReadData_Demo { public static void main(String args[]) { FileReader fr = null; try { File file = new File("file.txt"); fr = new FileReader(file); char [] a = new char[50]; fr.read(a); // reads the content to the array for(char c : a) System.out.print(c); // prints the characters one by one } catch (IOException e) { e.printStackTrace(); }finally { try { Đã mở file thì phải đóng fr.close(); file lại, bất kể chạy được } catch (IOException ex) { ex.printStackTrace(); hay phát sinh ngoại lệ } } } } duytrung.tcu@gmail.com
- Sử dụng try-with-resources • Là cơ chế xử lý exception xuất hiện từ Java SE 7 • “Tài nguyên” (resource) là một đối tượng mà phải được đóng lại sau khi thao tác xong • Chỉ cần đối tượng thực thi interface java.lang.AutoCloseable, thì có thể sử dụng câu lệnh try-with-resource • Tài nguyên đươc khai báo ở câu lệnh try sẽ tự động đóng lại bất kể khối lệnh của try chạy bình thường hay gặp lỗi -> không cần khối finally để dọn dẹp trong trường hợp này duytrung.tcu@gmail.com
- Ví dụ try-with-resources import java.io.FileReader; import java.io.IOException; public class Try_withDemo { public static void main(String args[]) { try(FileReader fr = new FileReader("E://file.txt")) { char [] a = new char[50]; fr.read(a); // reads the contentto the array for(char c : a) System.out.print(c); // prints the characters one by one } catch (IOException e) { e.printStackTrace(); } } } duytrung.tcu@gmail.com
- Nội dung bài học Tiếp cận với ngoại lệ Cây ngoại lệ trong java Cơ chế xử lý ngoại lệ trong java Các thao tác xử lý ngoại lệ trong java Ngoại lệ người dùng tự định nghĩa duytrung.tcu@gmail.com
- Ngoại lệ người dùng định nghĩa • Yêu cầu của chương trình nhiều khi đòi hỏi người lập trình phải định nghĩa thêm các ngoại lệ mới • Để sử dụng được với cơ chế ngoại lệ thông thường, ngoại lệ tự định nghĩa cần kế thừa và chuyên biệt hóa các lớp ngoại lệ có sẵn trong Java -> đều là hậu duệ của lớp Throwable • Để viết một ngoại lệ kiểu checked, chỉ cần kế thừa lớp Exception • Để viết một ngoại lệ kiểu unchecked, phải kế thừa lớp RuntimeException duytrung.tcu@gmail.com
- Ngoại lệ người dùng định nghĩa public class Test { public static void main(String[] args) throws MyException { int a = 5; if(a>0) { MyException ex = new MyException("Có lỗi gì đó"); System.out.println(ex.getNotiStr()); throw ex; } } } class MyException extends Exception { private String notiStr = "Một dòng thông báo bình thường"; public MyException() { super("Đây là ngoại lệ của tôi"); } public MyException(String mess){ super("Đây là ngoại lệ của tôi"); notiStr = mess; } public String getNotiStr() { return notiStr; } } duytrung.tcu@gmail.com
- Kinh nghiệm sử dụng • Một ngoại lệ tự định nghĩa điển hình chỉ cần đưa ra 2 hàm khởi tạo, một không có đối số và một có đối số truyền vào là thông báo lỗi tùy chọn • Trong nhiều trường hợp chỉ cần một lớp con rỗng với tên lớp phù hợp là đủ • Khi kế thừa ngoại lệ, nên chọn lớp cha là một lớp có liên quan • Việc sử dụng ngoại lệ tự định nghĩa giúp tăng tính trong sáng của chương trình, tăng tính đọc hiểu của code duytrung.tcu@gmail.com
- Tổng kết bài học • Ngoại lệ trong Java là đối tượng, đều là hậu duệ của lớp Throwable • Trình biên dịch chỉ quan tâm đến các ngoại lệ được kiểm tra (checked exception) • Không bắt buộc phải xử lý các ngoại lệ kiểu unchecked • Đăng ký ngoại lệ khi khai báo phương thức sử dụng throws • Chủ động ném ngoại lệ sử dụng từ khóa throw • Sử dụng try/catch/finally để xử lý ngoại lệ, nếu không xử lý được thì đăng ký ngoại lệ để né, chuyển ngoại lệ cho cấp hàm cao hơn xử lý duytrung.tcu@gmail.com
- Bài tập về nhà Viết một chương trình minh họa việc rút tiền tại cây ATM - Tạo một lớp mô tả một tài khoản ngân hàng với thuộc tính số dư tài khoản ($) và 2 phương thức nạp tiền và rút tiền - Khi gọi phương thức nạp tiền và rút tiền, để người dùng nhập vào số tiền cần rút/nạp, chi phí rút/nạp bằng 1% số tiền. Hãy xử lý các ngoại lệ có thể phát sinh - Tự định nghĩa các ngoại lệ để bắt các tính huống sau: • Số tiền nhập vào là số âm • Số tiền nhập vào lớn hơn số dư tài khoản • Số tiền cần rút lớn hơn 300$/giao dịch duytrung.tcu@gmail.com