Chuỗi ngoại lệ trong Java là gì | Xử lý ngoại lệ

Feb 24 2023
Bạn hiểu gì về ngoại lệ chuỗi trong Java? Chuỗi ngoại lệ, hoặc gói ngoại lệ, là một kỹ thuật lập trình hướng đối tượng để xử lý các ngoại lệ. Chuỗi ngoại lệ xảy ra khi một ngoại lệ gây ra một ngoại lệ khác.

Bạn hiểu gì về ngoại lệ chuỗi trong Java?

Xâu chuỗi ngoại lệ , hoặc gói ngoại lệ , là một kỹ thuật lập trình hướng đối tượng để xử lý các ngoại lệ . Chuỗi ngoại lệ xảy ra khi một ngoại lệ gây ra một ngoại lệ khác. một ngoại lệ xích là một ngoại lệ được gây ra bởi một ngoại lệ khác. Các ngoại lệ trong chuỗi được liên kết sao cho ngoại lệ trước đó gây ra từng ngoại lệ trong chuỗi.

· Throwable Class
· Sử dụng Chained Exceptions
· Tại sao chained exceptions?
· Khó khăn với ngoại lệ xích
· Chương trình

Lớp có thể ném

Trình xây dựng của lớp Throwable Hỗ trợ ngoại lệ chuỗi trong java:

  1. Có thể ném (Nguyên nhân có thể ném) : - Trong đó nguyên nhân là ngoại lệ gây ra ngoại lệ hiện tại.
  2. Có thể ném (Thông điệp chuỗi, Nguyên nhân có thể ném) : - Trong đó msg là thông báo ngoại lệ và nguyên nhân là ngoại lệ gây ra ngoại lệ hiện tại.
  1. Phương thức getCause() : - Phương thức này trả về nguyên nhân thực sự của một ngoại lệ.
  2. Phương thức initCause(Nguyên nhân có thể ném) : - Phương thức này đặt nguyên nhân cho ngoại lệ gọi.

Thông thường, bạn muốn bắt một ngoại lệ và ném một ngoại lệ khác, nhưng vẫn giữ thông tin về ngoại lệ ban đầu — đây được gọi là chuỗi ngoại lệ. Ngoại lệ ban đầu là nguyên nhân của ngoại lệ thứ hai. Vì vậy, Chained Exceptions cho phép liên kết một ngoại lệ với một ngoại lệ khác, tức là một ngoại lệ mô tả nguyên nhân của một ngoại lệ khác.

Trước JDK 1.4, các lập trình viên phải viết mã của riêng họ để lưu giữ thông tin ngoại lệ ban đầu, nhưng bây giờ tất cả các lớp con Throwable đều có tùy chọn lấy một đối tượng nguyên nhân trong hàm tạo của chúng. Nguyên nhân được dự định là ngoại lệ ban đầu và bằng cách chuyển nó vào, bạn duy trì dấu vết ngăn xếp trở lại nguồn gốc của nó, ngay cả khi bạn đang tạo và đưa ra một ngoại lệ mới.

Xâu chuỗi ngoại lệ còn được gọi là gói ngoại lệ vì chúng tôi thực hiện việc này bằng cách ném lại một ngoại lệ đã bắt được sau khi gói nó bên trong một ngoại lệ mới. Ngoại lệ ban đầu được lưu dưới dạng thuộc tính (chẳng hạn như nguyên nhân ) của ngoại lệ mới.

Luyện tập


  try {
     // creating an exception
     NullPointerException e = new NullPointerException("Actual cause");
     // wrapping the original exception in a new exception
     ArithmeticException exc = new ArithmeticException("Apearent cause",e);
     // throwing the exception
     throw exc;
  } catch(ArithmeticException e) {
     // display top level exception (aprearent cause)
     System.out.println("Caught: " + e);
     // Getting the actual cause of the exception
     System.out.println("Original cause: " + e.getCause());
  }
}

Caught: java.lang.ArithmeticException: Apearent cause
Original cause: java.lang.NullPointerException: Actual cause

Chẳng hạn, hãy xem xét một phương thức ném ArithmeticException do cố gắng chia cho 0 nhưng nguyên nhân thực sự của ngoại lệ là lỗi I/O khiến số chia bằng 0. Phương thức này sẽ ném ArithmeticException cho người gọi. Người gọi sẽ không biết về nguyên nhân thực sự của Ngoại lệ . Chained Exception được sử dụng trong những tình huống như vậy.

