Sửa lỗi java.lang.exception và lỗi chưa có driver trong java năm 2024

Ngoại lệ là sự kiện không tránh khỏi, xuất hiện thường xuyên trong cuộc sống hàng ngày. Lấy ví dụ, khi nấu ăn, việc ta kiểm tra và điều chỉnh nhiệt độ, thời gian và nguyên liệu có thể được xem như một dạng luồng công việc (flow), tương tự như luồng công việc mà chúng ta định nghĩa trong lập trình.

Khi mọi thứ diễn ra như dự kiến, chúng ta có thể hoàn thành món ăn một cách trôi chảy, theo đúng trình tự và hướng dẫn của công thức. Tuy nhiên, có những lúc chúng ta phải đối mặt với những tình huống không lường trước được, ví dụ như thiếu nguyên liệu, gia vị được thêm quá mức, hoặc một số dụng cụ bị hỏng. Tùy vào từng tình huống cụ thể, chúng ta cần tìm ra các giải pháp riêng biệt để xử lý từng ngoại lệ.

Trong lập trình, cũng tương tự như vậy. Không phải lúc nào, mã lệnh của chúng ta cũng diễn ra suôn sẻ như kì vọng. Có vô số lý do có thể làm cho chương trình phải dừng lại và xử lý ngoại lệ. Việc xử lý ngoại lệ là cực kỳ quan trọng, giúp đảm bảo rằng mã lệnh có thể tiếp tục thực thi, hoặc ít ra, cung cấp thông báo lỗi rõ ràng khi đối mặt với ngoại lệ không mong muốn.

Tóm lại, việc hiểu và xử lý ngoại lệ không chỉ là một phần quan trọng của lập trình mà còn là một kỹ năng cần thiết trong cuộc sống, giúp chúng ta nhanh chóng phản ứng và thích ứng với mọi tình huống không lường trước được.

Chúng ta cùng tìm hiểu thêm về ngoại lệ trong lập trình nói chung và trong Java nói riêng, cũng như cách xử lý ngoại lệ trong bài viết này.

1. Exception là gì?

Trong lập trình nói chung, exception - hay ngoại lệ - là một sự kiện có thể xảy ra trong quá trình thực thi của chương trình, và nó có thể làm gián đoạn luồng xử lý thông thường của chương trình đó.

Khi những exception này xuất hiện trong lúc thực thi trường trình, chúng ta cần có một cơ chế để xử lý các ngoại lệ này, tránh việc các exception này làm gián đoạn, tổn hại hay chết chương trình. Quá trình trên gọi là exception handling (xử lý ngoại lệ)

Exception có thể phát sinh do nhiều lý do khác nhau, chẳng hạn như truy cập vùng nhớ không hợp lệ (NullPointerException) hay chia cho số không (ArithmeticException). Có rất nhiều những lý do khác nhau khiến mã nguồn của chúng ta gặp exception. Điều này khiến cho việc exception handling (xử lý ngoại lệ) trở nên vô cùng cần thiết.

2. Exception trong Java là gì?

Trong Java, Throwable là lớp cơ sở dành cho tất cả các đối tượng mà có thể được ném (throw) hoặc bắt (catch) bởi câu lệnh try-catch. Throwable có hai lớp con trực tiếp gồm Exception và Error.

Trong bài viết này, chúng ta sẽ làm rõ cả hai khái niệm trên.

2.1. Định nghĩa Exception và Error trong Java

Trong Java, Exception là một class rất đặc biệt được dùng để đại diện cho các ngoại lệ trong quá trình thực thi chương trình. Khi một class Exception được phát hiện, chúng ta cần phải xử lý ngoại lệ đó trong trương trình.

Bên cạnh đó, Java còn cùng cấp 1 lớp nữa tên là Error, đại diện cho một lỗi rất nghiêm trọng mà chương trình không nên bắt trong quá trình thực thi. Trong bài này cũng sẽ đề cập đến khái niệm error.

Cả error và exception đều kế thừa từ lớp throwable, cho phép chúng ta thực thi ném (throw) và bắt (catch) bởi câu lệnh try/catch.

Nói cách khác, khi chúng ta phát hiện được Exception, chương trình sẽ

public static void main(String[] args) {
   int result = divide(10, 0);
   System.out.println(result);
}

6

public static void main(String[] args) {
   int result = divide(10, 0);
   System.out.println(result);
}

7, và nhiệm vụ của chúng ta cần làm là

public static void main(String[] args) {
   int result = divide(10, 0);
   System.out.println(result);
}

8 đó, và xử lý chúng. Cuối cùng, ngay cả khi có Exception xảy ra hay không, chúng ta có thể vẫn phải xử lý một số logic để hoàn tất việc xử lý chương trình.

Đối với Error, mặc dù có thể throw/catch, nhưng chương trình không nên xử lý với Error theo cách mà chương trình xử lý với Exception. Chúng ta sẽ nói rõ hơn ở phần sau.

2.2. Lý do Exception và Error xuất hiện trong chương trình

Exception và Error đều biểu diễn một vấn đề mà chương trình có thể gặp phải. Tuy nhiên, mục đích và bối cảnh sử dụng của cả 2 có chút khác biệt.

Exception là những sự cố mà lập trình viên có thể dự đoán và xử lý được. Chúng thường xuất hiện do dữ liệu đầu vào không hợp lệ, vấn đề khi kết nối tới một dịch vụ ngoài, v.v. Tất cả những trường hợp trên đều có thể dẫn đến việc exception được ném ra, làm gián đoạn luồng xử lý bình thường của chương trình.

Error biểu diễn những vấn đề nghiêm trọng mà chương trình không thể giải quyết hoặc xử lý được. Những lỗi này thường liên quan đến vấn đề về môi trường chạy của JVM, như hết bộ nhớ heap hoặc StackOverflow.

Một số error vẫn có thể xử lý được ở application layer, một số thì không thể xử lý được. Đa phần những lỗi này xảy ra rất khó dự đoán, khó nhận biết và gậy tổn hại đến hệ thống.

2.3. Tầm quan trọng của Exception Handling

Exception Handling chủ yếu giúp bảo vệ chương trình khỏi các sự cố và lỗi không dự đoán được, giữ cho chương trình tiếp tục hoạt động mà không bị gián đoạn, ngay cả khi gặp phải vấn đề. Việc này đặc biệt quan trọng trong các ứng dụng cần sự ổn định và độ tin cậy cao, ví dụ như các hệ thống quản lý cơ sở dữ liệu, các ứng dụng ngân hàng trực tuyến, hoặc các hệ thống điều khiển trực tuyến.

