Bài đăng nổi bật


Java Programming

Exception Handling & Assertion

1.  Exception Handling

1.1  Introduction

Một ngoại lệ là một sự kiện bất thường phát sinh trong quá trình thực hiện chương trình và làm gián đoạn dòng chảy bình thường của chương trình. Bất thường xảy ra khi chương trình của bạn đang chạy. Ví dụ: bạn có thể mong đợi người dùng nhập số nguyên, nhưng nhận được chuỗi văn bản; hoặc một lỗi I / O bất ngờ xuất hiện khi chạy. Điều thực sự quan trọng là "điều gì xảy ra sau khi bất thường xảy ra?" Nói cách khác, "làm thế nào các tình huống bất thường được xử lý bởi chương trình của bạn." Nếu những ngoại lệ này không được xử lý đúng cách, chương trình sẽ chấm dứt đột ngột và có thể gây ra hậu quả nghiêm trọng. Ví dụ: các kết nối mạng, kết nối cơ sở dữ liệu và tệp có thể vẫn được mở; cơ sở dữ liệu và hồ sơ tập tin có thể được để lại trong một trạng thái không nhất quán.
Java có một cơ chế tích hợp để xử lý các lỗi thời gian chạy, được gọi là xử lý ngoại lệ . Điều này là để đảm bảo rằng bạn có thể viết các chương trình mạnh mẽ cho các ứng dụng quan trọng.
Các ngôn ngữ lập trình cũ hơn như C có một số nhược điểm trong việc xử lý ngoại lệ. Ví dụ: giả sử lập trình viên muốn mở tệp để xử lý:
  1. Các lập trình viên không được thực hiện để nhận thức được các điều kiện đặc biệt. Ví dụ, tập tin được mở có thể không nhất thiết tồn tại. Do đó, lập trình viên không viết mã để kiểm tra xem tệp có tồn tại hay không trước khi mở tệp.
  2. Giả sử lập trình viên nhận thức được các điều kiện đặc biệt, anh ấy / cô ấy có thể quyết định hoàn thành logic chính trước và viết mã xử lý ngoại lệ sau - điều này "sau", thật không may, thường không bao giờ xảy ra. Nói cách khác, bạn không bắt buộc phải viết mã xử lý ngoại lệ cùng với logic chính.
  3. Giả sử lập trình viên quyết định viết mã xử lý ngoại lệ, mã xử lý ngoại lệ đan xen với logic chính trong nhiều câu lệnh if-other. Điều này làm cho logic chính khó theo dõi và toàn bộ chương trình khó đọc. Ví dụ,
  1. if (file exists) {
       open file;
       while (there is more records to be processed) {
          if (no IO errors) {
             process the file record
          } else {
             handle the errors
          }
       }
       if (file is opened) close the file;
    } else {
       report the file does not exist;
    }
Java khắc phục những nhược điểm này bằng cách xây dựng xử lý ngoại lệ thành ngôn ngữ thay vì để nó theo ý của các lập trình viên:
  1. Bạn sẽ được thông báo về các điều kiện đặc biệt có thể phát sinh khi gọi một phương thức - Các ngoại lệ được khai báo trong chữ ký của phương thức.
  2. Bạn bị buộc phải xử lý các trường hợp ngoại lệ trong khi viết logic chính và không thể để chúng như một suy nghĩ sau - Chương trình của bạn không thể được biên dịch mà không có mã xử lý ngoại lệ.
  3. Mã xử lý ngoại lệ được tách ra khỏi logic chính - Thông qua try-catch-finallycấu trúc.