Thật thú vị khi lưu ý rằng các lớp con Throwable duy nhất cung cấp đối số nguyên nhân trong hàm tạo là ba lớp ngoại lệ cơ bản Error (được JVM sử dụng để báo cáo lỗi hệ thống), ExceptionRuntimeException . Nếu bạn muốn xâu chuỗi bất kỳ loại ngoại lệ nào khác, bạn thực hiện điều đó thông qua phương thức initCause() thay vì hàm tạo.

Ví dụ

Dưới đây là một ví dụ minh họa cơ chế xử lý các ngoại lệ chuỗi:

// Demonstrate exception chaining.
class ChainExcDemo {
     static void demoproc() {
         // create an exception
         NullPointerException e = new NullPointerException("top layer");
         // add a cause
         e.initCause(new ArithmeticException("cause"));
         throw e;
     }
public static void main(String args[]) {
        try {
             demoproc();
         } catch (NullPointerException e) {
             // display top level exception
               System.out.println("Caught: " + e);
              // display cause exception
              System.out.println("Original cause: " + e.getCause());
         }
    }
}

Caught: java.lang.NullPointerException: top layer
Original cause: java.lang.ArithmeticException: cause

Chuỗi ngoại lệ có thể được thực hiện ở bất kỳ độ sâu nào là cần thiết. Do đó, nguyên nhân ngoại lệ có thể, chính nó, có một nguyên nhân. Xin lưu ý rằng chuỗi ngoại lệ quá dài có thể cho thấy thiết kế kém.

Chuỗi ngoại lệ không phải là thứ mà mọi chương trình sẽ cần. Tuy nhiên, trong những trường hợp mà kiến ​​thức về nguyên nhân cơ bản là hữu ích, chúng sẽ đưa ra một giải pháp tao nhã.

Tại sao ngoại lệ chuỗi?

Chuỗi ngoại lệ cho phép bạn ánh xạ một loại ngoại lệ này sang loại ngoại lệ khác, để một phương thức có thể đưa ra các ngoại lệ được xác định ở cùng mức độ trừu tượng như chính phương thức đó mà không loại bỏ thông tin gỡ lỗi quan trọng.

Đó là, nếu bạn có một phương thức tải một số đối tượng từ cơ sở dữ liệu, bạn có thể muốn một số ResourceLoadException (liên quan chặt chẽ hơn đến mức độ trừu tượng của phương thức) thay vì SQLException cấp thấp ngay cả khi đó là nguồn ban đầu của vấn đề. Tuy nhiên, nếu bạn chỉ bắt SQLException ném một ResourceLoadException thay vào đó, bạn có thể mất thông tin gỡ lỗi quan trọng.

Do đó, xâu chuỗi các ngoại lệ là một giải pháp thay thế tốt. Bạn đưa ra một ngoại lệ “cấp cao”, rất phù hợp với phương thức cụ thể, nhưng xâu chuỗi nó với ngoại lệ đã gây ra nó.

Chúng ta cần xâu chuỗi các ngoại lệ để làm cho nhật ký có thể đọc được. Hãy viết hai ví dụ. Đầu tiên không xâu chuỗi các ngoại lệ và thứ hai, với các ngoại lệ xâu chuỗi. Sau đó, chúng tôi sẽ so sánh cách nhật ký hoạt động trong cả hai trường hợp.

Để bắt đầu, chúng tôi sẽ tạo một loạt Ngoại lệ:

class NoLeaveGrantedException extends Exception {

    public NoLeaveGrantedException(String message, Throwable cause) {
        super(message, cause);
    }

    public NoLeaveGrantedException(String message) {
        super(message);
    }
}

class TeamLeadUpsetException extends Exception {
    // Both Constructors
}

Hãy viết một chương trình ví dụ mà không xâu chuỗi các ngoại lệ tùy chỉnh của chúng ta.

public class MainClass {

    public void main(String[] args) throws Exception {
        getLeave();
    }

    void getLeave() throws NoLeaveGrantedException {
        try {
            howIsTeamLead();
        } catch (TeamLeadUpsetException e) {
            e.printStackTrace();
            throw new NoLeaveGrantedException("Leave not sanctioned.");
        }
    }

    void howIsTeamLead() throws TeamLeadUpsetException {
        throw new TeamLeadUpsetException("Team Lead Upset");
    }
}