Khi một ngoại lệ xảy ra, chương trình có thể tạo ra thông báo lỗi có ý nghĩa, ghi lại thông tin chi tiết về sự cố, và thậm chí có thể phục hồi hoặc khôi phục trạng thái hệ thống.

Bên cạnh đó, việc xử lý ngoại lệ còn tăng cường khả năng bảo trì và phát triển chương trình. Khi các lỗi và sự cố được xử lý một cách hiệu quả và minh bạch, các lập trình viên có thể dễ dàng tìm ra nguyên nhân gốc rễ của vấn đề và phát triển các giải pháp và cải tiến một cách có hệ thống, giảm thiểu rủi ro và tăng hiệu suất phát triển.

3. Cơ chế Ngoại Lệ trong Java

Trong Java, chúng ta có thể sử dụng các cú pháp như

public static void main(String[] args) {
   int result = divide(10, 0);
   System.out.println(result);
}

6,

public static void main(String[] args) {
    try {
        int result = divide(10, 0); // Might throw ArithmeticException
        System.out.println(result);
    } catch (ArithmeticException e) {
        e.printStackTrace(); // Handle and log the exception
        System.out.println(e.getMessage());
    }
}

0,

public static void main(String[] args) {
    try {
        int result = divide(10, 0); // Might throw ArithmeticException
        System.out.println(result);
    } catch (ArithmeticException e) {
        e.printStackTrace(); // Handle and log the exception
        System.out.println(e.getMessage());
    }
}

1 và

public static void main(String[] args) {
    try {
        int result = divide(10, 0); // Might throw ArithmeticException
        System.out.println(result);
    } catch (ArithmeticException e) {
        e.printStackTrace(); // Handle and log the exception
        System.out.println(e.getMessage());
    }
}

2 để xử lý các Exception. Các từ khoá đó ứng với mỗi một giai đoạn trong quá trình phát hiện và xử lý exception.

Lưu ý, Java có phân biệt các kiểu ngoại lệ riêng biệt, mình sẽ đề cập đến các kiểu exception ở chương 4.

Dưới đây là giải thích từng bước về cơ chế Exception trong Java

3.1. Throw

Ở một số điều kiện cụ thể, chương trình sẽ thực hiện ném ra một ngoại lệ.

Ví dụ chúng ta phát triển một function có tên là

public static void main(String[] args) {
    try {
        int result = divide(10, 0); // Might throw ArithmeticException
        System.out.println(result);
    } catch (ArithmeticException e) {
        e.printStackTrace(); // Handle and log the exception
        System.out.println(e.getMessage());
    }
}

3, function này nhận 2 tham số

public static void main(String[] args) {
    try {
        int result = divide(10, 0); // Might throw ArithmeticException
        System.out.println(result);
    } catch (ArithmeticException e) {
        e.printStackTrace(); // Handle and log the exception
        System.out.println(e.getMessage());
    }
}

4 và

public static void main(String[] args) {
    try {
        int result = divide(10, 0); // Might throw ArithmeticException
        System.out.println(result);
    } catch (ArithmeticException e) {
        e.printStackTrace(); // Handle and log the exception
        System.out.println(e.getMessage());
    }
}

5, kết quả trả về là

public static void main(String[] args) {
    try {
        int result = divide(10, 0); // Might throw ArithmeticException
        System.out.println(result);
    } catch (ArithmeticException e) {
        e.printStackTrace(); // Handle and log the exception
        System.out.println(e.getMessage());
    }
}

6. Chương trình được viết như sau:

public static int divide(int a, int b) {
    return a / b;
}

Chương trình trên sẽ gặp bất thường nếu số

public static void main(String[] args) {
    try {
        int result = divide(10, 0); // Might throw ArithmeticException
        System.out.println(result);
    } catch (ArithmeticException e) {
        e.printStackTrace(); // Handle and log the exception
        System.out.println(e.getMessage());
    }
}

7, do phép chia không thể chia cho không. Đây là một exception.

Để xử lý exception này, chương trình của chúng ta cần xác định điều kiện cụ thể cho trường hợp đó, ở trường hợp này là khi

public static void main(String[] args) {
    try {
        int result = divide(10, 0); // Might throw ArithmeticException
        System.out.println(result);
    } catch (ArithmeticException e) {
        e.printStackTrace(); // Handle and log the exception
        System.out.println(e.getMessage());
    }
}

7. Khi đó, chúng ta cần viết code như sau

public static int divide(int a, int b) throws ArithmeticException {
    if (b == 0) {
        throw new ArithmeticException("Cannot divide by zero.");         // throw exception ở đây
    }
    return a / b;
}

Có hai từ khoá mà các bạn cần lưu ý, là

public static void main(String[] args) {
    try {
        int result = divide(10, 0); // Might throw ArithmeticException
        System.out.println(result);
    } catch (ArithmeticException e) {
        e.printStackTrace(); // Handle and log the exception
        System.out.println(e.getMessage());
    }
}

9 ở dòng 1 và từ

public static void main(String[] args) {
   int result = divide(10, 0);
   System.out.println(result);
}

6 ở dòng thứ ba.

  1. public static void main(String[] args) {

    try {  
        int result = divide(10, 0); // Might throw ArithmeticException  
        System.out.println(result);  
    } catch (ArithmeticException e) {  
        e.printStackTrace(); // Handle and log the exception  
        System.out.println(e.getMessage());  
    }  
    
    }

    9: từ khoá này được dùng để khai báo rằng function sẽ có thể ném ra ngoại lệ tương ứng. Mỗi một function có thể khai báo nhiều hơn 1 kiểu exception. Khi function khai báo từ khoá throws, điều đó có nghĩa là trách nhiệm xử lý exception này thuộc calling method (phương thức gọi) hoặc một calling method xa hơn. Kiểu ngoại lệ sẽ được đề cập ở chương sau.
  2. public static void main(String[] args) { int result = divide(10, 0); System.out.println(result); }

    6: từ khoá này được sử dụng để tạo ra một ngoại lệ cụ thể và chuyển quyền kiểm soát từ phương thức hiện tại đến phương thức gọi (calling method) hoặc một khối

    public static void main(String[] args) {

    try {  
        int result = divide(10, 0); // Might throw ArithmeticException  
        System.out.println(result);  
    } catch (ArithmeticException e) {  
        e.printStackTrace(); // Handle and log the exception  
        System.out.println(e.getMessage());  
    }  
    
    }

    1 tương ứng nếu có.

