Java Bài 40: Exception Tập 4 – Throw, Throws & Custom Exception –

Rate this item :

Rating: 4.9/5. From 52 votes.

Please wait…

Chào mừng những bạn đã đến với bài học kinh nghiệm Java số 40, bài học kinh nghiệm về Exception ( phần tiếp theo ). Đây là bài học kinh nghiệm trong chuỗi bài về lập trình ngôn từ Java của Yellow Code Books .
Mình thông tin cho những bạn biết rằng đây là bài viết ở đầu cuối của mình về “ series ” Exception này. Như vậy nhìn lại tất cả chúng ta có những bài viết về Exception như sau .

– Làm quen với Exception – Đây là bài học mở đầu về Exception. Bài học giúp cho bạn bắt đầu tiếp cận với khái niệm LỖI trong Java. Từ đó hiểu rõ thế nào là Exception. Ngoài ra bài học còn giúp bạn phân biệt các loại Exception trong hệ thống Java nữa.
– Bắt (bẫy) Exception – Sau khi đã hiểu Exception là gì, thì bạn sẽ biết được cách thức bắt, hay bẫy Exception. Biết được kiến thức này, bạn có thể “bẻ luồng” logic của chương trình thông qua công cụ try catch. Việc bẻ luồng này nhằm mục đích vẫn đảm bảo sao cho ứng dụng của bạn vẫn luôn luôn “sống” dù cho nó có bị các LỖI nào tác động đến đi chăng nữa.
– Tìm hiểu sâu hơn về try catch – Khi bạn đã biết bắt Exception thông qua try catch, bạn sẽ được biết đến công dụng cao hơn nữa của công cụ này qua hai khái niệm Try Catch với Finally và Try Catch với Resource. Và mình cũng tranh thủ nói đến một số phương thức hữu ích của lớp Exception ở bài này, giúp bạn có thể hiểu rõ hơn về cách sử dụng công cụ này, và có thể vận dụng các phương thức đó cho bài học ngày hôm nay.

Bài thời điểm ngày hôm nay tất cả chúng ta đến với kỹ năng và kiến thức còn lại của Exception. Bài học sẽ hướng dẫn bạn đạt đến một cảnh giới trọn vẹn không sợ sệt gì với Exception cả. Thậm chí bạn hoàn toàn có thể “ tung hứng ” chúng, đẩy chúng về một nơi khác giải quyết và xử lý, và rồi bạn còn hoàn toàn có thể tự tạo ra một Exception cho riêng bạn nữa .
Nếu bạn thấy mê hoặc thì mời những bạn cùng đến với bài viết .

Throw – Tự Tung Ra Một Exception

Tất cả tất cả chúng ta cho đến giờ phút này đều hiểu rõ về Exception rồi. Nhưng tất cả chúng ta vẫn chỉ đang “ chờ đón ” Exception đến một cách thụ động mà thôi. Tức là tất cả chúng ta chỉ mới đang try và chờ đón ( thậm chí còn không mong ước ) catch ra lỗi .

Nhưng đôi khi trong thực tế, có một số trường hợp buộc chúng ta muốn nhanh chóng hơn tung ra một Exception. Mình dùng từ “tung ra” bởi chính ở nghĩa của từ throw: ném, quăng, tung. Exception được tung ra này có thể là Checked hay Unchecked Exception. Và việc tung ra một Exception này là do chủ ý của bạn.

Cách sử dụng throw cũng khá là đơn giản. Bất cứ nơi nào đó bên trong một phương thức, hay một khối lệnh, mà bạn muốn tung ra Exception, thì cứ dùng throw như với bài thực hành sau.

Thực Hành Sử Dụng Throw Để Tung Ra Một Exception

Chúng ta mở màn với việc phong cách thiết kế một chương trình được cho phép người dùng nhập vào tuổi của nhân viên cấp dưới, rồi in ra tuổi vừa mới nhập .
Yêu cầu của chương trình tưởng như vô cùng đơn thuần, thế nhưng như bạn biết, nếu không khéo, ứng dụng hoàn toàn có thể sẽ bị “ crash ” nếu như người dùng cố ý nhập vào một tài liệu tuổi không hợp lệ .
Với bài thực hành thực tế này tất cả chúng ta thử phong cách thiết kế riêng một phương pháp nhập tuổi như sau .