com.baeldung.chainedexception.exceptions.TeamLeadUpsetException: 
  Team lead Upset
    at com.baeldung.chainedexception.exceptions.MainClass
      .howIsTeamLead(MainClass.java:46)
    at com.baeldung.chainedexception.exceptions.MainClass
      .getLeave(MainClass.java:34)
    at com.baeldung.chainedexception.exceptions.MainClass
      .main(MainClass.java:29)
Exception in thread "main" com.baeldung.chainedexception.exceptions.
  NoLeaveGrantedException: Leave not sanctioned.
    at com.baeldung.chainedexception.exceptions.MainClass
      .getLeave(MainClass.java:37)
    at com.baeldung.chainedexception.exceptions.MainClass
      .main(MainClass.java:29)

Tiếp theo, hãy viết một ví dụ về xâu chuỗi các ngoại lệ tùy chỉnh của chúng ta:

public class MainClass {
    public void main(String[] args) throws Exception {
        getLeave();
    }

    public getLeave() throws NoLeaveGrantedException {
        try {
            howIsTeamLead();
        } catch (TeamLeadUpsetException e) {
             throw new NoLeaveGrantedException("Leave not sanctioned.", e);
        }
    }

    public void howIsTeamLead() throws TeamLeadUpsetException {
        throw new TeamLeadUpsetException("Team lead Upset.");
    }
}

Exception in thread "main" com.baeldung.chainedexception.exceptions
  .NoLeaveGrantedException: Leave not sanctioned. 
    at com.baeldung.chainedexception.exceptions.MainClass
      .getLeave(MainClass.java:36) 
    at com.baeldung.chainedexception.exceptions.MainClass
      .main(MainClass.java:29) 
Caused by: com.baeldung.chainedexception.exceptions
  .TeamLeadUpsetException: Team lead Upset.
    at com.baeldung.chainedexception.exceptions.MainClass
  .howIsTeamLead(MainClass.java:44) 
    at com.baeldung.chainedexception.exceptions.MainClass
  .getLeave(MainClass.java:34) 
    ... 1 more

Khó khăn với ngoại lệ xích

Chúng ta có thể sử dụng chuỗi ngoại lệ trong các tình huống mà một ngoại lệ khác gây ra một ngoại lệ. Tuy nhiên, điều quan trọng cần lưu ý là chuỗi có thể làm cho mã của chúng ta khó đọc và khó hiểu hơn. Do đó, chúng ta nên sử dụng chuỗi ngoại lệ một cách tiết kiệm và chỉ khi cần thiết.

Nếu chúng tôi sử dụng các ngoại lệ theo chuỗi, thì nên ghi lại chuỗi trong mã của chúng tôi. Nó sẽ giúp những người khác hiểu mã của chúng tôi và giúp gỡ lỗi dễ dàng hơn nếu xảy ra lỗi.

Chương trình

//: exceptions/DynamicFields.java
// A Class that dynamically adds fields to itself. 
// Demonstrates exception chaining. 
import static net.mindview.util.Print.*; 
class DynamicFieldsException extends Exception {} 
public class DynamicFields { 
   private Object[][] fields; 
   public DynamicFields(int initialSize) { 
     fields = new Object[initialSize][2]; 
     for(int i = 0; i < initialSize; i++) 
     fields[i] = new Object[] { null, null }; 
   } 

 public String toString() { 
   StringBuilder result = new StringBuilder(); 
   for(Object[] obj : fields) { 
     result.append(obj[0]); 
     result.append(": "); 
     result.append(obj[1]); 
     result.append("\n");
   } 
   return result.toString(); 
 } 

//...................hasField(String id)........................//
 private int hasField(String id) { 
   for(int i = 0; i < fields.length; i++) 
     if(id.equals(fields[i][0])) 
       return i; 
   return -1; 
  } 

//......................getFieldNumber(String id)....................//
 private int getFieldNumber(String id) throws NoSuchFieldException { 
   int fieldNum = hasField(id); 
   if(fieldNum == -1) 
     throw new NoSuchFieldException(); 
   return fieldNum; 
 }