Tóm lại, 2 từ khoá

public static void main(String[] args) {
   int result = divide(10, 0);
   System.out.println(result);
}

6 và

public static void main(String[] args) {
    try {
        int result = divide(10, 0); // Might throw ArithmeticException
        System.out.println(result);
    } catch (ArithmeticException e) {
        e.printStackTrace(); // Handle and log the exception
        System.out.println(e.getMessage());
    }
}

9 được dùng để khai báo exception có thể xảy ra tại một function, và chuyển hướng kiểm soát exception này đến calling method.

3.2. Try/catch

Khi called method thực hiện ném một exception, calling method sẽ có trách nhiệm xử lý exception đó. Calling method có thể xử lý trực tiếp, hoặc tiếp tục throw exception đó.

Tiếp tục ví dụ ở trên, giả sử chúng ta call đến function

public static void main(String[] args) {
    try {
        int result = divide(10, 0); // Might throw ArithmeticException
        System.out.println(result);
    } catch (ArithmeticException e) {
        e.printStackTrace(); // Handle and log the exception
        System.out.println(e.getMessage());
    }
}

3 một cách trực tiếp như sau

public static void main(String[] args) {
   int result = divide(10, 0);
   System.out.println(result);
}

Chương trình này sẽ không thể hoạt động được, do function

public static void main(String[] args) {
    try {
        int result = divide(10, 0); // Might throw ArithmeticException
        System.out.println(result);
    } catch (ArithmeticException e) {
        e.printStackTrace(); // Handle and log the exception
        System.out.println(e.getMessage());
    }
}

3 đã throw exception, nhưng function

public static void main(String[] args) {
    try {
        int result = divide(10, 0); // Might throw ArithmeticException
        System.out.println(result);
    } catch (ArithmeticException e) {
        e.printStackTrace(); // Handle and log the exception
        System.out.println(e.getMessage());
    }
     finally {
        System.out.println("Finally block executed.");
    }
}

8 không thực hiện xử lý exception đó.

Sửa lỗi java.lang.exception và lỗi chưa có driver trong java năm 2024
Chương trình sẽ bị dừng hoạt động nếu chúng ta không thực hiên exception handling

Dưới đây là đoạn code giúp function

public static void main(String[] args) {
    try {
        int result = divide(10, 0); // Might throw ArithmeticException
        System.out.println(result);
    } catch (ArithmeticException e) {
        e.printStackTrace(); // Handle and log the exception
        System.out.println(e.getMessage());
    }
     finally {
        System.out.println("Finally block executed.");
    }
}

8 có thể xử lý được exception của function

public static void main(String[] args) {
    try {
        int result = divide(10, 0); // Might throw ArithmeticException
        System.out.println(result);
    } catch (ArithmeticException e) {
        e.printStackTrace(); // Handle and log the exception
        System.out.println(e.getMessage());
    }
}

3.

public static void main(String[] args) {
    try {
        int result = divide(10, 0); // Might throw ArithmeticException
        System.out.println(result);
    } catch (ArithmeticException e) {
        e.printStackTrace(); // Handle and log the exception
        System.out.println(e.getMessage());
    }
}

Có hai từ khoá mà các bạn cần lưu ý, là

public static void main(String[] args) {
    try {
        int result = divide(10, 0); // Might throw ArithmeticException
        System.out.println(result);
    } catch (ArithmeticException e) {
        e.printStackTrace(); // Handle and log the exception
        System.out.println(e.getMessage());
    }
}

0 ở dòng thứ 2 và từ

public static void main(String[] args) {
    try {
        int result = divide(10, 0); // Might throw ArithmeticException
        System.out.println(result);
    } catch (ArithmeticException e) {
        e.printStackTrace(); // Handle and log the exception
        System.out.println(e.getMessage());
    }
}

1 ở dòng thứ năm.

  1. public static void main(String[] args) {

    try {  
        int result = divide(10, 0); // Might throw ArithmeticException  
        System.out.println(result);  
    } catch (ArithmeticException e) {  
        e.printStackTrace(); // Handle and log the exception  
        System.out.println(e.getMessage());  
    }  
    
    }

    0: Khối try chứa đoạn mã có thể gặp lỗi hoặc ném ra ngoại lệ khi thực thi. Nếu có bất kỳ ngoại lệ nào xảy ra trong khối try, chương trình sẽ chuyển quyền kiểm soát đến khối catch tương ứng. Mỗi khối try phải theo sau ít nhất một khối catch hoặc một khối finally.
  2. public static void main(String[] args) {

    try {  
        int result = divide(10, 0); // Might throw ArithmeticException  
        System.out.println(result);  
    } catch (ArithmeticException e) {  
        e.printStackTrace(); // Handle and log the exception  
        System.out.println(e.getMessage());  
    }  
    
    }

    1: Khối catch chứa đoạn mã sẽ được thực thi nếu có ngoại lệ xảy ra trong khối try tương ứng. Khối catch chủ yếu được sử dụng để xử lý ngoại lệ hoặc ghi log về lỗi. Có thể có nhiều khối catch sau một khối try, mỗi khối catch xử lý một loại ngoại lệ cụ thể.

3.3. Finally

Trong xử lý ngoại lệ của Java, từ khoá

public static void main(String[] args) {
    try {
        int result = divide(10, 0); // Might throw ArithmeticException
        System.out.println(result);
    } catch (ArithmeticException e) {
        e.printStackTrace(); // Handle and log the exception
        System.out.println(e.getMessage());
    }
}

2 được sử dụng để tạo một khối mã mà sẽ được thực thi sau khi thực hiện xong khối

public static void main(String[] args) {
    try {
        int result = divide(10, 0); // Might throw ArithmeticException
        System.out.println(result);
    } catch (ArithmeticException e) {
        e.printStackTrace(); // Handle and log the exception
        System.out.println(e.getMessage());
    }
}

0 và khối

public static void main(String[] args) {
    try {
        int result = divide(10, 0); // Might throw ArithmeticException
        System.out.println(result);
    } catch (ArithmeticException e) {
        e.printStackTrace(); // Handle and log the exception
        System.out.println(e.getMessage());
    }
}