private static int nhapTuoiNhanVien() {
	Scanner scanner = new Scanner(System.in);
	System.out.print("Hãy nhập tuổi nhân viên: ");
	int tuoi = scanner.nextInt();
	return tuoi;
}

Bạn có thể thấy, phương thức nhapTuoiNhanVien() này có kiểu trả về là int (trả về tuổi vừa mới nhập vào). Và phương thức này còn được khai báo với từ khóa static, để lát nữa chúng ta có thể gọi đến từ phương thức main() (vốn là một phương thức static). Để hiểu rõ tại sao nhapTuoiNhanVien() lại phải khai báo static như vậy thì bạn có thể xem lại bài học này nhé.

Tiếp theo, ở phương thức main(), để đảm bảo ứng dụng không bị crash, chúng ta nên bao bọc lời gọi đến nhapTuoiNhanVien() vào bên trong một try catch hợp lý.

public static void main(String[] args) {
	try {
		int tuoi = nhapTuoiNhanVien();
		System.out.println("Tuổi đã nhập: " + tuoi);
	} catch (InputMismatchException e) {
		System.out.println("Tuổi nhập vào chưa hợp lệ. Lỗi: " + e.toString());
	}
}

Từ các bài học trước, bạn có thể dễ dàng đoán được rằng nếu nhập vào một tuổi 28 chẳng hạn, ứng dụng chạy tốt. Nhưng nếu người dùng cắc cớ nhập vào 28a, rất nhanh chóng ứng dụng sẽ thông báo lỗi ra cho người dùng ngay! Ôi mình yêu try catch biết bao nhiêu.

Exception - Bắt lỗi nhập vào không phải sốException - Bắt lỗi nhập vào không phải số

Thế nhưng nếu người dùng vẫn cắc cớ, nhập vào một tuổi mang giá trị âm, -5 chẳng hạn. Ứng dụng của chúng ta vẫn hơi “ngu” khi để lọt một sơ hở này.

Exception - Ví dụ để lọt một giá trị âm cho tuổiException - Ví dụ để lọt một giá trị âm cho tuổi

Làm gì có cái tuổi như thế này! Vậy để nâng cấp cho cái sự thông minh của ứng dụng, chúng ta sẽ tận dụng từ khóa throw. Như mình có nói, từ khóa này sẽ được tận dụng để chúng ta có thể tung ra một Exception bất cứ lúc nào chúng ta muốn, trong trường hợp này chúng ta nên tung ra một Exception khi mà người dùng nhập vào một tuổi âm. Bạn có thể xem mình code thêm vào phương thức nhapTuoiNhanVien() như sau.

private static int nhapTuoiNhanVien() {
	Scanner scanner = new Scanner(System.in);
	System.out.print("Hãy nhập tuổi nhân viên: ");
	int tuoi = scanner.nextInt();
	if (tuoi < 0) throw new InputMismatchException("tuổi không được nhỏ hơn 0.");
	return tuoi;
}

Bạn có thể thấy code bổ sung được tô sáng như trên. Mình giải thích tí xíu. Bên trong nhapTuoiNhanVien(), một khi kiểm tra thấy tuổi do người dùng nhập vào nhỏ hơn 0, ứng dụng sẽ tung ra (throw) bất kỳ một Exception nào, trong ví dụ này InputMismatchException được tung ra. Và bạn có thể hiểu bạn có thể tung ra bất cứ Exception nào bạn muốn. Bên cạnh việc tung ra một Exception, bạn có thể định nghĩa ra một thông báo kiểu String và truyền vào constructor của Exception như code trên nữa. Sau đó ở nơi gọi đến nhapTuoiNhanVien(), nếu nơi đó có try catch hợp lý, và gặp đúng điều kiện mà Exception được tung ra (tuổi nhập vào nhỏ hơn 0) thì như các bài học trước, logic của ứng dụng sẽ bị bẻ về khối catch.

Giờ đây, với sự tăng cấp này, nếu người dùng nhập vào một giá trị âm, bạn xem .

Exception - Tung ra một lỗiException - Tung ra một lỗi

