Language/Java

What is a NullPointerException, and how do I fix it? | Stack Overflow 정리

이웃비 2020. 12. 8. 03:31

 

본 내용은 Stack Overflow의 한국어 번역이 아니며, 개인적인 공부를 위해 Stack Overflow 질의를 정리한 내용입니다

 

 

 질문 

Null pointer Exception이란 무엇이며, 어떻게 해결해야 합니까?

What are Null Pointer Exceptions (java.lang.NullPointerException) and what causes them?

What methods/tools can be used to determine the cause so that you stop the exception from causing the program to terminate prematurely?

 


Null Pointer Exceptions(java.lang.NullPointerException) 이란 무엇이며 왜 생깁니까?

프로그램이 종료되는 것을 멈추기 위해 어떤 방법/도구를 사용해야 합니까?

 

 

 답변 

 

When you declare a reference variable (i.e. an object) you are really creating a pointer to an object. Consider the following code where you declare a variable of primitive type int:


 

기본형 int를 선언한 다음 코드를 참고하십시오.

 

 ** 기본형 (primitive type) : boolean, char, byte, short, int, long , float, double

int x;
x = 10;

 


In this example, the variable x is an int and Java will initialize it to 0 for you. When you assign it the value of 10 on the second line, your value of 10 is written into the memory location referred to by x.

But, when you try to declare a reference type, something different happens. Take the following code:

 


 

위 예시에서, 변수 x는 int이고, java는 int의 값을 0으로 초기화합니다.

두 번째 줄에서 x에 값 10을 할당하면, 값 10은 x를 가리키는 메모리 위치에 기록됩니다.

그러나, 참조형(reference type)을 선언하려고 하면, 상황이 다릅니다. 

 

** 참조형(reference type) : 기본형 8가지를 제외한 나머지 타입

 

Integer num;
num = new Integer(10);

 

The first line declares a variable named num, but it does not actually contain a primitive value yet. Instead, it contains a pointer (because the type is Integer which is a reference type). Since you have not yet said what to point to, Java sets it to null, which means "I am pointing to nothing".

 

In the second line, the new keyword is used to instantiate (or create) an object of type Integer, and the pointer variable num is assigned to that Integer object.

 


 

첫 번째 줄에선 num이라는 참조형 변수를 선언합니다. 그러나 아직 초기값이 들어가 있지 않은 상태입니다. 대신, 포인터를 가집니다. (integer 타입은 참조형이기 때문입니다).

아직 아무것도 가리키지 않으므로 자바는 null로 설정해놓는데, null로 설정한다는 것은 '아무것도 가리키지 않는다' 라는 뜻입니다.

 

두 번째 줄에서, 'new' 키워드는 Integer타입 객체를 만들기 위해 사용됩니다. 이때 'num'변수는 Integer객체를 가리키게 됩니다.

참조형 변수는 null 또는 주소를 값으로 갖습니다.

 

 ** 처음에 Integer num만 선언하면 메모리가 생성되기 전이라서 null로 초기화된다. 그러나 new연산자를 사용해 Integer타입을 생성한다고 선언하면 생성된 객체의 주소가 참조형 변수 num에 저장된다.  new로 인해 메모리를 생성하게 되는 것이다. 메모리가 생성되면 내부 인덱스 테이블에 메모리 주소를 매핑하는 참조값이 만들어진다. num을 프린트하면 그 참조값을 확인할 수 있다

 

 

 

The NullPointerException occurs when you declare a variable but did not create an object and assign it to the variable before trying to use the contents of the variable (called dereferencing). So you are pointing to something that does not actually exist.

 