1, không phụ thuộc vào việc có ngoại lệ xảy ra hay không. Điều này đảm bảo rằng mã trong khối finally luôn luôn được thực thi.

Quay trở lại ví dụ trên về phép chia cho số 0 ở trên, đoạn mã trên có thể được bổ sung như sau:

public static void main(String[] args) {
    try {
        int result = divide(10, 0); // Might throw ArithmeticException
        System.out.println(result);
    } catch (ArithmeticException e) {
        e.printStackTrace(); // Handle and log the exception
        System.out.println(e.getMessage());
    }
     finally {
        System.out.println("Finally block executed.");
    }
}

Xin lưu ý từ khoá

public static void main(String[] args) {
    try {
        int result = divide(10, 0); // Might throw ArithmeticException
        System.out.println(result);
    } catch (ArithmeticException e) {
        e.printStackTrace(); // Handle and log the exception
        System.out.println(e.getMessage());
    }
}

2 ở dòng 9, câu lệnh in ra dòng "Finally block executed." sẽ luôn được hiển thị ra bất chấp code của chúng ta có rơi vào exception hay không. Điều này cho phép chúng ta xử lý một số logic bắt buộc, ví dụ như dọn dẹp tài nguyên (đóng file sau khi mở, đóng kết nối cơ sở dữ liệu).

Finally là thành phần optional, nó không nhất thiết phải được sử dụng trong toàn bộ quá trình exception handling nếu không cần thiết.

4. Các loại Exception trong Java

Cả hai lớp Error và Exception đều kế thừa từ một lớp cha, có tên là Throwable.

Sửa lỗi java.lang.exception và lỗi chưa có driver trong java năm 2024
Cả Error và Exception đều kế thừa từ 1 lớp cha, Throwable

Trong lớp Exception, chúng ta quan tâm đến 2 loại exception chính là RuntimeException, và các exception còn lại.

Sửa lỗi java.lang.exception và lỗi chưa có driver trong java năm 2024
Hai loại exception chính trong Java

RuntimeException là exception mà chỉ có thể phát hiện được trong runtime, JVM sẽ bỏ qua các lỗi này ở compiler. Các lớp exception kế thừa lớp RuntimeException có tên gọi là Unchecked Exception.

Các other exception , kế thừa từ lớp exception được gọi là checked exception, do compiler phát hiện trong quá trình compile code. Do được phát hiện trong quá trình compile, các lập trình viên buộc phải xử lý try / catch các exception này.

Sửa lỗi java.lang.exception và lỗi chưa có driver trong java năm 2024
Throwable là lớp cha của tất cả Error và Exception trong Java

4.1. Checked Exception

Checked Exceptions là những exception mà compiler kiểm tra tại thời điểm biên dịch.