Qua bài thực hành này thì bạn đã hiểu cách dùng throw rồi đúng không nào.

Trước khi qua mục tiếp theo, mình có một lưu ý rằng. Với ví dụ trên đây, khi tung ra một Exception, chúng ta chọn InputMismatchException. Đây là một Unchecked Exception. Do đó nơi gọi đến phương thức có tung Exception này không bị trình biên dịch báo lỗi bắt bạn phải thêm vào một try catch. Nhưng nếu bạn thử nghiệm với một Checked Exception xem, khi đó sẽ có nhiều điều đáng nói đến, và còn liên quan đến khái niệm throws nữa nên mình sẽ dành trường hợp này và nói chung ở mục kế tiếp sau.

Throws – Đẩy Exception Cho Nơi Khác Xử Lý

Bạn chú ý đừng nhầm lẫn nhé. Mục trên kia có nói đến việc tự ý tung ra một Exception thông qua từ khóa throw. Còn mục này đang nói đến throws (có thêm chữ s).

Khác với throw rằng bạn có thể sử dụng bên trong một phương thức hay một khối lệnh nào đó. Throws lại dùng ngay khi bạn khai báo một phương thức.

Throws được dùng khi bạn không muốn phải xây dựng try catch bên trong một phương thức nào đó, bạn “đẩy trách nhiệm” phải try catch này cho phương thức nào đó bên ngoài có gọi đến nó phải try catch giúp cho bạn.

Chúng ta cùng đến với các bài thực hành sau để hiểu rõ nhất về throws, và về cả việc vận dụng tốt với nhau giữa throwthrows nữa nhé.

Bài Thực Hành Số 1 : Sử Dụng Throw Để Tung Ra Một Checked Exception

Bạn đang thắc mắc tại sao đang nói về throws mà tiêu đề lại là thực hành với throw?

Bạn cứ thử qua các bước sau đây sẽ rõ thực hư. Mình sẽ lấy lại source code của bài thực hành trên kia. Trong phương thức nhapTuoiNhanVien(), chúng ta thử thay thế việc throw một InputMismatchException, bằng một Checked Exception nào đó xem sao. Mình sẽ thử với IOException. Code của phương thức này sẽ như sau.

private static int nhapTuoiNhanVien() {
	Scanner scanner = new Scanner(System.in);
	System.out.print("Hãy nhập tuổi nhân viên: ");
	int tuoi = scanner.nextInt();
	if (tuoi < 0) throw new IOException("tuổi không được nhỏ hơn 0.");
	return tuoi;
}

Sau khi tung ra một Checked Exception như trên, bạn thuận tiện nhận thấy mạng lưới hệ thống sẽ nhu yếu bạn phải làm một trong hai việc như sau .

Exception - Lựa chọn throwsException - Lựa chọn throws

Lựa chọn đầu tiên Add throws declaration sẽ khai báo một throws cho phương thức nhapTuoiNhanVien() này. Và như mình có nói, nếu phương thức đã khai báo throws, nó không cần phải try catch gì nữa, mà có thể đẩy trách nhiệm try catch này cho phương thức nào đó khác. Lựa chọn thứ hai Surround with try/catch thì bạn cũng biết từ các bài học trước rồi, nó sẽ giúp bao đoạn code này lại bằng khối lệnh try catch. Chúng ta nên chọn lựa chọn đầu tiên trong tình huống này, vừa đúng với yêu cầu bài học, vừa giúp dễ quản lý code nữa, vì bạn vừa tung ra một Exception mà lại try catch nó ngay, thì trông hơi bị dở người tí.

Với lựa chọn tiên phong, code của tất cả chúng ta sẽ như sau .

private static int nhapTuoiNhanVien() throws IOException {
	Scanner scanner = new Scanner(System.in);
	System.out.print("Hãy nhập tuổi nhân viên: ");
	int tuoi = scanner.nextInt();
	if (tuoi < 0) throw new IOException("tuổi không được nhỏ hơn 0.");
	return tuoi;
}

Với việc đẩy trách nhiệm phải try catch Checked Exception này, thì phương thức gọi đến nhapTuoiNhanVien() sẽ báo lỗi quen thuộc như sau.

