Fix java.lang.NullPointerException trong Java

beginner Java2026-03-17| Java 8+ (JDK/JRE), mọi hệ điều hành (Linux, macOS, Windows), Spring Boot, các dự án Maven/Gradle

Error Message

java.lang.NullPointerException
#java#nullpointerexception#exception

Lỗi

Exception in thread "main" java.lang.NullPointerException
    at com.example.UserService.getUsername(UserService.java:42)
    at com.example.Main.main(Main.java:15)

Trong Spring Boot bạn có thể thấy dạng mô tả chi tiết hơn được thêm vào từ Java 14:

java.lang.NullPointerException: Cannot invoke "com.example.User.getName()" because "user" is null

NPE là exception runtime phổ biến nhất trong Java — xuất hiện trong hầu hết mọi codebase production. JVM ném ra nó khi bạn cố dùng một tham chiếu trỏ đến null: gọi method, truy cập field, hoặc truy cập phần tử mảng trên một đối tượng null.

Tại Sao Xảy Ra

Stack trace của bạn có câu trả lời. Hãy xem nó trước khi làm bất cứ điều gì khác. Nguyên nhân gốc rễ hầu như luôn nằm trong một trong các nhóm sau:

  • Một method trả về null và bạn dùng kết quả mà không kiểm tra
  • Một field chưa bao giờ được khởi tạo (quên new)
  • Một autowired bean là null — Spring context không được load trong tests
  • Một phần tử của collection hoặc mảng là null
  • Method chaining trên thứ gì đó có thể trả về null

Cách Sửa Từng Bước

Bước 1: Đọc Stack Trace

Đừng bỏ qua bước này. Stack trace cho bạn biết chính xác file và số dòng. Tìm dòng trên cùng thuộc code của bạn — không phải JDK hay framework nội bộ:

at com.example.UserService.getUsername(UserService.java:42)

Đi đến dòng 42. Đó là nơi null được truy cập. Xác định đối tượng nào trên dòng đó là null.

Bước 2: Thêm Kiểm Tra Null (Sửa Nhanh)

Cách vá nhanh nhất — thêm guard trước khi dùng giá trị:

// Trước (crash nếu user là null)
String name = user.getName();

// Sau
if (user != null) {
    String name = user.getName();
} else {
    log.warn("User là null, bỏ qua tra cứu tên");
}

Bước 3: Dùng Optional (Sửa Gọn Hơn)

Optional của Java 8 giúp xử lý null rõ ràng và có thể chain. Dùng nó khi một method có thể hợp lệ không trả về kết quả:

// Trả về Optional thay vì null
public Optional<User> findUser(Long id) {
    User user = dao.find(id); // trả về null nếu không tìm thấy
    return Optional.ofNullable(user);
}

// Tại điểm gọi — không cần kiểm tra null
String name = findUser(userId)
    .map(User::getName)
    .orElse("Unknown");

Bước 4: Sửa Field Chưa Khởi Tạo

NPE khi truy cập field thường có nghĩa là field chưa bao giờ được gán. Khởi tạo nó ngay tại khai báo:

// Lỗi — list chưa bao giờ được khởi tạo
public class OrderService {
    private List<Order> orders;

    public void addOrder(Order o) {
        orders.add(o); // NPE ở đây
    }
}

// Đã sửa
public class OrderService {
    private List<Order> orders = new ArrayList<>();

    public void addOrder(Order o) {
        orders.add(o); // hoạt động
    }
}

Bước 5: Sửa NPE Khi Autowiring Spring

Lỗi Spring kinh điển — bạn tự khởi tạo class bằng new thay vì để Spring quản lý:

// Lỗi — Spring không quản lý instance này, repo là null bên trong
UserService service = new UserService();
service.doSomething(); // NPE bên trong

// Đã sửa — để Spring inject
@Autowired
private UserService service;

Trong unit test khi Spring context không được load, mock các dependency bằng Mockito:

@ExtendWith(MockitoExtension.class)
class UserServiceTest {
    @Mock
    private UserRepository repo;

    @InjectMocks
    private UserService service;

    @Test
    void testGetUser() {
        when(repo.findById(1L)).thenReturn(new User("Alice"));
        assertEquals("Alice", service.getUsername(1L));
    }
}

Bước 6: Sửa NPE Khi Method Chaining

Một chain như a.getB().getC().getValue() sẽ NPE nếu bất kỳ bước nào trả về null. Bọc nó với Optional:

// Rủi ro — bất kỳ bước nào trong chain có thể là null
String city = order.getCustomer().getAddress().getCity();

// An toàn với Optional
String city = Optional.ofNullable(order)
    .map(Order::getCustomer)
    .map(Customer::getAddress)
    .map(Address::getCity)
    .orElse("N/A");

Bước 7: Bật Thông Báo NPE Chi Tiết (Java 14+)

Từ Java 14, JVM có thể cho bạn biết chính xác phần nào của biểu thức là null — không chỉ số dòng. Tính năng này được bật mặc định từ Java 15 trở đi:

# Bật rõ ràng trên Java 14
java -XX:+ShowCodeDetailsInExceptionMessages YourApp

# Kết quả lỗi trở nên cụ thể hơn nhiều:
# Cannot invoke "Address.getCity()" because the return value
# of "Customer.getAddress()" is null

Trên Java 8–13, thêm structured logging xung quanh chain đáng ngờ để tự thu hẹp phạm vi.

Kiểm Tra Sau Khi Sửa

  • Chạy lại code path đã kích hoạt NPE — xác nhận nó không còn throw nữa
  • Viết unit test bao phủ trường hợp null rõ ràng:
@Test
void shouldHandleNullUser() {
    when(repo.findById(99L)).thenReturn(null);
    String result = service.getUsername(99L);
    assertEquals("Unknown", result); // trả về giá trị mặc định, không phải exception
}
  • Kiểm tra logs — nếu bạn đã thêm log.warn trên null path, xác nhận nó xuất hiện mà không crash downstream

Tham Khảo Nhanh: Các Pattern Phổ Biến

// Objects.requireNonNull — lỗi ngay lập tức với thông báo rõ ràng
public UserService(UserRepository repo) {
    this.repo = Objects.requireNonNull(repo, "repo không được null");
}

// Trả về collection rỗng thay vì null
public List<User> getUsers() {
    List<User> result = repo.findAll();
    return result != null ? result : Collections.emptyList();
}

// So sánh chuỗi — đặt hằng số ở bên trái
if ("ACTIVE".equals(status)) { ... } // an toàn ngay cả khi status là null

Mẹo Debug

  • Đặt breakpoint tại dòng NPE và kiểm tra các biến trong IDE — bạn sẽ thấy tham chiếu null ngay lập tức
  • NPE chỉ xảy ra trên production? Thêm dòng log tạm thời trước điểm crash để in biến đáng ngờ, rồi redeploy
  • IntelliJ IDEA tô sáng code dễ bị NPE bằng annotation @NotNull / @Nullable — bật trong Settings → Editor → Inspections → Java → Probable bugs
  • Để kiểm tra null tại compile-time, thử dùng @NonNull của Lombok hoặc nullness checker của Checker Framework

Related Error Notes