Nếu một phương thức có khả năng phát sinh một Checked Exception, Java yêu cầu bạn hoặc phải xử lý ngoại lệ đó (sử dụng

public static void main(String[] args) {
    try {
        FileInputStream file = new FileInputStream("nonexistentfile.txt");
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }
}

  1. hoặc khai báo nó (sử dụng

public static void main(String[] args) {
    try {
        int result = divide(10, 0); // Might throw ArithmeticException
        System.out.println(result);
    } catch (ArithmeticException e) {
        e.printStackTrace(); // Handle and log the exception
        System.out.println(e.getMessage());
    }
}

9), để rõ ràng cho bản thân và cho người khác biết rằng có một tình huống ngoại lệ có thể xảy ra.

Java sẽ không thể complier thành công nếu các Checked Exception không được xử lý hoàn toàn, do đó, chúng ta cần phải xử lý toàn bộ các checked exception trước khi complier.

Mục tiêu chính của Checked Exception là để thông báo cho lập trình viên về các vấn đề có thể xảy ra, và buộc lập trình viên phải tự quyết định cách xử lý các tình huống đó.

public static void main(String[] args) {
    try {
        FileInputStream file = new FileInputStream("nonexistentfile.txt");
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }
}

Trong ví dụ trên,

public static int divide(int a, int b) {
    return a / b;
}

1 là một Checked Exception. Do đó, Java buộc chúng ta phải xử lý ngoại lệ này (hoặc sử dụng

public static void main(String[] args) {
    try {
        FileInputStream file = new FileInputStream("nonexistentfile.txt");
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }
}

9 để bắt ngoại lệ hoặc sử dụng

public static void main(String[] args) {
    try {
        int result = divide(10, 0); // Might throw ArithmeticException
        System.out.println(result);
    } catch (ArithmeticException e) {
        e.printStackTrace(); // Handle and log the exception
        System.out.println(e.getMessage());
    }
}

9 để khai báo ngoại lệ).

Các loại checked exception tiêu biểu bao gồm:

  • IOException:

Xuất hiện khi có lỗi nhập/xuất dữ liệu, ví dụ khi đọc/ghi file.

Các lớp con: FileNotFoundException, EOFException,

UnsupportedEncodingException v.v.

  • ClassNotFoundException:

Xuất hiện trong quá trình complier, khi JVM không thể tìm thấy class được yêu cầu.

  • SQLException:

Xuất hiện khi có lỗi trong quá trình truy vấn cơ sở dữ liệu

  • InterruptedException:

Phát sinh khi một thread đang chờ, ngủ, hoặc bận rộn, và một thread khác đã làm gián đoạn nó.

  • ReflectiveOperationException:

Là lớp cha của các ngoại lệ có thể phát sinh khi sử dụng API Reflection, như InvocationTargetException, InstantiationException, v.v.

  • MalformedURLException:

Là một exception trong việc phân tích URL, nó sẽ kiểm tra URL có chính xác hay không.

  • ParseException:

Xuất hiện khi có lỗi phân tích (parse) chuỗi sang một đối tượng, thường xuất hiện khi chuyển đổi chuỗi sang đối tượng Date hoặc số.

4.2. Unchecked Exception

Khác với checked exception có thể được JVM tìm ra và cảnh báo đến các lập trình viên, unchecked exception lại không được JVM phát hiện ra trong quá trình complier.

Tất cả các unchecked exception đều kế thừa từ lớp RuntimeException, và chỉ được phát hiện trong runtime, không được kiểm tra trong compile-time.

Compiler sẽ không báo lỗi hay cảnh báo nếu bạn không xử lý (ví dụ: không sử dụng

public static void main(String[] args) {
    try {
        FileInputStream file = new FileInputStream("nonexistentfile.txt");
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }
}

9 hoặc không khai báo

public static void main(String[] args) {
    try {
        int result = divide(10, 0); // Might throw ArithmeticException
        System.out.println(result);
    } catch (ArithmeticException e) {
        e.printStackTrace(); // Handle and log the exception
        System.out.println(e.getMessage());
    }
}

  1. các Unchecked Exceptions.

Các bạn chắc vẫn còn nhớ về ví dụ phép chia cho số 0 ở chương đầu.

public static int divide(int a, int b) {
    return a / b;
}

Compiler sẽ không báo lỗi tại thời điểm biên dịch, nhưng khi bạn chạy chương trình, JVM sẽ ném ra

public static int divide(int a, int b) {
    return a / b;
}

6 tại runtime nếu

public static void main(String[] args) {
    try {
        int result = divide(10, 0); // Might throw ArithmeticException
        System.out.println(result);
    } catch (ArithmeticException e) {
        e.printStackTrace(); // Handle and log the exception
        System.out.println(e.getMessage());
    }
}

7.

Các loại unchecked exception tiêu biểu bao gồm:

  • ArithmeticException

Xảy ra khi có một phép toán không hợp lệ, ví dụ như chia một số cho zero.

  • NullPointerException

Xảy ra khi chương trình cố gắng truy cập hoặc sử dụng một đối tượng mà thực sự là null.

  • ArrayIndexOutOfBoundsException

Xảy ra khi chương trình cố gắng truy cập vào một chỉ số của mảng không tồn tại.

  • ClassCastException

Xảy ra khi chương trình cố gắng ép kiểu (cast) một đối tượng sang một kiểu không tương thích.

  • NumberFormatException

Xảy ra khi chương trình cố gắng chuyển đổi một chuỗi không phải là số sang một kiểu số (ví dụ: Integer.parseInt("abc")).

  • IllegalStateException

Xảy ra khi một đối tượng không ở trong trạng thái thích hợp để thực hiện một hoạt động nào đó.

Nhìn chung, những Unchecked Exception này thường xuất phát từ các lỗi lập trình và thiết kế, và lập trình viên có trách nhiệm phát hiện và xử lý chúng để tránh các lỗi runtime có thể xảy ra.

Cá nhân mình nhận thấy đa phần lỗi xuất phát từ những Unchecked Exception này.

4.3. Error

Error là một lớp con của Throwable, nó ngang hàng với Exception. Do đó, về mặt bản chất nếu liệt kê Error là một Exception là không đúng. Tuy nhiên mình vẫn sẽ đề cập đến Error như một lỗi có thể xuất phát trong lúc code để cùng phân tích và xử lý.

Sửa lỗi java.lang.exception và lỗi chưa có driver trong java năm 2024
Error và Exception cùng kế thừa từ lớp Throwable

Giống với Exception, Error cũng kế thừa lớp Throwable, nên chúng ta có thể dùng cú pháp

public static int divide(int a, int b) {
    return a / b;
}

8 để bắt được error trong quá trình thực thi code.

Tuy nhiên, Error được xác định là một lớp riêng biệt với mục đích rất cụ thể: chúng ta không nên cố gắng bắt nó.

Theo tài liệu chính thống từ Oracle – công ty quản lý và phát triển phiên bản chính thức của Java, Error biểu thị một vấn đề nghiêm trọng xảy ra khi thực thi chương trình mà ta không nên cố gắng xử lý.

Đây là một tình trạng bất thường mà một ứng dụng không nên gặp phải.

An Error is a subclass of Throwable that indicates serious problems that a reasonable application should not try to catch. Most such errors are abnormal conditions. The ThreadDeath error, though a "normal" condition, is also a subclass of Error because most applications should not try to catch it. (by Oracle)

Dưới đây là một số loại error phổ biến mà bạn có thể gặp trong Java:

  • OutOfMemoryError: Xảy ra khi hệ thống không còn đủ bộ nhớ cho ứng dụng. Điển hình là khi bạn cố gắng cấp phát bộ nhớ mà JVM không thể cung cấp.
  • StackOverflowError: Được gây ra bởi đệ quy quá sâu, dẫn đến kích thước của stack vượt quá giới hạn cho phép.
  • NoClassDefFoundError: Xảy ra khi lớp đã được biên dịch, nhưng không thể được tìm thấy tại thời điểm chạy.
  • UnknownError: Một lỗi không rõ nguyên nhân.

Đây chỉ là một số error phổ biến trong Java, và có nhiều error khác tồn tại. Điều quan trọng cần nhớ là, trong hầu hết các trường hợp, các lỗi này biểu thị các vấn đề mà ứng dụng không nên (hoặc không thể) xử lý và thường cần được giải quyết ở cấp độ hệ thống hoặc cấu hình.

5. Custom một Exception hoặc Error

Ngoài các error, exception có sẵn, Java cho phép chúng ta tạo thêm các error và exception để lập trình viên có thể tuỳ biến và sử dụng trong các mục đích khác nhau.

5.1. Custom một Exception

Để có thể tạo ra một exception tuỳ ý, đầu tiên, bạn tạo một lớp mới kế thừa từ

public static int divide(int a, int b) {
    return a / b;
}

9 với trường hợp checked exception, hoặc lớp

public class CustomException extends Exception {
    public CustomException(String message) {
        super(message);
    }
    // Bạn cũng có thể thêm các hàm khởi tạo (constructor) khác nếu cần thiết
}

0 với trường hợp unchecked exception.

public class CustomException extends Exception {
    public CustomException(String message) {
        super(message);
    }
    // Bạn cũng có thể thêm các hàm khởi tạo (constructor) khác nếu cần thiết
}

Tiếp theo, chúng ta sẽ sử dụng

public class CustomException extends Exception {
    public CustomException(String message) {
        super(message);
    }
    // Bạn cũng có thể thêm các hàm khởi tạo (constructor) khác nếu cần thiết
}

1 này như sau

public class SomeClass {
    public void someMethod(int value) throws CustomException {
        if (value < 0) {
            throw new CustomException("Value không được âm");
        }
        // phần xử lý còn lại.
    }
    public static void main(String[] args) {
        SomeClass obj = new SomeClass();
        try {
            obj.someMethod(-1);
        } catch (CustomException e) {
            e.printStackTrace();
        }
    }
}

Ở ví dụ trên, chúng ta đã tạo và sử dụng một class tên

public class CustomException extends Exception {
    public CustomException(String message) {
        super(message);
    }
    // Bạn cũng có thể thêm các hàm khởi tạo (constructor) khác nếu cần thiết
}

1. Function

public class CustomException extends Exception {
    public CustomException(String message) {
        super(message);
    }
    // Bạn cũng có thể thêm các hàm khởi tạo (constructor) khác nếu cần thiết
}

3 đã khai báo

public class CustomException extends Exception {
    public CustomException(String message) {
        super(message);
    }
    // Bạn cũng có thể thêm các hàm khởi tạo (constructor) khác nếu cần thiết
}

4, vì thế function

public static void main(String[] args) {
    try {
        int result = divide(10, 0); // Might throw ArithmeticException
        System.out.println(result);
    } catch (ArithmeticException e) {
        e.printStackTrace(); // Handle and log the exception
        System.out.println(e.getMessage());
    }
     finally {
        System.out.println("Finally block executed.");
    }
}

8 buộc phải xử lý

public static void main(String[] args) {
   int result = divide(10, 0);
   System.out.println(result);
}

8 mà function

public class CustomException extends Exception {
    public CustomException(String message) {
        super(message);
    }
    // Bạn cũng có thể thêm các hàm khởi tạo (constructor) khác nếu cần thiết
}

3 đã ném ra.

5.2. Custom một Error

Tương tự như việc tạo ra một exception tuỳ chỉnh, chúng ta cũng có thể tạo một error tuỳ chỉnh bằng cách tạo ra một class kế thừa class

public class CustomException extends Exception {
    public CustomException(String message) {
        super(message);
    }
    // Bạn cũng có thể thêm các hàm khởi tạo (constructor) khác nếu cần thiết
}

8.

public class CustomError extends Error {
    public CustomError(String message) {
        super(message);
    }
    // Bạn cũng có thể thêm các hàm khởi tạo khác nếu cần thiết
}

Tuy nhiên, xin lưu ý rằng việc tạo và sử dụng lớp error tùy chỉnh không được khuyến khích, vì

public class CustomException extends Exception {
    public CustomException(String message) {
        super(message);
    }
    // Bạn cũng có thể thêm các hàm khởi tạo (constructor) khác nếu cần thiết
}

8 thường dành cho các vấn đề nghiêm trọng mà JVM gặp phải và không nên được sử dụng trong mã ứng dụng thông thường.

6. Thực hành một số Exception thường gặp

6.1. Checked Exception

Dưới đây là một số ví dụ về việc sử dụng checked exception

6.1.1. Tìm kiếm file

Để có thể tìm kiếm vào trong một file, chúng ta sử dụng class

public class SomeClass {
    public void someMethod(int value) throws CustomException {
        if (value < 0) {
            throw new CustomException("Value không được âm");
        }
        // phần xử lý còn lại.
    }
    public static void main(String[] args) {
        SomeClass obj = new SomeClass();
        try {
            obj.someMethod(-1);
        } catch (CustomException e) {
            e.printStackTrace();
        }
    }
}

0

public static int divide(int a, int b) throws ArithmeticException {
    if (b == 0) {
        throw new ArithmeticException("Cannot divide by zero.");         // throw exception ở đây
    }
    return a / b;
}

0

Class

public class SomeClass {
    public void someMethod(int value) throws CustomException {
        if (value < 0) {
            throw new CustomException("Value không được âm");
        }
        // phần xử lý còn lại.
    }
    public static void main(String[] args) {
        SomeClass obj = new SomeClass();
        try {
            obj.someMethod(-1);
        } catch (CustomException e) {
            e.printStackTrace();
        }
    }
}

1 sẽ throw một exception là

public static int divide(int a, int b) {
    return a / b;
}

1

Sửa lỗi java.lang.exception và lỗi chưa có driver trong java năm 2024
FileReader sẽ throw một exception tên là FileNotFoundException

Vì thế, khi chúng ta muốn tìm kiếm một file, chúng ta cần xử lý exception

public static int divide(int a, int b) {
    return a / b;
}

1.

Vì thế, code đầy đủ trong trường hợp này là:

public static int divide(int a, int b) throws ArithmeticException {
    if (b == 0) {
        throw new ArithmeticException("Cannot divide by zero.");         // throw exception ở đây
    }
    return a / b;
}

1

6.1.2. Tìm một class

Để tim một class khác trong Java, chúng ta có thể dùng hàm

public class SomeClass {
    public void someMethod(int value) throws CustomException {
        if (value < 0) {
            throw new CustomException("Value không được âm");
        }
        // phần xử lý còn lại.
    }
    public static void main(String[] args) {
        SomeClass obj = new SomeClass();
        try {
            obj.someMethod(-1);
        } catch (CustomException e) {
            e.printStackTrace();
        }
    }
}

4

public static int divide(int a, int b) throws ArithmeticException {
    if (b == 0) {
        throw new ArithmeticException("Cannot divide by zero.");         // throw exception ở đây
    }
    return a / b;
}

2

Function

public class SomeClass {
    public void someMethod(int value) throws CustomException {
        if (value < 0) {
            throw new CustomException("Value không được âm");
        }
        // phần xử lý còn lại.
    }
    public static void main(String[] args) {
        SomeClass obj = new SomeClass();
        try {
            obj.someMethod(-1);
        } catch (CustomException e) {
            e.printStackTrace();
        }
    }
}

4 sẽ throw một exception tên là

public class SomeClass {
    public void someMethod(int value) throws CustomException {
        if (value < 0) {
            throw new CustomException("Value không được âm");
        }
        // phần xử lý còn lại.
    }
    public static void main(String[] args) {
        SomeClass obj = new SomeClass();
        try {
            obj.someMethod(-1);
        } catch (CustomException e) {
            e.printStackTrace();
        }
    }
}

6

Sửa lỗi java.lang.exception và lỗi chưa có driver trong java năm 2024
Class.forName sẽ throw ClassNotFoundException

Do đó, chúng ta cần xử lý exception này khi sử dụng.

public static int divide(int a, int b) throws ArithmeticException {
    if (b == 0) {
        throw new ArithmeticException("Cannot divide by zero.");         // throw exception ở đây
    }
    return a / b;
}

3

6.2. Unchecked Exception

6.2.1. Kiểm tra giá trị null

Khi chúng ta đang cố truy cập vào một giá trị null, exception

public class SomeClass {
    public void someMethod(int value) throws CustomException {
        if (value < 0) {
            throw new CustomException("Value không được âm");
        }
        // phần xử lý còn lại.
    }
    public static void main(String[] args) {
        SomeClass obj = new SomeClass();
        try {
            obj.someMethod(-1);
        } catch (CustomException e) {
            e.printStackTrace();
        }
    }
}

7 sẽ được ném ra. Tuy nhiên do là unchecked exception, chúng ta có thể không cần catch exception này.

public static int divide(int a, int b) throws ArithmeticException {
    if (b == 0) {
        throw new ArithmeticException("Cannot divide by zero.");         // throw exception ở đây
    }
    return a / b;
}

4

6.2.2. Truy cập vào index không tồn tại

Khi chúng ta cố gắng truy cập đến một index không có trong một array, exception

public class SomeClass {
    public void someMethod(int value) throws CustomException {
        if (value < 0) {
            throw new CustomException("Value không được âm");
        }
        // phần xử lý còn lại.
    }
    public static void main(String[] args) {
        SomeClass obj = new SomeClass();
        try {
            obj.someMethod(-1);
        } catch (CustomException e) {
            e.printStackTrace();
        }
    }
}

8 sẽ được ném ra.

public static int divide(int a, int b) throws ArithmeticException {
    if (b == 0) {
        throw new ArithmeticException("Cannot divide by zero.");         // throw exception ở đây
    }
    return a / b;
}

5

6.3. Error

Thực tế, khi một

public class CustomException extends Exception {
    public CustomException(String message) {
        super(message);
    }
    // Bạn cũng có thể thêm các hàm khởi tạo (constructor) khác nếu cần thiết
}

8 xảy ra, nó thường chỉ ra một vấn đề nghiêm trọng và ứng dụng thường nên được dừng lại và kiểm tra lại.

Do đó, bất cứ thời điểm và dòng code nào trong ứng dụng cũng có thể xuất hiện

public class CustomException extends Exception {
    public CustomException(String message) {
        super(message);
    }
    // Bạn cũng có thể thêm các hàm khởi tạo (constructor) khác nếu cần thiết
}

8, vì thế

public class CustomException extends Exception {
    public CustomException(String message) {
        super(message);
    }
    // Bạn cũng có thể thêm các hàm khởi tạo (constructor) khác nếu cần thiết
}

8 mới khó đoán và khó xử lý.

Dưới đây là một số mô phỏng thực tế có thể xảy ra

public class CustomException extends Exception {
    public CustomException(String message) {
        super(message);
    }
    // Bạn cũng có thể thêm các hàm khởi tạo (constructor) khác nếu cần thiết
}

8.

6.3.1. Hết bộ nhớ

Khi hệ thống hết bộ nhớ,

public class CustomError extends Error {
    public CustomError(String message) {
        super(message);
    }
    // Bạn cũng có thể thêm các hàm khởi tạo khác nếu cần thiết
}

3 sẽ được ném ra. Chúng ta có thể mô phỏng lỗi này bằng cách cố gắng cấp phát một mảng lớn.

public static int divide(int a, int b) throws ArithmeticException {
    if (b == 0) {
        throw new ArithmeticException("Cannot divide by zero.");         // throw exception ở đây
    }
    return a / b;
}

6

6.3.2. Tràn bộ đệm (stack overflow)

Khi độ sâu của ngăn xếp gọi vượt quá giới hạn cho phép,

public class CustomError extends Error {
    public CustomError(String message) {
        super(message);
    }
    // Bạn cũng có thể thêm các hàm khởi tạo khác nếu cần thiết
}

4 sẽ được ném ra. Một cách thông thường để gây ra lỗi này là thông qua đệ quy không có điều kiện dừng:

public static int divide(int a, int b) throws ArithmeticException {
    if (b == 0) {
        throw new ArithmeticException("Cannot divide by zero.");         // throw exception ở đây
    }
    return a / b;
}

7

7. Best practice về Exception trong Java

Khi làm việc với exception trong Java, việc tuân theo một số best practice sẽ giúp mã của bạn dễ đọc, dễ bảo trì và tránh được các lỗi tiềm ẩn. Một số cách triển khai thiếu hiệu quả sẽ có tác dụng ngược lại, vì thế hãy thận trọng khi sử dụng exception.

Dưới đây là một số best practice bạn nên theo dõi:

7.1. Sử dụng một Exception đúng nghĩa

Khi bạn sử dụng một loại exception không phù hợp với tình huống thực sự xảy ra. Giả sử bạn muốn kiểm tra giá trị tuổi của một người:

public static int divide(int a, int b) throws ArithmeticException {
    if (b == 0) {
        throw new ArithmeticException("Cannot divide by zero.");         // throw exception ở đây
    }
    return a / b;
}

8

Việc sử dụng Exception sai với bối cảnh mà chúng ta gặp phải dễ dẫn đến các nhầm lẫn trong quá trình triển khai code sau này.

Ở ví dụ trên, chúng ta nên tạo một exception mới, có tên là

public class CustomError extends Error {
    public CustomError(String message) {
        super(message);
    }
    // Bạn cũng có thể thêm các hàm khởi tạo khác nếu cần thiết
}

5.

public static int divide(int a, int b) throws ArithmeticException {
    if (b == 0) {
        throw new ArithmeticException("Cannot divide by zero.");         // throw exception ở đây
    }
    return a / b;
}

9

Với trường hợp này, exception

public class CustomError extends Error {
    public CustomError(String message) {
        super(message);
    }
    // Bạn cũng có thể thêm các hàm khởi tạo khác nếu cần thiết
}

5 rất rõ nghĩa, đúng bối cảnh sử dụng, tránh các nhầm lẫn không đáng có và gây ra hiểu nhầm không cần thiết.

7.2. Tránh việc "ăn" Exception - Eating Exceptions

Khi chúng ta bắt gặp một exception, chúng ta buộc phải xử lý nó. Đó là lý do mà exception tồn tại. "Ăn" exception là hành động ngược lại, chúng ta cố tình phớt lờ, bỏ qua nó, "ăn" nó khiến exception này dường như không tồn tại trong hệ thống.

Việc bỏ qua exception sẽ gây ra rất nhiều hệ luỵ, ví dụ như:

  • Khó khăn trong việc xác định và sửa lỗi: Việc ẩn giấu một exception khiến công việc truy vết gặp rất nhiều khó khăn.
  • Ứng dụng thiếu tính ổn định: Việc bỏ qua exception có thể dẫn tới việc xử lý các vấn đề logic bị sai phạm.
  • Rủi ro về an ninh: Trong một số trường hợp, việc "giấu" exception có thể che giấu những lỗ hổng an ninh, khiến hệ thống trở nên dễ bị tấn công.

Và một vài vấn đề khác nữa.

Ví dụ về một trường hợp "ăn" exception

public static void main(String[] args) {
   int result = divide(10, 0);
   System.out.println(result);
}

0

Hiển nhiên việc không làm gì trong khối

public static void main(String[] args) {
    try {
        int result = divide(10, 0); // Might throw ArithmeticException
        System.out.println(result);
    } catch (ArithmeticException e) {
        e.printStackTrace(); // Handle and log the exception
        System.out.println(e.getMessage());
    }
}

1 là một hành động "ăn" exception, nó làm mất đi dấu vết về một vấn đề trong hệ thống.

Nếu không làm gì về mặt logic, ít nhất chúng ta cũng nên viết Log để hệ thống có thể truy vết trong trường hợp cần thiết.

public static void main(String[] args) {
   int result = divide(10, 0);
   System.out.println(result);
}

1

7.3. Sử dụng Checked Exception cho các tình huống cố gắng phục hồi

Bài toán giả định đặt ra là bạn đang muốn kết nối đến một cơ sở dữ liệu.

Sử dụng unchecked exception trong trường hợp này không bắt buộc người gọi phương thức phải xử lý ngoại lệ hoặc phải khai báo nó. Điều này dẫn đến việc người dùng hoặc ứng dụng có thể không biết về khả năng xảy ra ngoại lệ và không có cơ hội phục hồi từ sự cố đó.

public static void main(String[] args) {
   int result = divide(10, 0);
   System.out.println(result);
}

2

Sử dụng checked exception (ví dụ:

public class CustomError extends Error {
    public CustomError(String message) {
        super(message);
    }
    // Bạn cũng có thể thêm các hàm khởi tạo khác nếu cần thiết
}

8,

public class CustomError extends Error {
    public CustomError(String message) {
        super(message);
    }
    // Bạn cũng có thể thêm các hàm khởi tạo khác nếu cần thiết
}

9 hoặc một exception tùy chỉnh) cho tình huống trên, ví dụ:

public static void main(String[] args) {
   int result = divide(10, 0);
   System.out.println(result);
}

3

Khi sử dụng checked exception, calling method bị ép buộc phải xử lý ngoại lệ. Điều này thông báo cho chúng ta về khả năng xảy ra ngoại lệ và buộc chúng ta phải có cơ chế phục hồi, ví dụ: bằng cách thử kết nối lại hoặc hiển thị thông báo lỗi cho người dùng cuối.

7.4. Viết rõ chi tiết trong thông điệp Exception

Việc đưa ra đúng exception đôi khi vẫn chưa đủ ngữ cảnh cho chúng ta, do đó việc viết một message chính xác cũng cần được đảm bảo.

Ví dụ về cách dùng không chính xác

public static void main(String[] args) {
   int result = divide(10, 0);
   System.out.println(result);
}

4

Việc viết ra một message lỗi là Error occurred rất chung chung, khó hiểu và vì thế, có thể làm chúng ta mất thời gian để hiểu toàn bộ thông tin lỗi.

Thay vào đó, một message tốt sẽ dễ dàng cho chúng ta biết được bối cảnh đang diễn ra.

public static void main(String[] args) {
   int result = divide(10, 0);
   System.out.println(result);
}

5

Kết luận về Exception

Exception, hay ngoại lệ, là một sự kiện bất thường xảy ra trong quá trình thực thi chương trình và có thể làm gián đoạn quá trình này. Các exception xuất phát từ nhiều nguyên nhân khác nhau, chẳng hạn như lỗi người dùng, lỗi phần cứng, hoặc lỗi logic trong mã nguồn.

Mục tiêu chính của việc sử dụng exception là cung cấp một cơ chế để phát hiện và xử lý các tình huống bất thường mà không làm gián đoạn toàn bộ chương trình. Hơn nữa, nó giúp mã nguồn trở nên gọn gàng, dễ đọc và dễ bảo trì hơn bằng cách tách biệt giữa logic chính của chương trình và logic xử lý lỗi.

Để triển khai exception một cách hiệu quả, người lập trình nên tuân theo một số nguyên tắc quan trọng.

  • Sử dụng exception có ý nghĩa: Chọn loại exception phù hợp với tình huống cụ thể.
  • Không "ăn" exception: Tránh việc bắt exception mà không xử lý hoặc không ghi log.
  • Sử dụng checked exception cho các tình huống có thể phục hồi, và unchecked exception cho các lỗi nghiêm trọng không thể phục hồi.
  • Cung cấp thông tin chi tiết trong thông điệp exception: Điều này giúp debug và tìm hiểu nguyên nhân của lỗi.

Exception là một công cụ mạnh mẽ trong lập trình, cho phép ta xử lý các tình huống không mong muốn một cách linh hoạt và hiệu quả. Khi được sử dụng đúng cách, exception không chỉ giúp mã nguồn trở nên sạch sẽ và dễ đọc hơn, mà còn tăng cường độ bền và ổn định của ứng dụng.

Tài liệu tham khảo:

  1. Exception in Java - Geeksforgeeks
  2. Exception handling - wiki
  3. Error - Oracle
  4. Throwable - Oracle
  5. Exception - Oracle
  6. Best practice to handle exception in Java - Geeksforgeeks

Exception trong Java đóng vai trò quan trọng trong việc xử lý lỗi và ngoại lệ, giúp quản lý lỗi một cách hiệu quả. Nắm vững cách sử dụng và xử lý exception là một kỹ năng cần thiết cho mọi lập trình viên Java.

Bài viết đã giới thiệu khái niệm, cách sử dụng và cách xử lý exception, đồng thời chia sẻ những lời khuyên quan trọng khi làm việc với lỗi và ngoại lệ. Nắm bắt điều này sẽ giúp bạn xây dựng ứng dụng Java ổn định và đáng tin cậy hơn. Hãy tiếp tục rèn luyện và áp dụng kiến thức này vào công việc lập trình hàng ngày của bạn nhé!

Bạn hãy thường xuyên theo dõi các bài viết hay về Lập Trình & Dữ Liệu trên 200Lab Blog nhé. Cũng đừng bỏ qua những khoá học Lập Trình tuyệt vời trên 200Lab nè.