 //........................makeField(String id).....................//
 private int makeField(String id) { 
   for(int i = 0; i < fields.length; i++) 
     if(fields[i][0] == null) { 
       fields[i][0] = id; 
       return i; 
     } 
   // No empty fields. Add one: 
   Object[][] tmp = new Object[fields.length + 1][2]; 
   for(int i = 0; i < fields.length; i++) 
     tmp[i] = fields[i]; 
   for(int i = fields.length; i < tmp.length; i++) 
     tmp[i] = new Object[] { null, null }; 
   fields = tmp; 
   // Recursive call with expanded fields: 
   return makeField(id); 
 } 

//.............................getField(String id).......................//
 public Object getField(String id) throws NoSuchFieldException { 
   return fields[getFieldNumber(id)][1]; 
 } 

//.........................setField(String id, Object value)..........................
 public Object setField(String id, Object value) throws DynamicFieldsException { 
   if(value == null) { 
     // Most exceptions don’t have a "cause" constructor. 
     // In these cases you must use initCause(), 
     // available in all Throwable subclasses. 
     DynamicFieldsException dfe = new DynamicFieldsException(); 
     dfe.initCause(new NullPointerException()); 
     throw dfe; 
   } 
   int fieldNumber = hasField(id); 
   if(fieldNumber == -1) 
   fieldNumber = makeField(id); 
   Object result = null; 
   try { 
     result = getField(id); // Get old value 
   } catch(NoSuchFieldException e) { 
     // Use constructor that takes "cause": 
     throw new RuntimeException(e); 
    } 
   fields[fieldNumber][1] = value; 
   return result; 
 } 

// ......................main...................//
 public static void main(String[] args) { 
   DynamicFields df = new DynamicFields(3); 
   print(df); 
   try { 
     df.setField("d", "A value for d"); 
     df.setField("number", 47); 
     df.setField("number2", 48); 
     print(df); 
     df.setField("d", "A new value for d"); 
     df.setField("number3", 11); 
     print("df: " + df); 
     print("df.getField(\"d\") : " + df.getField("d")); 
     Object field = df.setField("d", null); // Exception 
   } catch(NoSuchFieldException e) { 
     e.printStackTrace(System.out); 
   } catch(DynamicFieldsException e) { 
     e.printStackTrace(System.out); 
   } 
 } 
}

null: null 
null: null 
null: null 

d: A value for d 
number: 47 
number2: 48 

df: d: A new value for d 
number: 47 
number2: 48 
number3: 11 

df.getField("d") : A new value for d 
DynamicFieldsException 
   at DynamicFields.setField(DynamicFields.java:64) 
   at DynamicFields.main(DynamicFields.java:94) 
Caused by: java.lang.NullPointerException 
   at DynamicFields.setField(DynamicFields.java:66) 
   ... 1 more 
*///:~

Là một giá trị trả về, setField() cũng tìm nạp giá trị cũ tại vị trí trường đó bằng cách sử dụng getField() , điều này có thể tạo ra NoSuchFieldException . Nếu lập trình viên máy khách gọi getField( ) thì họ chịu trách nhiệm xử lý NoSuchFieldException , nhưng nếu ngoại lệ này được ném vào bên trong setField() , thì đó là lỗi lập trình, vì vậy NoSuchFieldException được chuyển đổi thành RuntimeException bằng cách sử dụng hàm tạo lấy đối số nguyên nhân.

Bạn sẽ nhận thấy rằng toString( ) sử dụng StringBuilder để tạo kết quả của nó. Bạn sẽ tìm hiểu thêm về StringBuilder trong chương Strings, nhưng nói chung bạn sẽ muốn sử dụng nó bất cứ khi nào bạn viết một toString( ) liên quan đến vòng lặp, như trường hợp ở đây.

Học vui vẻ

Tham gia nhóm Mouad Oumous Java WhatsApp THAM GIA

Tham gia kênh Telegram Mouad Oumous THAM GIA

Hãy ủng hộ ấn phẩm của chúng tôi bằng cách theo dõi nó

Tối đa hóa tiềm năng của nhóm của bạn: Cách giữ cho nhóm phân phối phần mềm của bạn bận rộn Câu hỏi phỏng vấn dựa trên luồng Java8 thường gặp nhất — Phần cuối cùng Cách tạo một dự án Quarkus đơn giản với tích hợp cơ sở dữ liệu Viết lại một ngoại lệ trong Java | Xử lý ngoại lệ Tôi kiếm được 300 đô la bằng cách viết một cuốn sách trong 2 ngày - Đây là cách 10 cách đơn giản nhưng hiệu quả để cải thiện chất lượng cuộc sống của bạn