Bài 25: Cách khắc phục biệt lệ
1. Một số dạng lỗi thực thi thường gặp:
1.1. Lỗi tràn ngăn xếp
Là lỗi gặp phải khi phải thực thi quá nhiều phép toán, khi đó, các phép toán chưa được thực thi sẽ phải lưu trữ lại trong Stack. Và khi Stack đầy thì lỗi này sẽ xảy ra.
Ví dụ: Thực hiện viết hàm được gọi lại nhiều lần
public class TryCatch
{
static int i = 0;
// Hàm thực hiện in các số nguyên
public static int printNumber(int x)
{
i = i + 2;
System.out.println(i);
return i + printNumber(i + 2);
}
public static void main(String[] args)
{
// Thực thi hàm
TryCatch.printNumber(i);
}
}
Minh họa lỗi:

1.2. Lỗi tràn bộ nhớ
Là lỗi gặp phải khi lưu trữ quá nhiều giá trị tạm thời khi thực hiện phép toán, các giá trị ấy phải lưu trên bộ nhớ RAM. Và khi nó không đủ để lưu trữ, sẽ gây ra lỗi tràn bộ nhớ.
Ví dụ: Trong hàm main, ta khai báo 1 mảng arr với kiểu Integer nhưng với kích thước của mảng rất lớn
public class TryCatch
{
public static void main(String[] args)
{
// Khai báo mảng nhưng với kích thước rất lớn
Integer[] arr=new Integer[100000*100000];
}
}
Minh họa lỗi:

1.3. Lỗi liên kết
Là lỗi gặp phải khi thực hiện liên kết chương trình với một thư viện bên thứ 3 trong trường hợp đường dẫn đến thư viện đó sai hoặc đuôi mở rộng không đúng định dạng (không có file thưc viện)
2. Một số dạng lỗi logic thường gặp:
2.1. Giá trị tham chiếu của đối tượng bị null
Là lỗi khi sử dụng tham số hoặc thuộc tính có kiểu dữ liệu là kiểu có cấu trúc, nhưng tại thời điểm chạy chương trình thì không được khởi tạo hoặc vẫn mang giá trị null.
Ví dụ:
public class TryCatch
{
public static void main(String[] args)
{
String txt=null;
System.out.println("Ky tu dau tien: "+txt.charAt(0));
}
}
Rõ ràng là giá trị của biến txt (có kiểu dữ liệu là String) là null (không xác định rõ ràng giá trị ), vậy thì lấy đâu ra ký tự đầu tiên để hiển thị ra màn hình.
Minh họa lỗi:

2.2. Sai định dạng số
Là dạng lỗi gặp khi thực hiện chuyển đổi chuỗi ký tự sang định dạng số, nhưng trong đó có chứa cả ký tự không phải là số.
Ví dụ:
public class TryCatch
{
public static void main(String[] args)
{
String txt="1999H";
System.out.println("Ky tu dau tien: "+Integer.parseInt(txt));
}
}
Ta thấy trong biến txt có kiểu dữ liệu là chuỗi ký tự, biến này chứa ký tự ‘H’ không phải là kiểu số.
Minh họa lỗi:

2.3. Chỉ số vượt ngoài phạm vi truy cập
Là lỗi xảy ra khi thực hiện truy cập vào các biến mà chỉ số được truy cập nằm ngoài phạm vi của ô nhớ.
Ví dụ:
public class TryCatch
{
public static void main(String[] args)
{
String txt="1999H";
System.out.println("Ky tu cuoi cung: "+txt.charAt(7));
}
}
Mỗi ký tự trong chuỗi được đánh chỉ số, ký tự đầu tiên có chỉ số là 0, ký tự thứ hai có chỉ số là 1, cứ như vậy thì ký tự cuối cùng có chỉ số là (n-1) với n là độ dài của chuỗi đó.
Trong ví dụ trên thì độ dài chuỗi n = 5 nên i chạy từ 0 đến 4. Do đó khg có ký tự nào ứng với chỉ số tại 7.
Minh họa lỗi:

2.4. Phép chia cho 0
Đây là một trong những lỗi toán học vô nghĩa, những lỗi chia cho 0 xảy ra nhiều nhất nên mình lấy đây làm đại diện nhé.
Ví dụ:
public class TryCatch
{
public static void main(String[] args)
{
System.out.println("Ket qua phep chia la: "+9/0);
}
}
Minh họa lỗi:

2.5. Tệp tin không tồn tại, không được tìm thấy
Lỗi gặp phải khi đường dẫn trỏ đến tập tin không tồn tại hay không được tìm thấy.
Ví dụ:
import java.io.FileInputStream;
public class TryCatch
{
public static void main(String[] args)
{
String path="D://tuan.txt";
FileInputStream fin=new FileInputStream(path);
}
}
Thậm chí là khi ta gõ lệnh như trong ví dụ trên thì IDE cũng phải bắt chúng ta thêm khối try…catch hoặc ném ra một lỗi throw.
Nếu ta thực hiện đầy đủ một trong hai cách try…catch hoặc throw mà file vẫn không được tìm thấy thì ta sẽ nhận được lỗi với tên là: FileNotFoundException.
3. Cách khắc phụ ngoại lệ:
3.1. Khối lệnh try…catch
Rõ ràng là khi lập trình, ta cần phải lưu ý xét đầy đủ các trường hợp có thể xảy ra trong bài toán, điển hình là câu lệnh điều kiện if…else để giúp ta tránh tránh những trường hợp có lỗi không đáng có.
Ngoài ra, khố lệnh try…catch còn được sử dụng với những trường hợp lỗi nhưng không làm tắt hoăc dừng đột ngột chương trình. Nó phải được khai báo trong phương thức.
try{
// Tập các câu lệnh
} catch(Exception_class_Name exception){
// Xử lý ngoại lệ
}
Nếu trong quá trình thực thi các câu lệnh trong try xảy ra biệt lệ thì chương trình sẽ vào catch để thực thi lệnh trong đó.
Ví dụ:
public class TryCatch
{
public static void main(String[] args)
{
try{
int x=9/0;
System.out.println("Gia tri cua x la: "+x);
} catch(ArithmeticException ex){
System.out.println("Loi toan hoc: "+ex.getMessage());
} catch(Exception e){
System.out.println("Loi chuong trinh: "+e.getMessage());
}
}
}
Đối với mỗi khối try, có thể có không hoặc nhiều khối catch. Nhiều khối catch cho phép chúng ta xử lý từng ngoại lệ khác nhau.
Loại đối số của mỗi khối catch cho biết loại ngoại lệ có thể được xử lý bởi nó. Ta hiểu nôm na giống như các nhánh else vậy.
3.2. Câu lệnh thử tài nguyên trong Java
Câu lệnh try-with-resources là câu lệnh try có một hoặc nhiều khai báo tài nguyên.
Tài nguyên là một đối tượng được đóng ở cuối chương trình. Nó phải được khai báo và khởi tạo trong câu lệnh try.
Câu lệnh try-with-resources còn được gọi là quản lý tài nguyên tự động . Câu lệnh này tự động đóng tất cả các tài nguyên ở cuối câu lệnh.
Ví dụ:
import java.io.FileOutputStream;
public class TryCatch
{
public static void main(String[] args)
{
try (FileOutputStream fos = new FileOutputStream("/hihi.txt"))
{
String msg = "Welcome";
// converting string thanh mang byte
byte byteArray[] = msg.getBytes();
fos.write(byteArray);
System.out.println("Ghi vao file thanh cong!");
} catch (Exception exception) {
System.out.println(exception);
}
}
}
Ghi một chuỗi vào một file. Nó sử dụng một thể hiện của đối tượng FileOutputStream để ghi dữ liệu vào file. FileOutputStream là một tài nguyên phải được đóng lại sau khi chương trình kết thúc. Vì vậy, trong ví dụ này, việc đóng tài nguyên được thực hiện bằng chính nó.