Hãy xem xét ba điểm này chi tiết hơn.
Điểm 1: Ngoại lệ phải được khai báo
Ví dụ: giả sử bạn muốn sử dụng một java.util.Scannerđể thực hiện định dạng đầu vào từ một tệp đĩa. Chữ ký của hàm Scannertạo với một Fileđối số được đưa ra như sau:
public Scanner(File source) throws FileNotFoundException;
Chữ ký của phương thức thông báo cho các lập trình viên rằng một điều kiện đặc biệt "không tìm thấy tệp" có thể phát sinh. Bằng cách khai báo các ngoại lệ trong chữ ký của phương thức, các lập trình viên được tạo ra để nhận biết các điều kiện đặc biệt trong việc sử dụng phương thức.
Điểm 2: Ngoại lệ phải được xử lý
Nếu một phương thức khai báo một ngoại lệ trong chữ ký của nó, bạn không thể sử dụng phương thức này mà không xử lý ngoại lệ đó - bạn không thể biên dịch chương trình.
Ví dụ 1: Chương trình không xử lý ngoại lệ được khai báo, được nối lại trong lỗi biên dịch.
import java.util.Scanner;
import java.io.File;
public class ScannerFromFile {
   public static void main(String[] args) {
      Scanner in = new Scanner(new File("test.in"));
      // do something ...
   }
}
ScannerFromFile.java:5: unreported exception java.io.FileNotFoundException; must be caught or declared to be thrown
      Scanner in = new Scanner(new File("test.in"));
                   ^
Để sử dụng một phương thức khai báo một ngoại lệ trong chữ ký của nó, bạn PHẢI:
  1. cung cấp mã xử lý ngoại lệ trong cấu trúc " try-catch" hoặc " try-catch-finally" hoặc
  2. không xử lý ngoại lệ trong phương thức hiện tại, nhưng khai báo ngoại lệ sẽ được đưa lên ngăn xếp cuộc gọi để phương thức cấp cao hơn tiếp theo xử lý.
Ví dụ 2: Bắt ngoại lệ thông qua cấu trúc " try-catch" (hoặc " try-catch-finally").
import java.util.Scanner;
import java.io.File;
import java.io.FileNotFoundException;
public class ScannerFromFileWithCatch {
   public static void main(String[] args) {
      try {
         Scanner in = new Scanner(new File("test.in"));
         // do something if no exception ...
         // you main logic here in the try-block
      } catch (FileNotFoundException ex) { // error handling separated from the main logic
         ex.printStackTrace();             // print the stack trace
      }
   }
}
Nếu tập tin không thể được tìm thấy, ngoại lệ được bắt trong khối catch. Trong ví dụ này, trình xử lý lỗi chỉ cần in dấu vết ngăn xếp , cung cấp thông tin hữu ích để gỡ lỗi. Trong một số trường hợp, bạn có thể cần thực hiện một số thao tác dọn dẹp hoặc mở tệp khác thay thế. Xin lưu ý rằng logic chính trong khối thử được tách biệt với các mã xử lý lỗi trong khối catch.
Ví dụ 3: Bạn đã quyết định không xử lý ngoại lệ trong phương thức hiện tại, nhưng ném ngoại lệ lên ngăn xếp cuộc gọi cho phương thức cấp cao hơn tiếp theo để xử lý.
import java.util.Scanner;
import java.io.File;
import java.io.FileNotFoundException;
public class ScannerFromFileWithThrow {
   public static void main(String[] args) throws FileNotFoundException {  
                                 // to be handled by next higher-level method
      Scanner in = new Scanner(new File("test.in"));   
                                // this method may throw FileNotFoundException
      // main logic here ...
   }
}
Trong ví dụ này, bạn đã quyết định không xử lý việc FileNotFoundException ném theo phương thức Scanner(File) (với try-catch). Thay vào đó, phần gọi của Scanner(File)main()phương thức - khai báo chữ ký " throws FileNotFoundException", có nghĩa là ngoại lệ này sẽ được đưa lên ngăn xếp cuộc gọi, để xử lý phương thức cấp cao hơn tiếp theo. Trong trường hợp này, phương thức cấp cao hơn tiếp theo main()là JVM, đơn giản là chấm dứt chương trình và in dấu vết ngăn xếp.
Điểm 3: Logic chính được tách ra khỏi các mã xử lý ngoại lệ
Như được hiển thị trong Ví dụ 2, logic chính được chứa trong khối try, trong khi các mã xử lý ngoại lệ được giữ trong (các) khối catch tách biệt với logic chính. Điều này cải thiện đáng kể khả năng đọc của chương trình.
Ví dụ, một chương trình Java để xử lý tệp có thể như sau:
try {
   // Main logic here
   open file;
   process file;
   ......
} catch (FileNotFoundException ex) {   // Exception handlers below
   // Exception handler for "file not found"
} catch (IOException ex) {
   // Exception handler for "IO errors"
} finally {
  close file;      // always try to close the file
}

