웹사이트 검색

Java NullPointerException - 감지, 수정 및 모범 사례


java.lang.NullPointerException은 가장 널리 사용되는 자바 웹 애플리케이션 중 하나입니다.

java.lang.NullPointerException

NullPointerException은 런타임 예외이므로 프로그램에서 잡을 필요가 없습니다. NullPointerException은 개체가 필요한 null에서 일부 작업을 수행하려고 할 때 애플리케이션에서 발생합니다. Java 프로그램에서 NullPointerException에 대한 일반적인 이유 중 일부는 다음과 같습니다.

  1. 개체 인스턴스에서 메서드를 호출하지만 런타임에 개체가 null입니다.
  2. 런타임에 null인 개체 인스턴스의 변수에 액세스합니다.
  3. 프로그램에서 null 던지기
  4. 인덱스 액세스 또는 null인 배열의 인덱스 값 수정
  5. 런타임에 null인 배열의 길이를 확인합니다.

자바 NullPointerException 예

Java 프로그램에서 NullPointerException의 몇 가지 예를 살펴보겠습니다.

1. 인스턴스 메서드 호출 시 NullPointerException 발생

public class Temp {

	public static void main(String[] args) {

		Temp t = initT();
		
		t.foo("Hi");
		
	}

	private static Temp initT() {
		return null;
	}

	public void foo(String s) {
		System.out.println(s.toLowerCase());
	}
}

위의 프로그램을 실행하면 다음과 같은 NullPointerException 오류 메시지가 발생합니다.

Exception in thread "main" java.lang.NullPointerException
	at Temp.main(Temp.java:7)

여기에서 "t\가 null이기 때문에 t.foo(\Hi\); 문에서 NullPointerException이 발생합니다.

2. null 개체의 필드에 액세스/수정하는 동안 Java NullPointerException 발생

public class Temp {

	public int x = 10;
	
	public static void main(String[] args) {

		Temp t = initT();
		
		int i = t.x;
		
	}

	private static Temp initT() {
		return null;
	}

}

위의 프로그램은 다음 NullPointerException 스택 추적을 발생시킵니다.

Exception in thread "main" java.lang.NullPointerException
	at Temp.main(Temp.java:9)

여기서 "t\가 null이기 때문에 int i = t.x; 문에서 NullPointerException이 발생합니다.

3. 메서드 인수에 null이 전달될 때 Java NullPointerException 발생

public class Temp {

	public static void main(String[] args) {

		foo(null);

	}

	public static void foo(String s) {
		System.out.println(s.toLowerCase());
	}
}

이는 null 인수를 전달하는 호출자이기 때문에 java.lang.NullPointerException의 가장 일반적인 발생 중 하나입니다.

아래 이미지는 Eclipse IDE에서 위의 프로그램을 실행할 때 null 포인터 예외를 보여줍니다.

4. null이 발생한 경우 java.lang.NullPointerException

public class Temp {

	public static void main(String[] args) {

		throw null;
	}

}

아래는 throw null; 문으로 인해 NullPointerException을 보여주는 위 프로그램의 예외 스택 추적입니다.

Exception in thread "main" java.lang.NullPointerException
	at Temp.main(Temp.java:5)

5. null 배열의 길이를 가져올 때 NullPointerException 발생

public class Temp {

	public static void main(String[] args) {

		int[] data = null;
		
		int len = data.length;
	}

}
Exception in thread "main" java.lang.NullPointerException
	at Temp.main(Temp.java:7)

6. null 배열의 인덱스 값 접근 시 NullPointerException 발생

public class Temp {

	public static void main(String[] args) {

		int[] data = null;
		
		int len = data[2];
	}

}
Exception in thread "main" java.lang.NullPointerException
	at Temp.main(Temp.java:7)

7. null 개체에서 동기화될 때 NullPointerException 발생

public class Temp {

	public static String mutex = null;
	
	public static void main(String[] args) {

		synchronized(mutex) {
			System.out.println("synchronized block");
		}		
	}
}

synchronized(mutex)는 \mutex 객체가 null이기 때문에 NullPointerException을 발생시킵니다.

8. HTTP 상태 500 java.lang.NullPointerException

때때로 우리는 "HTTP 상태 500 – 내부 서버 오류”와 같은 오류 메시지와 java.lang.NullPointerException과 같은 근본 원인과 함께 Java 웹 응용 프로그램에서 오류 페이지 응답을 받습니다.