Dereferencing usually happens when using . to access a method or field, or using [ to index an array.

 

 

NullPointerException은 변수를 선언은 했지만 객체를 생성하는 것도 아니고(new Integer), 값을 할당하지도 않고 (x=10) 변수 값을 사용하려고 할 때(dereferencing이라고 부릅니다) 발생합니다. 존재하지 않는 것을 가리키고 있다고 하는 것입니다.

 

** reference: 참조, 포인터 자체

dereference: 포인터가 가리키는 값 (Value)

 

 

Dereferencing 은 보통 . 이나 [ 를 사용할 때 발생합니다.

'.' 은 methods나 field에 접근하기 위한 것이고, '['는 배열에 사용합니다.(아래 예제 참고)

 

 

If you attempt to dereference num BEFORE creating the object you get a NullPointerException. In the most trivial cases, the compiler will catch the problem and let you know that "num may not have been initialized," but sometimes you may write code that does not directly create the object.

For instance, you may have a method as follows:


NullPointerException에러가 발생하지 않기 위해 가장 많이 쓰이는 해결법은, 보통 컴파일러가 num may not have been initialized 라고 알려주기도 하지만, 다음과 같이 객체를 직접 생성하지 않는 식으로 코드를 작성할 수 도 있습니다

 

public void doSomething(SomeObject obj) {
   //do something to obj, assumes obj is not null
   obj.myMethod();
}

 

In which case, you are not creating the object obj, but rather assuming that it was created before the doSomething() method was called. Note, it is possible to call the method like this:


이 경우 obj 객체생성하지 않습니다. 그러나 doSomething() 메서드가 쓰이기 전에 생성이 되었다고 가정했을 때의 경우입니다. 참고로 이렇게 doSomething() 메소드가 호출될 수도 있습니다

 

doSomething(null);

 

In which case, obj is null, and the statement obj.myMethod() will throw a NullPointerException.

 

If the method is intended to do something to the passed-in object as the above method does, it is appropriate to throw the NullPointerException because it's a programmer error and the programmer will need that information for debugging purposes.

In addition to NullPointerExceptions thrown as a result of the method's logic, you can also check the method arguments for null values and throw NPEs explicitly by adding something like the following near the beginning of a method:


이 경우에, obj는 null이며, obj.myMethod() 는 NullPointerException을 일으키게 됩니다.

 

만약 위처럼 전달된 object로 대해 뭔가 할 의도라면, NullPointerException 으로 넘기는 것은 적절항 방법입니다. 그것은 프로그래머가 디버깅을 목적으로 한 의도적인 에러이기 때문입니다

메서드의 논리로 인해 던져진 예외 외에도, 메서드의 시작 부근에 다음과 같은 것을 추가하여 null 값에 대한 메서드 인수를 확인하고 NPE를 명시적으로 던질 수 있다.(잘 이해 안됨. 대략 아래와 같이 obj가 null일 경우 에러 메시지를 지정해 같이 NPE로 던질 수 있다는 말인 듯)

 

//Throws an NPE with a custom error message if obj is null
Objects.requireNonNull(obj, "obj must not be null");

Note that it's helpful to say in your error message clearly which object cannot be null. The advantage of doing validation like this is that 1) you can return your own clearer error messages and 2) for the rest of the method you know that unless obj is reassigned, it is not null and can be dereferenced safely.

Alternatively, there may be cases where the purpose of the method is not solely to operate on the passed in object, and therefore a null parameter may be acceptable. In this case, you would need to check for a null parameter and behave differently. You should also explain this in the documentation. For example, doSomething() could be written as:


어떤 객체가 null이면 안되는지 에러 메시지에 명확하게 명시하는 것이 좋습니다.  

이렇게 할 때의 이점 : 

1) 명확한 오류 메시지를 반환한다

2) 값을 할당하지 않은(혹은 참조하지 않은) obj가 뭔지 알 수 있다

 

하지만 null이 허용될 경우가 있을 수 있으니 이 경우에는 null 을 확인하고 다르게 코딩해야 합니다. 또 설명도 곁들어야 합니다. 예를 들어 doSomething 메서드는 다음과 같이 코딩할 수 있습니다.

 

/**
  * @param obj An optional foo for ____. May be null, in which case 
  *  the result will be ____.
  */
public void doSomething(SomeObject obj) {
    if(obj == null) {
       //do something
    } else {
       //do something else
    }
}