Exception - Try Catch với Exception đã throwsException - Try Catch với Exception đã throws

Mình cá là các bạn đã biết cách làm gì rồi. Và đây, mình đã sửa lại theo ý các bạn, try catch với IOException.

public static void main(String[] args) {
	try {
		int tuoi = nhapTuoiNhanVien();
		System.out.println("Tuổi đã nhập: " + tuoi);
	} catch (IOException e) {
		System.out.println("Tuổi nhập vào chưa hợp lệ. Lỗi: " + e.toString());
	}
}

Bạn đã hiểu cách thức dùng đến throws để đẩy trách nhiệm try catch đi chỗ khác chưa nào.

Bài Thực Hành Số 2 : Sử Dụng Throws Để Đẩy Exception Đi Nơi Khác

Với bài thực hành trên kia có lẽ bạn cũng đã hiểu rõ cách dùng throws rồi. Tuy nhiên mình muốn các bạn làm thêm một ví dụ nữa để chỉ tập trung vào một mình throws thôi, không có throw xuất hiện trong này.

Chúng ta cùng quay lại kiến thiết xây dựng trường hợp try catch như bài thực hành thực tế này của bài trước. Chỉ khác một chỗ lần này tất cả chúng ta kiến thiết xây dựng riêng một phương pháp ghi file và trong phương pháp này không hề có một try catch nào cả .
Nào, mở màn với việc thiết kế xây dựng phương pháp ghi file ( code ghi file giống bài thực hành thực tế hôm trước thôi ) .

private static void ghiFile() {
	FileOutputStream outputStream;
	outputStream = new FileOutputStream("E://file.txt");
	outputStream.write(65);
	outputStream.close();
}

Chúng ta lại sẽ nhận được thông tin lỗi .

Exception - Báo lỗi chưa catchException - Báo lỗi chưa catch

Khi click vào icon cái bóng đèn bên trái, bạn đã biết là nên chọn tùy chọn này .

Exception - Chọn throwsException - Chọn throws

Và khi đã throws đầy đủ, phương thức này sẽ trông như thế này đây.

private static void ghiFile() throws FileNotFoundException, IOException {
	FileOutputStream outputStream;
	outputStream = new FileOutputStream("E://file.txt");
	outputStream.write(65);
	outputStream.close();
}

Cuối cùng, hiển nhiên ở nơi gọi đến phương thức này sẽ buộc phải try catch đầy đủ, hoặc throws tiếp cho phương thức nào đó kế tiếp.