1.2  Method Call Stack

Một ứng dụng điển hình bao gồm nhiều cấp độ của các cuộc gọi phương thức, được quản lý bởi một ngăn xếp cuộc gọi phương thức . Một ngăn xếp là một hàng đợi vào trước ra trước . Trong ví dụ sau, main()phương thức gọi methodA()methodA()các lời gọi methodB()methodB()các lời gọi methodC().
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class MethodCallStackDemo {
   public static void main(String[] args) {
      System.out.println("Enter main()");
      methodA();
      System.out.println("Exit main()");
   }
 
   public static void methodA() {
      System.out.println("Enter methodA()");
      methodB();
      System.out.println("Exit methodA()");
   }
 
   public static void methodB() {
      System.out.println("Enter methodB()");
      methodC();
      System.out.println("Exit methodB()");
   }
 
   public static void methodC() {
      System.out.println("Enter methodC()");
      System.out.println("Exit methodC()");
   }
}
Enter main()
Enter methodA()
Enter methodB()
Enter methodC()
Exit methodC()
Exit methodB()
Exit methodA()
Exit main()
Exception_MethodCallStack.png
Nhìn từ đầu ra, chuỗi các sự kiện là:
  1. JVM gọi main().
  2. main()đẩy lên ngăn xếp cuộc gọi, trước khi gọi methodA().
  3. methodA()đẩy lên ngăn xếp cuộc gọi, trước khi gọi methodB().
  4. methodB()đẩy lên ngăn xếp cuộc gọi, trước khi gọi methodC().
  5. methodC() hoàn thành
  6. methodB() bật ra từ ngăn xếp cuộc gọi và hoàn thành.
  7. methodA() bật ra từ ngăn xếp cuộc gọi và hoàn thành.
  8. main()bật ra từ ngăn xếp cuộc gọi và hoàn thành. Chương trình thoát.
Giả sử rằng chúng tôi sửa đổi methodC()để thực hiện thao tác "chia cho 0", kích hoạt ArithmeticException:
public static void methodC() {
   System.out.println("Enter methodC()");
   System.out.println(1 / 0);  // divide-by-0 triggers an ArithmeticException
   System.out.println("Exit methodC()");
}
Thông báo ngoại lệ hiển thị rõ ràng dấu vết ngăn xếp cuộc gọi phương thức với các số dòng câu lệnh liên quan:
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)
        at MethodCallStackDemo.main(MethodCallStackDemo.java:4)
MethodC()kích hoạt một ArithmeticExceptionVì nó không xử lý ngoại lệ này, nó bật ra khỏi ngăn xếp cuộc gọi ngay lập tức. MethodB()cũng không xử lý ngoại lệ này và bật ra khỏi ngăn xếp cuộc gọi. Vì vậy, hiện methodA()và main()phương pháp. Các main()phương pháp họ sẽ trả lại cho JVM, mà đột ngột chấm dứt chương trình và in các cuộc gọi stack dấu vết, như thể hiện.

1.3  Exception & Call Stack

