Toàn tập về xử lý ngoại lệ Exception, trycatch trong Java

Xử lý ngoại lệ Exception, trycatch trong Java là việc mà khi lập trình chúng ta luôn luôn xử lý.Đặc biệt là làm dự án thì chắc chắn rằng các bạn cũng đã từng  gặp một số lỗi thông báo (error, exception) làm cho chương trình chúng ta không chạy những dòng code tiếp theo được nữa.  Với lập trình, giai đoạn đầu là việc mà chúng ta cần dành thời gian nhiều cho việc đọc lỗi, tìm cách và giải quyết vấn đề. Nó rất quan trọng để dẫn đến con đường và kinh nghiệm của  developer phát triển hơn. Vì vậy, trong bài viết này mang đậm chất chia sẻ hơn là những giáo trình toàn lý thuyết.

Trong bài viết này chúng ta sẽ cùng nhau làm rõ những vấn đề như sau:

Có rất nhiều hình ảnh về exception trog java nhưng tôi thấy hính sơ đồ này các bạn sẽ dễ hiểu :

mô hình exception trong java
mô hình exception trong java

Ngoại lệ là gì (Exception)? 

Exception là một tình trạng bất thường. Trong Java, Exception là một sự kiện mà phá vỡ luồng chuẩn của chương trình. Nó là một đối tượng mà được ném tại Runtime. Một exception (ngoại lệ) trong Java là một vấn đề xảy ra trong quá trình thực hiện của chương trình.

Ví dụ như : Một số không thể chia cho 0, trong trường hợp này nó chính là ngoại lệ. Hay input đầu vào là một số trong khi người dùng nhập vào một chuỗi dẫn đến khi ép kiểu nó sang thành kiểu số nguyên (int) thì sẽ bị exception.

Cần chú ý đến ở đây là trong lớp Exception lại có hai dạng là Unchecked và Checked.

Unchecked là quá trình compile nó sẽ không bắt lỗi, hệ thống không thể kiểm soát lỗi của chúng ta nên nó chỉ bắn ra lỗi khi trong thời điểm runtime. (Thằng này nằm trong nhánh RuntimeException như mô hình trên )

Đối với checked exception, việc kiểm tra được thực hiện ngay thời điểm compile time, một số IDE sẽ giúp chúng ta bằng cách hiển thị lỗi cú pháp nếu ta gọi một method throw ra bất kỳ checked exception nào mà không được catch. Một số checked exception tiêu biểu như: IOException, InterruptedException.

Đến đây mình nghĩ sẽ có một số bạn tự hỏi, compile và runtime error là cái gì? đọc không hiểu nó để làm gì? nhưng cứ biết là compile và runtime là gì đi.  Nhưng như vậy lại không được, phải hiểu mới đọc tiếp được chứ. Đến đây tôi xin định nghĩa hai khái niệm trên như sau:

  • Compile time: là quá trình mà những dòng code của chúng ta hay nói chung là chươn trình nó đang được biên dịch.

Lúc này nó sẽ kiểm tra xem chương trình của chúng ta có lỗi syntax gì không? (syntax là lỗi cú pháp, viết sai lệnh chẳng hạn vv), hiện tại thì như các bạn tháy các lỗi được compie ngay trên IDE hỗ trợ, và chúng ta biết đang sai ở đâu, dòng nào vv.

  • Run time là gì? là thời điểm chương trình của chúng ta chạy. (Theo tôi nó xảy ra như các bạn đang truy cập vào một method của object, nhưng object đó đang có giá trị null, và như các bạn biết một object null thì không thể sử dụng các method của class đó được)

OK, hy vọng các bạn đã hiểu những khái niệm trên và nó cũng khá trừu tượng khó hiểu. Như mình hy vọng các bạn sẽ hiểu ^^.

Error trong Java là gì ? 

Error: Nó không giống các exception, nhưng vấn đề xảy ra vượt quá tầm kiểm soát của lập trình viên hay người dùng. Error được bỏ qua trong code của bạn vì bạn hiếm khi có thể làm gì đó khi chương trình bị error. Ví dụ như OutOfMemoryError, VirtualMachineError, AssertionError, … Nó được bỏ qua trong quá trình Java biên dịch.