이를 위해 Spring MVC Example 프로젝트를 수정하여 아래와 같이 HomeController 메소드를 변경하였습니다.

@RequestMapping(value = "/user", method = RequestMethod.POST)
	public String user(@Validated User user, Model model) {
		System.out.println("User Page Requested");
		System.out.println("User Name: "+user.getUserName().toLowerCase());
		System.out.println("User ID: "+user.getUserId().toLowerCase());
		model.addAttribute("userName", user.getUserName());
		return "user";
	}

아래 이미지는 웹 애플리케이션 응답에서 발생한 오류 메시지를 보여줍니다.

다음은 예외 스택 추적입니다.

HTTP Status 500 – Internal Server Error

Type Exception Report

Message Request processing failed; nested exception is java.lang.NullPointerException

Description The server encountered an unexpected condition that prevented it from fulfilling the request.

Exception

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.NullPointerException
	org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:982)
	org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:872)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:661)
	org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
	org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
Root Cause

java.lang.NullPointerException
	com.journaldev.spring.controller.HomeController.user(HomeController.java:38)
	sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	java.lang.reflect.Method.invoke(Method.java:498)

user.getUserId()가 null을 반환하기 때문에 근본 원인은 user.getUserId().toLowerCase() 문에서 NullPointerException입니다.

java.lang.NullPointerException을 감지하는 방법

NullPointerException을 감지하는 것은 매우 쉽습니다. 예외 추적을 보면 예외의 클래스 이름과 줄 번호가 표시됩니다. 그런 다음 코드를 보고 NullPointerException을 일으키는 null이 무엇인지 확인하십시오. 위의 모든 예를 보면 null 포인터 예외를 일으키는 원인이 스택 추적에서 매우 분명합니다.

NullPointerException을 수정하는 방법

java.lang.NullPointerException은 확인되지 않은 예외이므로 포착할 필요가 없습니다. null 포인터 예외는 null 검사 및 예방적 코딩 기술을 사용하여 방지할 수 있습니다. java.lang.NullPointerException을 피하는 방법을 보여주는 아래 코드 예를 살펴보세요.

if(mutex ==null) mutex =""; //preventive coding
		
synchronized(mutex) {
	System.out.println("synchronized block");
}
//using null checks
if(user!=null && user.getUserName() !=null) {
System.out.println("User Name: "+user.getUserName().toLowerCase());
}
if(user!=null && user.getUserName() !=null) {
	System.out.println("User ID: "+user.getUserId().toLowerCase());
}

NullPointerException을 피하기 위한 코딩 모범 사례

1. 아래 함수를 고려하고 널 포인터 예외를 일으키는 시나리오를 찾아봅시다.

public void foo(String s) {
    if(s.equals("Test")) {
	System.out.println("test");
    }
}

인수가 null로 전달되는 경우 NullPointerException이 발생할 수 있습니다. NullPointerException을 피하기 위해 아래와 같은 메소드를 작성할 수 있습니다.

public void foo(String s) {
	if ("Test".equals(s)) {
		System.out.println("test");
	}
}

2. 인수에 대한 null 검사를 추가하고 필요한 경우 IllegalArgumentException을 발생시킬 수도 있습니다.

public int getArrayLength(Object[] array) {
	
	if(array == null) throw new IllegalArgumentException("array is null");
	
	return array.length;
}

3. 아래 예제 코드와 같이 삼항 연산자를 사용할 수 있습니다.

String msg = (str == null) ? "" : str.substring(0, str.length()-1);

4. toString() 메서드 대신 String.valueOf() 사용 예를 들어 check PrintStream println() 메서드 코드는 아래와 같이 정의되어 있습니다.

public void println(Object x) {
        String s = String.valueOf(x);
        synchronized (this) {
            print(s);
            newLine();
        }
    }

아래 코드 스니펫은 toString() 대신 valueOf() 메서드를 사용하는 예를 보여줍니다.

Object mutex = null;

//prints null
System.out.println(String.valueOf(mutex));

//will throw java.lang.NullPointerException
System.out.println(mutex.toString());

5. 빈 목록, 빈 문자열 등과 같이 가능하면 null이 아닌 빈 개체를 반환하는 메서드를 작성합니다.

6. NullPointerException을 피하기 위해 컬렉션 클래스에 정의된 몇 가지 메서드가 있습니다. 이를 사용해야 합니다. 예를 들어 contains(), containsKey() 및 containsValue()가 있습니다.

참조: API 문서