Java 개발을 하면서 가장 흔히 접하는 예외 중 하나가 바로 **Null Pointer Exception (NPE)**입니다. 이번 글에서는 NPE의 개념, 원인, 예제, 해결 방법, 그리고 방지하는 방법까지 자세히 살펴보겠습니다.
1. Null Pointer Exception이란?
**Null Pointer Exception (NPE)**은 Java에서 null 값을 참조하려고 할 때 발생하는 런타임 예외입니다. 즉, null인 객체에 대해 메서드를 호출하거나 필드에 접근하려고 하면 NullPointerException이 발생합니다.
NPE는 java.lang.NullPointerException 클래스를 통해 제공되며, 체크 예외(Checked Exception)가 아닌 **런타임 예외(Runtime Exception)**입니다. 따라서 개발자가 직접 핸들링하지 않으면 프로그램이 강제 종료될 수 있습니다.
2. Null Pointer Exception이 발생하는 원인
2.1. null 값을 참조하는 객체에서 메서드 호출
public class NullPointerExample {
public static void main(String[] args) {
String str = null;
System.out.println(str.length()); // NullPointerException 발생
}
}
- str이 null인데 length() 메서드를 호출하려고 하면서 예외가 발생합니다.
2.2. null을 포함하는 배열 요소 접근
public class NullPointerArray {
public static void main(String[] args) {
String[] arr = new String[3];
arr[0] = null;
System.out.println(arr[0].length()); // NullPointerException 발생
}
}
- arr[0]이 null인데 .length()를 호출하려고 하면 NPE가 발생합니다.
2.3. null을 반환하는 메서드 호출
public class NullPointerMethod {
public static String getNullString() {
return null;
}
public static void main(String[] args) {
String str = getNullString();
System.out.println(str.length()); // NullPointerException 발생
}
}
- getNullString() 메서드가 null을 반환했지만, 이를 체크하지 않고 .length()를 호출하면 예외가 발생합니다.
2.4. null 값이 포함된 Map에서 값 조회
import java.util.HashMap;
import java.util.Map;
public class NullPointerMap {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("key", null);
System.out.println(map.get("key").length()); // NullPointerException 발생
}
}
- map.get("key")가 null인데 .length()를 호출하면서 NPE가 발생합니다.
2.5. null 객체에서 필드 접근
public class NullPointerField {
static class Person {
String name;
}
public static void main(String[] args) {
Person person = null;
System.out.println(person.name); // NullPointerException 발생
}
}
- person 객체가 null인데 person.name을 호출하려고 하면 NPE가 발생합니다.
3. Null Pointer Exception 해결 방법
3.1. null 체크 후 사용
public class NullCheckExample {
public static void main(String[] args) {
String str = null;
if (str != null) {
System.out.println(str.length());
} else {
System.out.println("str is null");
}
}
}
- null 체크를 하면 NPE를 방지할 수 있습니다.
3.2. Optional 사용 (Java 8 이상)
import java.util.Optional;
public class OptionalExample {
public static void main(String[] args) {
String str = null;
Optional<String> optionalStr = Optional.ofNullable(str);
System.out.println(optionalStr.map(String::length).orElse(0));
}
}
- Optional을 사용하면 null을 안전하게 처리할 수 있습니다.
3.3. 기본값 설정
public class DefaultValueExample {
public static void main(String[] args) {
String str = null;
System.out.println((str != null) ? str.length() : 0);
}
}
- 삼항 연산자를 활용하여 null일 때 기본값을 설정할 수 있습니다.
3.4. Objects.requireNonNull() 사용
import java.util.Objects;
public class RequireNonNullExample {
public static void main(String[] args) {
String str = null;
System.out.println(Objects.requireNonNull(str, "str은 null일 수 없습니다."));
}
}
- Objects.requireNonNull()을 사용하면 null일 경우 명확한 예외 메시지를 제공할 수 있습니다.
4. Null Pointer Exception을 방지하는 방법
4.1. @NonNull 어노테이션 활용
Lombok 또는 Java의 @NonNull 어노테이션을 활용하면 컴파일 단계에서 null을 방지할 수 있습니다.
import lombok.NonNull;
public class NonNullExample {
public void print(@NonNull String str) {
System.out.println(str.length());
}
public static void main(String[] args) {
NonNullExample example = new NonNullExample();
example.print(null); // 컴파일 단계에서 경고
}
}
4.2. Optional 적극 활용
Java 8 이상에서는 Optional을 활용하여 null 처리를 더욱 명확하게 할 수 있습니다.
4.3. final 키워드 활용
final 키워드를 사용하여 객체가 null이 되지 않도록 강제할 수 있습니다.
public class FinalExample {
private final String message;
public FinalExample() {
this.message = "Hello, World!";
}
public String getMessage() {
return message;
}
}
- message는 한 번 초기화되면 null이 될 수 없습니다.
4.4. 방어적 프로그래밍
- 메서드에서 null이 반환될 가능성이 있다면 호출하는 쪽에서 반드시 체크하는 습관을 가지세요.
- API를 설계할 때 null을 반환하는 대신 **빈 객체(empty object)**나 Optional을 반환하는 것이 좋습니다.
5. 결론
Null Pointer Exception은 Java 개발에서 가장 흔한 예외 중 하나이지만, 올바른 코딩 습관과 방어적 프로그래밍을 통해 충분히 방지할 수 있습니다. 다음과 같은 원칙을 지키면 NPE를 줄일 수 있습니다.
✅ null 체크를 철저히 한다.
✅ Optional을 적극 활용한다.
✅ Objects.requireNonNull()을 사용한다.
✅ @NonNull 어노테이션을 활용하여 코드 품질을 높인다.
✅ final 키워드를 활용하여 필드가 null이 되지 않도록 한다.
'백엔드' 카테고리의 다른 글
[자바/JAVA] DAO, DTO, VO, Entity의 차이점과 역할 (1) | 2025.02.11 |
---|---|
[DB] 관계형(RDB) vs 비관계형(NoSQL) 데이터베이스 비교 (1) | 2025.02.10 |
[자바/JAVA] 자바 스프링 AOP(Aspect-Oriented Programming) 이해하기 (0) | 2025.02.08 |
[자바/JAVA] JPA N+1 문제란? (1) | 2025.02.05 |
[자바/JAVA] JPA(Java Persistence API) 개념 정리🚀 (2) | 2025.02.02 |