1. Các loại ngoại lệ

Tất cả các ngoại lệ được chia thành 4 loại, đây thực sự là các lớp kế thừa lẫn nhau.

Throwablelớp học

Lớp cơ sở cho tất cả các ngoại lệ là Throwablelớp. Lớp Throwablechứa mã ghi ngăn xếp cuộc gọi hiện tại (dấu vết ngăn xếp của phương thức hiện tại) vào một mảng. lát nữa chúng ta sẽ tìm hiểu dấu vết ngăn xếp là gì.

Toán tử ném chỉ có thể chấp nhận một đối tượng xuất phát từ Throwablelớp. Và mặc dù về mặt lý thuyết, bạn có thể viết mã như thế throw new Throwable();, nhưng không ai thường làm điều này. Mục đích chính của Throwablelớp là có một lớp cha duy nhất cho tất cả các ngoại lệ.

Errorlớp học

Lớp ngoại lệ tiếp theo là Errorlớp kế thừa trực tiếp Throwablelớp. Máy Java tạo các đối tượng của Errorlớp (và hậu duệ của nó) khi xảy ra sự cố nghiêm trọng . Ví dụ: trục trặc phần cứng, không đủ bộ nhớ, v.v.

Thông thường, với tư cách là một lập trình viên, bạn không thể làm gì trong tình huống xảy ra lỗi như vậy (loại lỗi nên Errorđược đưa ra) trong chương trình: những lỗi này quá nghiêm trọng. Tất cả những gì bạn có thể làm là thông báo cho người dùng rằng chương trình đang gặp sự cố và/hoặc ghi tất cả thông tin đã biết về lỗi vào nhật ký chương trình.

Exceptionlớp học

Các lớp ExceptionRuntimeExceptiondành cho các lỗi phổ biến xảy ra trong hoạt động của nhiều phương thức. Mục tiêu của mỗi ngoại lệ được ném là bị bắt bởi một catchkhối biết cách xử lý nó đúng cách.

Khi một phương thức không thể hoàn thành công việc của nó vì một lý do nào đó, nó sẽ ngay lập tức thông báo cho phương thức đang gọi bằng cách đưa ra một ngoại lệ thuộc loại thích hợp.

Nói cách khác, nếu một biến bằng null, phương thức sẽ đưa ra một NullPointerException. Nếu các đối số không chính xác được truyền cho phương thức, nó sẽ đưa ra một tệp InvalidArgumentException. Nếu phương thức vô tình chia cho 0, nó sẽ ném ra một ArithmeticException.

RuntimeExceptionlớp học

RuntimeExceptionslà tập con của Exceptions. Chúng tôi thậm chí có thể nói rằng đó RuntimeExceptionlà phiên bản nhẹ của các ngoại lệ thông thường ( Exception) — ít yêu cầu và hạn chế hơn đối với các ngoại lệ đó

Bạn sẽ học được sự khác biệt giữa ExceptionRuntimeExceptionsau này.


2. Throws: kiểm tra ngoại lệ

Tất cả các ngoại lệ Java thuộc 2 loại: được chọnkhông được chọn .

Tất cả các ngoại lệ kế thừa RuntimeExceptionhoặc Errorđược coi là ngoại lệ không được kiểm tra . Tất cả những người khác được kiểm tra ngoại lệ .

Quan trọng!

Hai mươi năm sau khi các ngoại lệ được kiểm tra được giới thiệu, hầu hết mọi lập trình viên Java đều coi đây là một lỗi. Trong các khung hiện đại phổ biến, 95% tất cả các ngoại lệ đều không được chọn. Ngôn ngữ C#, gần như sao chép chính xác Java, không thêm các ngoại lệ được kiểm tra .

Sự khác biệt chính giữa ngoại lệ được kiểm trakhông được kiểm tra là gì ?

Có các yêu cầu bổ sung áp dụng cho các trường hợp ngoại lệ được kiểm tra . Nói một cách đại khái, chúng là:

yêu cầu 1

Nếu một phương thức đưa ra một ngoại lệ được kiểm tra , nó phải chỉ ra loại ngoại lệ trong chữ ký của nó . Theo cách đó, mọi phương thức gọi nó đều biết rằng "ngoại lệ có ý nghĩa" này có thể xảy ra trong đó.

Chỉ ra các ngoại lệ được kiểm tra sau các tham số phương thức sau throwstừ khóa (không sử dụng throwtừ khóa do nhầm lẫn). Nó trông giống như thế này:

type method (parameters) throws exception

Ví dụ:

kiểm tra ngoại lệ ngoại lệ không được kiểm soát
public void calculate(int n) throws Exception
{
   if (n == 0)
      throw new Exception("n is null!");
}
public void calculate(n)
{
   if (n == 0)
      throw new RuntimeException("n is null!");
}

Trong ví dụ bên phải, mã của chúng tôi đưa ra một ngoại lệ không được kiểm tra — không cần thực hiện thêm hành động nào. Trong ví dụ bên trái, phương thức đưa ra một ngoại lệ được kiểm tra , vì vậy throwstừ khóa được thêm vào chữ ký phương thức cùng với loại ngoại lệ.

Nếu một phương thức muốn đưa ra nhiều ngoại lệ được kiểm tra , thì tất cả chúng phải được chỉ định sau throwstừ khóa, được phân tách bằng dấu phẩy. Thứ tự không quan trọng. Ví dụ:

public void calculate(int n) throws Exception, IOException
{
   if (n == 0)
      throw new Exception("n is null!");
   if (n == 1)
      throw new IOException("n is 1");
}

yêu cầu 2

Nếu bạn gọi một phương thức đã kiểm tra các ngoại lệ trong chữ ký của nó, thì bạn không thể bỏ qua thực tế là nó sẽ ném chúng.

Bạn phải nắm bắt tất cả các ngoại lệ như vậy bằng cách thêm catchcác khối cho từng ngoại lệ hoặc bằng cách thêm chúng vào một throwsmệnh đề cho phương thức của bạn.

Như thể chúng ta đang nói, " Những ngoại lệ này quan trọng đến mức chúng ta phải nắm bắt chúng. Và nếu chúng ta không biết cách xử lý chúng, thì bất kỳ ai có thể gọi phương thức của chúng ta đều phải được thông báo rằng những ngoại lệ như vậy có thể xảy ra trong đó.

Ví dụ:

Hãy tưởng tượng rằng chúng ta đang viết một phương thức để tạo ra một thế giới có con người sinh sống. Số lượng người ban đầu được thông qua dưới dạng đối số. Vì vậy, chúng ta cần thêm trường hợp ngoại lệ nếu có quá ít người.

tạo trái đất Ghi chú
public void createWorld(int n) throws EmptyWorldException, LonelyWorldException
{
   if (n == 0)
      throw new EmptyWorldException("There are no people!");
   if (n == 1)
      throw new LonelyWorldException ("There aren't enough people!");
   System.out.println("A wonderful world was created. Population: " + n);
}
Phương thức này có khả năng đưa ra hai ngoại lệ được kiểm tra :

  • EmptyWorldException
  • Thế giới cô đơnNgoại lệ

Cuộc gọi phương thức này có thể được xử lý theo 3 cách:

1. Không bắt bất kỳ trường hợp ngoại lệ nào

Điều này thường được thực hiện khi phương pháp không biết cách xử lý tình huống đúng cách.

Mã số Ghi chú
public void createPopulatedWorld(int population)
throws EmptyWorldException, LonelyWorldException
{
   createWorld(population);
}
Phương thức gọi không nắm bắt các ngoại lệ và phải thông báo cho người khác về chúng: nó thêm chúng vào throwsmệnh đề riêng của nó

2. Nắm bắt một số ngoại lệ

Chúng tôi xử lý các lỗi chúng tôi có thể xử lý. Nhưng những cái chúng tôi không hiểu, chúng tôi ném chúng vào phương thức gọi. Để làm điều này, chúng ta cần thêm tên của chúng vào mệnh đề throws:

Mã số Ghi chú
public void createNonEmptyWorld(int population)
throws EmptyWorldException
{
   try
   {
      createWorld(population);
   }
   catch (LonelyWorldException e)
   {
      e.printStackTrace();
   }
}
Người gọi chỉ bắt được một ngoại lệ được kiểm traLonelyWorldException. Ngoại lệ khác phải được thêm vào chữ ký của nó, chỉ ra nó sau throwstừ khóa

3. Bắt tất cả các ngoại lệ

Nếu phương thức không đưa ra ngoại lệ cho phương thức gọi, thì phương thức gọi luôn tự tin rằng mọi thứ đều hoạt động tốt. Và nó sẽ không thể thực hiện bất kỳ hành động nào để khắc phục các tình huống ngoại lệ.

Mã số Ghi chú
public void createAnyWorld(int population)
{
   try
   {
      createWorld(population);
   }
   catch (LonelyWorldException e)
   {
      e.printStackTrace();
   }
   catch (EmptyWorldException e)
   {
      e.printStackTrace();
   }
}
Tất cả các ngoại lệ đều bị bắt trong phương pháp này. Người gọi sẽ tự tin rằng mọi thứ diễn ra tốt đẹp.


3. Bắt nhiều ngoại lệ

Các lập trình viên thực sự ghét phải sao chép mã. Họ thậm chí còn đưa ra một nguyên tắc phát triển tương ứng — DRY : Don't Repeat Yourself. Nhưng khi xử lý các ngoại lệ, thường xảy ra trường hợp một trykhối được theo sau bởi một số catchkhối có cùng mã.

Hoặc có thể có 3 catchkhối có cùng mã và 2 catchkhối khác có mã giống hệt nhau. Đây là một tình huống tiêu chuẩn khi dự án của bạn xử lý các ngoại lệ một cách có trách nhiệm.

Bắt đầu với phiên bản 7, trong ngôn ngữ Java đã thêm khả năng chỉ định nhiều loại ngoại lệ trong một catchkhối. Nó trông đại khái như thế này:

try
{
   // Code where an exception might occur
}
catch (ExceptionType1 | ExceptionType2 | ExceptionType3 name)
{
   // Exception handling code
}

Bạn có thể có bao nhiêu catchkhối tùy thích. Tuy nhiên, một catchkhối duy nhất không thể chỉ định các ngoại lệ kế thừa lẫn nhau. Nói cách khác, bạn không thể viết catch ( Exception| RuntimeExceptione ), bởi vì RuntimeExceptionlớp kế thừa Exception.