Exception_CallStack.png
Khi một ngoại lệ xảy ra bên trong một phương thức Java, phương thức này tạo ra một Exceptionđối tượng và chuyển Exceptionđối tượng cho JVM (theo thuật ngữ Java, phương thức " throw" an Exception). Đối Exceptiontượng chứa loại ngoại lệ và trạng thái của chương trình khi ngoại lệ xảy ra. JVM chịu trách nhiệm tìm một trình xử lý ngoại lệ để xử lýException đối tượng. Nó tìm kiếm ngược thông qua ngăn xếp cuộc gọi cho đến khi tìm thấy một trình xử lý ngoại lệ phù hợp cho lớp Exceptionđối tượng cụ thể đó (theo thuật ngữ Java, nó được gọi là " catch" the Exception). Nếu JVM không thể tìm thấy trình xử lý ngoại lệ phù hợp trong tất cả các phương thức trong ngăn xếp cuộc gọi, nó sẽ chấm dứt chương trình.
Quá trình này được minh họa như sau. Giả sử rằng methodD()gặp một điều kiện bất thường và ném a XxxExceptionvào JVM. JVM tìm kiếm ngược thông qua ngăn xếp cuộc gọi để xử lý ngoại lệ phù hợp. Nó tìm thấy methodA()có một XxxExceptiontrình xử lý và chuyển đối tượng ngoại lệ cho trình xử lý. Lưu ý rằng methodC()và methodB()được yêu cầu khai báo " throws XxxException"trong chữ ký phương thức của họ để biên dịch chương trình.

1.4  Exception Classes - ThrowableErrorException & RuntimeException

Hình dưới đây cho thấy sự phân cấp của các Exceptionlớp. Lớp cơ sở cho tất cả Exceptioncác đối tượng là java.lang.Throwable, cùng với hai lớp con java.lang.Exceptionvà java.lang.Error.
Exception_Classes.png
  • Các Errorlớp mô tả lỗi hệ thống nội bộ (ví dụ VirtualMachineErrorLinkageError) mà hiếm khi xảy ra. Nếu xảy ra lỗi như vậy, có rất ít điều bạn có thể làm và chương trình sẽ bị chấm dứt bởi thời gian chạy Java.
  • Các Exceptionlớp mô tả các lỗi gây ra bởi chương trình của bạn (ví dụ FileNotFoundExceptionIOException). Các lỗi này có thể bị chương trình của bạn bắt và xử lý (ví dụ: thực hiện một hành động thay thế hoặc thực hiện một lối thoát duyên dáng bằng cách đóng tất cả các tệp, kết nối mạng và cơ sở dữ liệu).
  • 1.5 Các trường hợp ngoại lệ được kiểm tra và không được kiểm tra

    Như được minh họa, các lớp con của Errorvà RuntimeExceptionđược gọi là ngoại lệ không được kiểm tra . Các ngoại lệ này không được kiểm tra bởi trình biên dịch, và do đó, không cần phải bị bắt hoặc khai báo sẽ bị ném trong chương trình của bạn. Điều này là do bạn không thể làm gì nhiều với những ngoại lệ này. Ví dụ: "chia cho 0" kích hoạt một ArithmeticException, chỉ mục mảng ngoài giới hạn kích hoạt một ArrayIndexOutOfBoundException,lỗi logic lập trình thực sự sẽ được sửa trong thời gian biên dịch, thay vì xử lý ngoại lệ thời gian chạy.
    Tất cả các ngoại lệ khác được gọi là ngoại lệ được kiểm tra . Chúng được kiểm tra bởi trình biên dịch và phải được bắt hoặc khai báo để ném.

    1.6 Hoạt động xử lý ngoại lệ

    Năm từ khóa được sử dụng trong xử lý ngoại lệ: trycatchfinallythrowsvà throw(lưu ý rằng có một sự khác biệt giữa throwvà throws).
    Xử lý ngoại lệ của Java bao gồm ba hoạt động:
    1. Tuyên bố ngoại lệ;
    2. Ném một ngoại lệ; 
    3. Bắt một ngoại lệ.
    Khai báo ngoại lệ
    Một phương thức Java phải khai báo trong chữ ký của nó các loại ngoại lệ được kiểm tra mà nó có thể "ném" khỏi cơ thể của nó, thông qua từ khóa "throws".
    Ví dụ: giả sử methodD()được xác định như sau:
public void methodD() throws XxxException, YyyException {
   // method body throw XxxException and YyyException
}
Chữ ký của phương thức chỉ ra rằng việc chạy methodD()có thể gặp hai ngoại lệ được kiểm tra: XxxExceptionvà YyyExceptionNói cách khác, một số điều kiện bất thường bên trong methodD()có thể kích hoạt XxxExceptionhoặc YyyException.
Ngoại lệ thuộc ErrorRuntimeExceptionvà họ lớp con không cần phải được công bố. Các ngoại lệ này được gọi là ngoại lệ không được kiểm tra vì chúng không được trình biên dịch kiểm tra.
Ném một ngoại lệ
Khi một hoạt động Java gặp phải một tình huống bất thường, phương thức chứa câu lệnh sai sẽ tạo ra một Exceptionđối tượng thích hợp và ném nó vào thời gian chạy Java thông qua câu lệnh " throw XxxException". Ví dụ,
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
   ...
}
Lưu ý rằng từ khóa để khai báo ngoại lệ trong chữ ký của phương thức là " throws" và từ khóa để ném một đối tượng ngoại lệ trong phần thân của phương thức là " throw".
Bắt ngoại lệ
Khi một phương thức đưa ra một ngoại lệ, JVM sẽ tìm kiếm ngược thông qua ngăn xếp cuộc gọi để xử lý ngoại lệ phù hợp. Mỗi trình xử lý ngoại lệ có thể xử lý một lớp ngoại lệ cụ thể. Một trình xử lý ngoại lệ xử lý một lớp cụ thể cũng có thể xử lý các lớp con của nó . Nếu không tìm thấy trình xử lý ngoại lệ nào trong ngăn xếp cuộc gọi, chương trình sẽ kết thúc.
Ví dụ: giả sử methodD()tuyên bố rằng nó có thể ném XxxExceptionvà YyyExceptiontrong chữ ký của nó, như sau:
public void methodD() throws XxxException, YyyException { ...... }
    Để sử dụng methodD()trong chương trình của bạn (nói trong methodC()), bạn có thể:
    1. Kết thúc lời gọi methodD()bên trong một thử bắt (hoặc thử bắt cuối cùng) như sau. Mỗi khối bắt có thể chứa một trình xử lý ngoại lệ cho một loại ngoại lệ 
  1. public void methodC() {  // no exception declared
       ......
       try {
          ......
          // uses methodD() which declares XxxException & YyyException
          methodD();
          ......
       } catch (XxxException ex) {
          // Exception handler for XxxException
          ......
       } catch (YyyException ex} {
          // Exception handler for YyyException
          ......
       } finally {   // optional
          // These codes always run, used for cleaning up
          ......
       }
       ......
    }
  2. Giả sử rằng methodC()ai gọi methodD()không muốn xử lý các ngoại lệ (thông qua một lần thử), nó có thể khai báo các ngoại lệ này để ném lên ngăn xếp cuộc gọi trong chữ ký của nó như sau:
    public void methodC() throws XxxException, YyyException {   // for next higher-level method to handle
       ...
       // uses methodD() which declares "throws XxxException, YyyException"
       methodD();   // no need for try-catch
       ...
    }
    Trong trường hợp này, nếu một XxxExceptionhoặc YyyExceptionbị ném bởi methodD(), JVM sẽ chấm dứt methodD() cũng như methodC()và chuyển đối tượng ngoại lệ lên ngăn xếp cuộc gọi cho người gọi của methodC().

1.7  try-catch-finally