Theo tôi, thông thường error thường xảy ra khi các bạn config sai thư viện, hay library của chúng ta không đồng bộ. Nghĩa là có thể có hai thư viện khác nhau cùng được sử dụng, hay các library có version cũ không thích hợp với ứng dụng hiện tại. Hay trong trường hợp chúng ta sử dụng một class nào đó nhưng hiện tại class đó chưa có trong project để gọi vv…

Error là lỗi nghiệm trọng, dẫn đến cả hệ thống sẽ chết và không thể chạy được nữa.

Như sơ đồ mô hình trên về các lớp lỗi trong Java thì các bạn thấy rằng cả phần chúng ta vừa tìm hiểu là Error và Exception đều kế thừa từ lớp Throwable. Như vậy khi xử lý lỗi các bạn có thể hoàn toàn dùng lớp cha này, tuy nhiên theo tôi các bạn nên sử dụng các  class sau đó kết thừa từ các lớp Error hay Exception, không nên kế thừa lớp cha bởi vì khi lỗi xảy ra thì chương trình sẽ tìm đến các lớp con kiểm tra xem nó nằm trong trường hợp nào, Error hay Exception rồi sau đó xử lý tiếp, thứ hai nó bắn ra các lỗi chung không cụ thể nên khó fix lỗi hơn trog quá trình debug. Thông thường thì chúng ta xử lý lỗi các bạn sẽ kế thừa từ lớp Exception trong Java.

Vậy cách xử lý lỗi như thế nào?

Với error thì chúng ta khó mà xử lý được vì nó nằm tại thời điểm runtime, chúng ta khắc phục error qua kinh nghiệm . Chúng ta chưa biết trước được là chương trình chúng ta có lỗi, ví dụ như mình nói ở ví dụ trên là thư viện sử dụng cho ứng dụng có nhiều version khác nhau.

Error là lỗi không thể cứu chữa được, ví dụ tronh trường hợp: OutOfMemoryError, VirtualMachineError, AssertionError, vv

Một lỗi các bạn có thể thường gặp là OutOfMemoryError, đây là lỗi do  đầy bộ nhớ và chúng ta vẫn muốn nạp vào bộ nhớ.

Lỗi outofmemoryerror

Với Exception chúng ta xử lý như thế nào?

Chúng ta có bao nhiêu cách xử lý Exception trong java? 

Đây là một trong những câu hỏi phỏng vấn các bạn thường gặp.

Trả lời: Trong Java có hai cách xử lý Exception. Cách 1 dùng try/catch. Cách 2 dùng throws.

Cách sử dụng try/catch:

Đề bài áp dụng: Giả xử tôi có một bài sau: nhập vào một số yêu thích, sau đó in ra màn hình số vừa nhập.

Bài làm :

ví dụ về exception
ví dụ về exception

Nhìn bài toán trên đơn giản khi người dùng nhập theo ý của người lập trình, tuy nhiên khi một người dùng khó tính hay người ta cố tình nhập vào một input không phải là số mà là một chuỗi hay là số thực chẳn hạn lúc này chương trình của chúng ta sẽ có exception sau:

ví dụ về exception trong java
ví dụ về exception trong java

Như vậy chương trình đang có lỗi và tất nhiên không thể chạy những dòng code bên tiếp theo được.

Lúc này chương trình đang báo lỗi NumberFormatException, đây cũng là lỗi thường gặp khi lập trình với java đó là không đúng định dạng của số.

Vậy nếu để chương trình như vậy cũng được thôi, tuy nhiên trải nghiệm của người dùng không tốt cũng như chương trình của chúng ta sẽ không trơn tru.

Để khắc phục tôi sẽ code như sau:

Sử dụng try/cactch để khắc phục exception:

cách khắc phục exception
cách khắc phục exception

Vậy khi chạy lại chương trình thì chương trình của chúng ta không báo lỗi nữa mà sẽ in ra một thông báo để người dùng biết là lỗi gì, và người ta nhập lại.

Với cách trên tôi đang sử dụng hai từ khóa try/ catch:

Vậy khối try{} là nơi các bạn có thể nhận ra đường rằng nó có thể xảy ra lỗi hay nói cách khác là đoán là nó sẽ lỗi, thì các bạn đặt trong khối try{}. catch là khối nếu những dòng code trong khối try {} lỗi gì nó sẽ xử lý ngoại lệ exception trong khố catch.

mình sẽ tóm lại như sau:

try{
// ý nói rằng đây là đoạn code có thể xảy ra lỗi
} catch(Exception e) {
// ý nói rằng , nếu trên thằng try có lỗi thì chạy xuống thằng catch, 
//chính là tao, tao sẽ xử lý cho mày. tao là người giải quyết vấn đề cho mày. OK?
}

Tiếp theo, những dòng code trên các bạn tháy rằng, giả sử như tôi sẽ thêm điều kiện vào là nếu như người dùng nhập vào số âm thì in ra thông báo.

Trong Java không có Exception là xử lý những bài toán đó cả, chúng ta phải tự ném ra ngoại ra. Vậy ném ra như thế nào:

Ở đây tôi ném ra một ngoại lệ là SoAmException nên khi các bạn chạy và nhập vào số ấm, nó sẽ in ra thông báo.

Trong trường hợp này class SoAmException  là do chúng ta tự tạo ra, bằng cách extends từ lớp Exception, như sau:

Sử dụng Throw:

Ta sẽ giải quyết vấn đề trên như sau:

package exception;

public class SoAmException extends Exception {

	public SoAmException(String msg) {
		super(msg);
	}
}

Vì trường hợp trên các bạn tháy vì chúng ta xử lý ngoại lệ bằng  cách dùng throws trên method main nên khi chúng ta ném ra ngoại lệ SoAmException thì nó đã hứng ngoại lệ đó. Trong trường hợp các bạn xóa throws trên method main. Điều gì sẽ xảy ra::

package action;

import java.util.Scanner;

import exception.SoAmException;