public static void main(String[] args) {
	try {
		ghiFile();
	} catch (FileNotFoundException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	} catch (IOException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
}

Tự Tạo Một Exception Cho Riêng Bạn

Đến bước này đây, toàn bộ tất cả chúng ta đều đã đạt đến đỉnh điểm nhất của việc hiểu và sử dụng Exception rồi. Khi mà tổng thể tất cả chúng ta rồi đây sẽ tự tạo ra một Exception cho riêng tất cả chúng ta .

Việc xây dựng một Exception cho riêng chúng ta sẽ được gọi với một cái tên ngắn thôi: Custom Exception. Custom Exception thực chất cũng là một Exception nào đó, nó được đặt một cái tên hợp lý hơn trong hoàn cảnh ứng dụng của bạn (thay vì các cái tên Exception có sẵn mà bạn đã từng làm quen). Rồi thông qua Custom Exception, bạn có thể điều chỉnh việc thông báo lỗi, để mang đến một sự rõ ràng hơn cho ứng dụng.

Trước khi chính thức tạo một Custom Exception, tất cả chúng ta cùng điểm qua mấy ý quan trọng này trước .

– Tất cả các Custom Exception phải đều là con của lớp Throwable (bạn có thể xem lại kiến thức ở mục này nếu lỡ quên lớp Throwable là lớp gì nha).
– Nếu bạn muốn tạo ra một Custom Exception và muốn hệ thống báo lỗi khi không try catch cho nó, thì hãy tạo một lớp và kế thừa từ lớp Exception (có thể xem ở link trên để biết lớp Exception là lớp gì). Và dĩ nhiên lớp Exception là con của Throwable rồi nên nếu bạn làm theo ý này thì cũng sẽ thỏa ý tên kia thôi.
– Còn nếu bạn muốn tạo một Custom Exception và không cần hệ thống báo lỗi, bạn chỉ cần tạo một lớp và kế thừa từ lớp RuntimeException mà thôi (RuntimeException cũng là con của Throwable, và link trên cũng đã nói về lớp này).

Các ý trên đây cỏn con thôi đúng không nào. Chúng ta cùng đến với bài thực hành thực tế .

Thực Hành Tự Tạo Một Exception Kiểm Tra Tuổi

Chúng ta sẽ làm lại một chương trình nhập vào tuổi nhân viên và kiểm tra tuổi như trên kia. Nhưng thay vì dùng IOException để kiểm tra và tung ra một lỗi, trông chẳng ăn nhập gì, chúng ta sẽ nên tạo ra một Exception đúng nhất trong trường hợp của bài thực hành này. Custom Exception này sẽ thuộc loại Checked Exception. Và Custom Exception này còn có thể được điều chỉnh để có thể hiển thị lỗi rõ ràng hơn nữa.

Custom Exception mà chúng ta đang nói đến sẽ có cái tên AgeCheckingException. Bạn hãy tạo mới một lớp như sau.

public class AgeCheckingException extends Exception {

	public AgeCheckingException(String message) {
		super(message);
	}
	
	@Override
	public String getMessage() {
		return "Lỗi nhập vào một tuổi: " + super.getMessage();
	} 
}

Rất đơn giản. Custom Exception này sẽ kế thừa từ lớp Exception. Như vậy đây chính là một Checked Exception rồi. Bạn cũng nên xây dựng một constructor cho nó để nhận vào một message rồi truyền message này cho lớp cha thông qua lời gọi super(). Cuối cùng, Custom Exception này sẽ phủ quyết phương thức getMessage() để trả về nhiều thông tin hơn.

Chúng ta sẽ xây dựng phương thức nhapTuoiNhanVien() như sau. Chắc bạn đã hiểu và quen thuộc với các cách sử dụng Exception như sau rồi nên mình không giải thích thêm.

private static int nhapTuoiNhanVien() throws AgeCheckingException {
	Scanner scanner = new Scanner(System.in);
	System.out.print("Hãy nhập tuổi nhân viên: ");
	int tuoi = 0;
	try {
		tuoi = scanner.nextInt();
		if (tuoi < 0) throw new AgeCheckingException("tuổi không được nhỏ hơn 0.");
	} catch (InputMismatchException e) {
		throw new AgeCheckingException("tuổi phải là một số.");
	}
	return tuoi;
}

Và rồi ở phương thức main() nơi gọi đền nhapTuoiNhanVien() chúng ta sẽ try catch như sau.

public static void main(String[] args) {
	try {
		int tuoi = nhapTuoiNhanVien();
		System.out.println("Tuổi đã nhập: " + tuoi);
	} catch (AgeCheckingException e) {
		System.out.println(e.getMessage());
	}
}

Kết quả sẽ như sau nếu bạn cố ý nhập vào một số ít âm .

Custom Exception - Báo lỗi tuổi nhỏ hơn 0Custom Exception - Báo lỗi tuổi nhỏ hơn 0

Hay khi nhập 1 số ít có kèm ký tự .

Custom Exception - Báo lỗi tuổi không phải sốCustom Exception - Báo lỗi tuổi không phải số

Như vậy kết thúc bài học hôm nay chúng ta cũng đã xong luôn kiến thức thú vị về Exception. Bài sau sẽ là một kiến thức thú vị khác của Java, đó là kiến thức về Thread.

Cảm ơn bạn đã đọc các bài viết của Yellow Code Books. Bạn hãy ủng hộ blog bằng cách:

Đánh giá 5 sao bên dưới mỗi bài nếu thấy thích.
Comment bên dưới mỗi bài nếu có thắc mắc.
Để lại địa chỉ email của bạn ở thanh bên phải để nhận được thông báo sớm nhất khi có bài viết mới.
Chia sẻ các bài viết của Yellow Code Books đến nhiều người khác.

Bài Kế Tiếp

Chúng ta sẽ bước qua kiến thức cực kỳ thú vị về Thread trong Java.