The syntax of try-catch-finally is:
try {
   // main logic, uses methods that may throw Exceptions
   ......
} catch (Exception1 ex) {
   // error handler for Exception1
   ......
} catch (Exception2 ex) {
   // error handler for Exception1
   ......
} finally {   // finally is optional
   // clean up codes, always executed regardless of exceptions
   ......
}
Nếu không có ngoại lệ xảy ra trong quá trình chạy khối try, tất cả các khối catch được bỏ qua và khối finally sẽ được thực hiện sau khối tryNếu một trong các câu lệnh trong khối try đưa ra một ngoại lệ, thì bộ thực thi Java sẽ bỏ qua các câu lệnh còn lại trong khối try và bắt đầu tìm kiếm một trình xử lý ngoại lệ phù hợp. Nó phù hợp với loại ngoại lệ với từng khối catch liên tục . Nếu một khối try bắt lớp ngoại lệ đó hoặc bắt một siêu lớp của ngoại lệ đó, câu lệnh trong khối try đó sẽ được thực thi. Các câu lệnh trong khối finally sau đó được thực hiện sau khối bắt đó. Chương trình tiếp tục vào câu lệnh tiếp theo sau lần thử catch cuối cùng, trừ khi nó được kết thúc trước khi kết thúc hoặc phân nhánh.
Nếu không có kết quả nào catch phù hợp, ngoại lệ sẽ được chuyển lên ngăn xếp cuộc gọi. Phương thức hiện tại thực thi finally mệnh đề (nếu có) và bật ra khỏi ngăn xếp cuộc gọi. Người gọi làm theo các thủ tục tương tự để xử lý ngoại lệ.
Các khối finally là gần như chắc chắn được thực thi, bất kể có hay không ngoại lệ xảy ra (trừ khi JVM gặp phải một lỗi nghiêm trọng hay System.exit()được gọi là trong khối catch).
Example 1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import java.util.Scanner;
import java.io.File;
import java.io.FileNotFoundException;
public class TryCatchFinally {
   public static void main(String[] args) {
      try {         // main logic
         System.out.println("Start of the main logic");
         System.out.println("Try opening a file ...");
         Scanner in = new Scanner(new File("test.in"));
         System.out.println("File Found, processing the file ...");
         System.out.println("End of the main logic");
      } catch (FileNotFoundException ex) {    // error handling separated from the main logic
         System.out.println("File Not Found caught ...");
      } finally {   // always run regardless of exception status
         System.out.println("finally-block runs regardless of the state of exception");
      }
      // after the try-catch-finally
      System.out.println("After try-catch-finally, life goes on...");
   }
}
This is the output when the FileNotFoundException triggered:
Start of the main logic
Try opening a file ...
File Not Found caught ...
finally-block runs regardless of the state of exception
After try-catch-finally, life goes on...
This is the output when no exception triggered:
Start of the main logic
Try opening a file ...
File Found, processing the file ...
End of the main logic
finally-block runs regardless of the state of exception
After try-catch-finally, life goes on...
Example 2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class MethodCallStackDemo {
   public static void main(String[] args) {
      System.out.println("Enter main()");
      methodA();
      System.out.println("Exit main()");
   }
 
   public static void methodA() {
      System.out.println("Enter methodA()");
      try {
         System.out.println(1 / 0);
            // A divide-by-0 triggers an ArithmeticException - an unchecked exception
            // This method does not catch ArithmeticException
            // It runs the "finally" and popped off the call stack
      } finally {
         System.out.println("finally in methodA()");
      }
      System.out.println("Exit methodA()");
   }
}
Enter main()
Enter methodA()
finally in methodA()
Exception in thread "main" java.lang.ArithmeticException: / by zero
        at MethodCallStackDemo.methodA(MethodCallStackDemo.java:11)
        at MethodCallStackDemo.main(MethodCallStackDemo.java:4)