public class Ex1 {
	public static void main(String[] args)  {
		Scanner input = new Scanner(System.in);
		System.out.println("Nhập vào một số yêu thích : ");
		int number = Integer.valueOf(input.nextLine());
		if (number < 0) {
			try {
				throw new SoAmException("Không được nhập vào số âm!");
			} catch (SoAmException e) {
				System.out.println(e.getMessage());
			}
		}
		System.out.println("Number love : " + number);
	}

Tất nhiên, nó sẽ báo lỗi vì các bạn đang ném ra ngoại lệ nhưng không xử lý ngoại lệ đó, vậy nên ở đây chúng ta lại quay về lại try/catch xử lý.

Vậy nên các bạn cần lưu ý: Đã ném ra ngoại lệ thì phải xử lý (hứng) ngoại lệ đó.

Sử dụng throws 

Ngoài cách xử lý trên, chúng ta có thể sử dụng từ khóa throws. Cách xử lý như sau:

Chúng ta sử dụng từ khóa throws trên các method, chú ý chỉ trên các method thôi nhé.

Chạy và thử nhập vào một chuỗi:

Sử dụng throws để xủ lý exception
Sử dụng throws để xủ lý exception

Và lúc này các bạn tháy rằng chúng ta vẫn được in ra lỗi như.  Vì chúng ta đang quăng ra loại lệ trên method main nên khi có lỗi thì nó in ra thôi.

Điểm khác nhau giữa try/catch và throws là gì?

Nó sẽ khác nhau ở điểm này:

Thay vì tôi viết các dòng code trong method main thì bây giờ sẽ viết trong một method:

Bây giờ thôi sẽ dùng throws ở trên method và bây giờ ở method nào gọi đến inputData thì bắt buộc nó phải xử ngoại lệ được quăng ra đó nếu không sẽ lỗi chương trình.

Cách xử lý chúng ta có thể dùng try/catch rồi throws trên method main tiếp tục cũng được. Không sao.

package action;

import java.util.Scanner;

import exception.SoAmException;

public class Ex1 {
	public static void main(String[] args)  {
		try {
			inputData();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public static void inputData() throws Exception {
		Scanner input = new Scanner(System.in);
		System.out.println("Nhập vào một số yêu thích : ");
		int number = Integer.valueOf(input.nextLine());
		System.out.println("Number love : " + number);
	}

Ý nói là: tôi viết method này, mà method này có thể xảy ra ngoại lệ, nhưng chưa biết thông báo ra cái gì hoặc xử lý gì tiếp theo, nhưng tôi biết chắc là nó có thể xảy ra ngoại lệ nên ông nào muốn sử dụng method của tôi thì phải xử lý ngoại lệ đó.

OK, Tiếp tục chúng ta vẫn còn từ khóa cuối cùng chưa được giải quyết là finally.

Finally là gì?

Khối finally trong là một khối được sử dụng để thực thi các phần code quan trọng như đóng kết nối, đóng stream, đóng file … Khối finally luôn luôn được thực thi dù cho exception có được xử lý hay không. Khối finally phải được theo sau bởi khối try hoặc khối catch.

Ghi chú: nếu bạn không xử lý ngoại lệ trước khi kết thúc chương trình, JVM sẽ thực thi khối finally (nếu bạn không có).

Cấu trúc : 

try{
// ý nói rằng đây là đoạn code có thể xảy ra lỗi
} catch(Exception e) {
// ý nói rằng , nếu trên thằng try có lỗi thì chạy xuống thằng catch, 
//chính là tao, tao sẽ xử lý cho mày. tao là người giải quyết vấn đề cho mày. OK?
} finally {
// đây là khối luôn luôn được thực thi khi chạy chương trình cho dù có lỗi hay không
}

Trở lại với bài thực hành trên, giả sử như tôi muốn cho dù chương trình lỗi hay không thì luôn luôn in ra là “kết thúc chương trình.”

Code:

package action;

import java.util.Scanner;

import exception.SoAmException;

public class Ex1 {
	public static void main(String[] args) {
		try {
			inputData();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			System.out.println("Kết thúc chương trình");
		}
	}

	public static void inputData() throws Exception {
		Scanner input = new Scanner(System.in);
		System.out.println("Nhập vào một số yêu thích : ");
		int number = Integer.valueOf(input.nextLine());
		System.out.println("Number love : " + number);
	}

kết quả:

Như các bạn thấy, mặc dù chương trình chúng ta nhập vào lỗi nhưng dòng chữ in ra trong khối finally luôn được thực hiện.




Như vậy, qua những nội dung trên mình hy vọng các bạn đã tự tin xử lý lỗi và áp dụng qua những bài toán của các bạn đang thực hiện. Cũng như hiểu sâu hơn về exception. Ở trên đã có những câu hỏi phỏng vấn thường gặp các bạn có thể tham khảo thêm loạt câu hỏi phỏng vấn cho Exception bên dưới:

Các câu hỏi phỏng vấn Exception thường gặp :

Một Exception là một vấn đề được tạo ra trong khi thực thi một chương trình. Các Exception được bắt bởi Handler được xác định cùng với lời gọi phương thức của Thread.

Đặc trưng của loại Exception này là một lỗi người dùng hoặc một vấn đề không thể biết trước bởi lập trình viên. Ví dụ, nếu một file đã được mở, nhưng không tìm thấy file đó, thì một Exception xuất hiện. Những Exception này không thể được bỏ qua một cách đơn giản tại thời điểm biên dịch.

Nó là một Exception mà có thể được tránh bởi lập trình viên. Trái ngược với Checked Exception, các Runtime Exception bị bỏ qua tại thời điểm biên dịch.

Lớp Exception có hai lớp con chính là: lớp IOException và lớp RuntimeException.

Nếu một phương thức không xử lý một Checked Exception, phương thức phải được khai báo với từ khóa throws. Từ khóa throws xuất hiện ở phần cuối một phương thức.

Một Exception có thể được ném, hoặc bởi được khởi tạo hoặc một Exception mà bạn vừa bắt, bởi sử dụng từ khóa throw.


Bài viết liên quan:

6 lý do thường gặp gây ra lỗi NumberFormatException

Chào các bạn! Để lại comment cho nó máu nhé! Thanks các bạn đã đọc bài viết.

 

0 0 đánh giá
Đánh giá bài viết
Theo dõi
Thông báo của
guest
0 Góp ý
Phản hồi nội tuyến
Xem tất cả bình luận
x