try-catch-finally
  • Một khối try phải được kèm theo ít nhất một khối catch hoặc khối finally.
  • Bạn có thể có nhiều khối catchMỗi khối bắt chỉ catch được một loại ngoại lệ.
  • Một khối catch yêu cầu một đối số, đó là một throwable đối tượng (nghĩa là một lớp con của java.lang.Throwable), như sau:
  • catch (AThrowableSubClass aThrowableObject) {
       // exception handling codes
    }
  • Bạn có thể sử dụng các phương thức sau để lấy loại ngoại lệ và trạng thái của chương trình từ Throwableđối tượng:
    • printStackTrace(): In này Throwablevà theo dõi ngăn xếp cuộc gọi của nó vào luồng lỗi tiêu chuẩn System.errDòng đầu tiên của kết quả đầu ra chứa kết quả toString()và các dòng còn lại là dấu vết ngăn xếp. Đây là xử lý phổ biến nhất, nếu không có gì tốt hơn mà bạn có thể làm. Ví dụ,
      • Bạn cũng có thể sử dụng printStackTrace(PrintStream s)hoặc printStackTrace(PrintWriter s).
      • getMessage(): Trả về messagechỉ định nếu đối tượng được xây dựng bằng cách sử dụng hàm tạo Throwable(String message).
      • toString(): Trả về một mô tả ngắn về Throwableđối tượng này , bao gồm tên của lớp, dấu hai chấm ':'và một thông báo từ getMessage().
    • Một khối bắt bắt một lớp ngoại lệ cụ thể cũng có thể bắt các lớp con của nó Do đó, catch(Exception ex) {...}bắt tất cả các loại ngoại lệ. Tuy nhiên, đây không phải là một thực tiễn tốt vì trình xử lý ngoại lệ quá chung chung có thể vô tình bắt được một số ngoại lệ của các lớp con mà nó không có ý định.
    • Thứ tự của các khối bắt là quan trọng. Một lớp con phải được bắt (và đặt ở phía trước) trước khi siêu lớp của nó. Nếu không, bạn nhận được một lỗi biên dịch "ngoại lệ XxxExceptionđã bị bắt".
    • Khối cuối cùng có nghĩa là cho mã dọn dẹp, chẳng hạn như đóng tệp, kết nối cơ sở dữ liệu bất kể khối thử có thành công hay không. Khối cuối cùng luôn được thực thi (trừ khi khối bắt trước kết thúc trước phương thức hiện tại).
    • try {
         Scanner in = new Scanner(new File("test.in"));
         // process the file here
         ......
      } catch (FileNotFoundException ex) {
         ex.printStackTrace();
      }
Điều gì xảy ra nếu tôi thực sự không quan tâm đến các ngoại lệ
    Chắc chắn không nên dùng ngoài việc viết chương trình trò chơi. Nhưng để bỏ qua các thông báo lỗi biên dịch được kích hoạt bởi các phương thức khai báo các ngoại lệ không được kiểm tra, bạn có thể khai báo " throws Exception" trong main()(và các phương thức khác), như sau:
public static void main(String[] args) throws Exception {  // throws all subclass of Exception to JRE
   Scanner in = new Scanner(new File("test.in"));   // declares "throws FileNotFoundException"
   ......
   // other exceptions
}
Overriding and Overloading Methods
Một phương thức ghi đè phải có cùng danh sách đối số và kiểu trả về (hoặc lớp con của bản gốc từ JDK 1.5). Một phương thức nạp chồng phải có danh sách đối số khác nhau, nhưng nó có thể có bất kỳ kiểu trả về nào.
Một phương thức ghi đè không thể có quyền truy cập hạn chế hơn. Ví dụ: một phương thức có protectedquyền truy cập có thể bị ghi đè để có quyền truy cập được bảo vệ hoặc công khai nhưng không có privatequyền truy cập mặc định hoặc không có quyền truy cập mặc định. Điều này là do một phương thức ghi đè được coi là thay thế cho phương thức ban đầu của nó, do đó, nó không thể hạn chế hơn.
Phương thức ghi đè không thể khai báo các loại ngoại lệ không được khai báo trong bản gốc. Tuy nhiên, nó có thể khai báo các loại ngoại lệ giống như hoặc lớp con của bản gốc. Nó không cần phải khai báo tất cả các ngoại lệ như bản gốc của nó. Nó có thể ném ít ngoại lệ hơn bản gốc, nhưng không nhiều hơn.
Một phương thức nạp chồng phải được phân biệt bởi danh sách đối số của nó. Nó không thể được phân biệt bởi loại trả về, ngoại lệ và công cụ sửa đổi, đó là bất hợp pháp. Nó có thể có bất kỳ kiểu trả về, sửa đổi truy cập và ngoại lệ nào, miễn là nó có thể được phân biệt bởi danh sách đối số.

1.8  Common Exception Classes

ArrayIndexOutOfBoundException : được ném bởi JVM khi mã của bạn sử dụng một chỉ mục mảng, nằm ngoài giới hạn của mảng. Ví dụ,
int[] anArray = new int[3];
System.out.println(anArray[3]);
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 3
NullPulumException : được ném bởi JVM khi mã của bạn cố gắng sử dụng tham chiếu null trong đó yêu cầu tham chiếu đối tượng. Ví dụ,
String[] strs = new String[3];
System.out.println(strs[0].length());
Exception in thread "main" java.lang.NullPointerException
NumberFormatException : Ném theo chương trình (ví dụ: bởi Integer.parseInt()) khi một nỗ lực được thực hiện để chuyển đổi một chuỗi thành một kiểu số, nhưng chuỗi không có định dạng phù hợp. Ví dụ,
Integer.parseInt("abc");
Exception in thread "main" java.lang.NumberFormatException: For input string: "abc"
ClassCastException : được ném bởi JVM khi một nỗ lực được thực hiện để truyền tham chiếu đối tượng không thành công. Ví dụ,
Object o = new Object();
Integer i = (Integer)o;
Exception in thread "main" java.lang.ClassCastException: java.lang.Object cannot be cast to java.lang.Integer
IllegalArgumentException : ném theo chương trình để chỉ ra rằng một phương thức đã được thông qua một đối số bất hợp pháp hoặc không phù hợp. Bạn có thể sử dụng lại ngoại lệ này cho các phương thức của riêng bạn.
IllegalStateException : được lập trình ném khi một phương thức được gọi và chương trình không ở trạng thái thích hợp để phương thức đó thực hiện nhiệm vụ của nó. Điều này thường xảy ra khi một phương thức được gọi ra khỏi chuỗi hoặc có lẽ một phương thức chỉ được phép gọi một lần và một nỗ lực được thực hiện để gọi lại nó.
NoClassDefFoundError : được ném bởi JVM hoặc trình nạp lớp khi không thể tìm thấy định nghĩa của một lớp. Trước JDK 1.7, bạn sẽ thấy dấu vết ngăn xếp cuộc gọi ngoại lệ này nếu bạn cố chạy một lớp không tồn tại. JDK 1.7 đơn giản hóa thông báo lỗi thành "Lỗi: Không thể tìm hoặc tải xxx lớp chính".

1.9  Creating Your Own Exception Classes

Bạn nên cố gắng tái sử dụng các Exceptionlớp học được cung cấp trong JDK, ví dụ IndexOutOfBoundExceptionArithmeticExceptionIOException, và IllegalArugmentExceptionNhưng bạn luôn có thể tạo Exceptioncác lớp riêng của mình bằng cách mở rộng từ lớp Exceptionhoặc một trong các lớp con của nó.
Lưu ý rằng RuntimeExceptionvà các lớp con của nó không được trình biên dịch kiểm tra và không cần phải khai báo trong chữ ký của phương thức. Do đó, hãy cẩn thận sử dụng chúng, vì bạn sẽ không được thông báo và có thể không nhận thức được các ngoại lệ có thể xảy ra bằng cách sử dụng phương pháp đó (và do đó không có mã xử lý ngoại lệ phù hợp) - một thực hành kỹ thuật phần mềm tồi.
Example
1
2
3
4
5
6
// Create our own exception class by subclassing Exception. This is a checked exception
public class MyMagicException extends Exception {
   public MyMagicException(String message) {  //constructor
      super(message);
   }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class MyMagicExceptionTest {
   // This method "throw MyMagicException" in its body.
   // MyMagicException is checked and need to be declared in the method's signature 
   public static void magic(int number) throws MyMagicException {
      if (number == 8) {
         throw (new MyMagicException("you hit the magic number"));
      }
      System.out.println("hello");  // skip if exception triggered
   }
   
   public static void main(String[] args) {
      try {
         magic(9);   // does not trigger exception
         magic(8);   // trigger exception
      } catch (MyMagicException ex) {   // exception handler
         ex.printStackTrace();
      }
   }
}
The output is as follows:
hello
MyMagicException: you hit the magic number
        at MyMagicExceptionTest.magic(MyMagicExceptionTest.java:6)
        at MyMagicExceptionTest.main(MyMagicExceptionTest.java:14)

Post a Comment

أحدث أقدم