Null Pointer Exceptions In Java

JP·2022년 3월 13일
0

자바

목록 보기
7/10

이 포스팅은 Null Pointer Exceptions In Java를 보고 쓴 글입니다.

자바에서 아마도 가장 많은 익셉션을 발생시키는 것은 바로 이 Null Pointer Exception 이 아닐까 생각합니다.

null의 등장

영국의 컴퓨터 과학자인 토니호어(Tony Hoare)가 1985년 알골이라는 프로그래밍 언어를 설계하면서 처음 등장했다.

당시에 그는 null참조라는 개념이 값이 없는 상황을 가장 단순하게 구현할 수 있는 방법 이라고 생각했다.

하지만 시간이 흘러 2009년, 한 소프트웨어 컨퍼런스에서 그는 자신이 고안한 null 참조를 10억 달러(1조 이상)짜리 실수 라고 표현하며 사과했다.

단순히 구현하기 쉬웠기 때문에 null참조를 구상했지만, 이로 인해 수많은 오류, 취약성 및 시스템 충돌이 생기고 피해가 막대했기 때문이다.

자바에서의 null이란?

  • null은 참조형의 기본값이다.
    - int가 0, boolean이 true를 기본형으로 갖는 것처럼 참조형의 기본 값은 null이다.
  • 데이터 타입은 아니다
    - 모든 참조형 레퍼런스에 할당할 수 있는 특수한 값이다.
    - 형변환 또한 허용하는 특이한 값이다
    - String str = (String)null;
    - Integer val = (Integer)null;

왜 문제인가?

  • 레퍼런스를 존재하지 안흔 값을 나타내는 null 참조는 그 자체로 에러의 근원이다.
    - null 참조를 남발하다가는 nullPointerException을 쉽게 만날 수 있다.
  • 예외 상황을 피하기 위해 null 체크 코드를 추가하다 보면 깔끔하게 작성된 코드를 기대하기 어려워 진다.

null pointer exception이란?

  • 포인터의 개념은 c에서 온 것으로, 메모리상의 주소를 가리키는 변수를 칭한다.
    - 메모리상의 객체가 가리키는 포인터가 null(참조가 없음)을 뜻한다는 의미에서 null-pointer
  • 널포인터 익셉션은 프로그램 실행 중에 발생하는 런타임 익셉션이다.
    - 컴파일 시점에서 에러를 파악하기가 어려워서 예외의 발생 시점을 알아채기 어렵다.

널 포인터 익셉션은 왜 발생하고, 어떻게 고쳐야 할지 알아보겠습니다.

public class Test{
	public static void main(String[] args) {
    	Cat myCat = new Cat();
        myCat.makeNoise(); // 울음 소리를 낸다.
    }
}

위의 소스코드에서 myCat이라는 객체는 기본 생성자를 통해 새로 할당되었습니다.
메모리 어딘가에 주소를 가지고 있을거고 myCat.makeNoise() 를 하면 해당 메서드까지 제대로 수행하겠죠

하지만

public class Test{
	public static void main(String[] args) {
    	Cat myCat = null;
        myCat.makeNoise(); // NullPointerException
    }
}

위와 같이 myCat을 null로 지정하면 우리는 Null Pointer Exception 을 만나게 됩니다.

여기서 말하는 널포인터 익셉션이란 말 그대로 참조하고 있는 pointer, 즉 참조하고 있는 주소가 null, 빈 상자와 같이 아무것도 없다는 뜻입니다.
메모리상에 가리키는 myCat이 아무것도 없기에 당연히 makeNoise()라는 함수도 실행을 못할테고, 그로 인해 익셉션이 터지는 것이죠.

그렇다면 널 포인터 익셉션을 피하기 위한 방법은 무엇이 있을까요?

Boolean 대신 boolean 쓰기

Boolean은 boolean의 래퍼클래스로 다양한 함수들과 Null을 할당할 수 있기까지 합니다.
이는 당연히 널포인터 익셉션의 문제를 야기할 수 있는 원인이 될 것 같습니다.
따라서 Boolean 대신 원시타입인 boolean을 써서 예방할 수 있습니다.

List 에 null 대신 empty list로 초기화 하기

List<String> someStrings = null;
for(String string: someStrings){
	System.out.println(string); // 널포인터 익셉션!
}

위와같이 리스트를 null로 초기화 할 경우 당연히 null 포인터 익셉션이 발생할 수 있습니다.
대신에 이를 비어있는 리스트로 초기화 시켜준다면 문제를 예방할 수 있습니다

List<String> someStrings = new ArrayList<>();
for(String string: someStrings){
	System.out.println(string);
}

null check하기

if-else 구문으로 null이 아닌 경우에만 로직을 실행할 수 있게 코드를 변경하기

public class Test{
	public static void main(String[] args) {
    	Cat myCat = null;
        if(myCat != null ){
        	myCat.makeNoise();
        }
    }
}

하지만 이는 불필요한 if 절을 써야한다

List<Cat> cats = null;
cats.getFirstCatNameLength();
private static int makeNoise(List<Cat> cats){
	returns cats.get(0).getName().length();
}

만약 다음과 같은 함수가 존재한다면 불필요한 if절들이 계속 붙을 것이고 매우 못생긴 코드가 될 것이다.
이 코드를 위에서 말한 방법대로 null check를 한다면 아래와 같은 코드가 될 것이다.

List<Cat> cats = null;
cats.getFirstCatNameLength();
private static int makeNoise(List<Cat> cats){
	if( cats!= null &&
    	cats.get(0) != null &&
        cats.get(0).getName() != null){
          returns cats.get(0).getName().length();    
    }
    return 0;
}

벌써부터 짜증이 몰려오는 코드가 되어버렸다!

이를 예방하기 위해서는 위에 말한것과 같이
1. null로 할당하지 않기
2. collection 클래스들은 empty list로 초기화 해주기
3. null로 초기화를 했다면 해당 객체가 사용되기 전에 반드시 객체를 할당해주기
4. Optional 래퍼 클래스로 감싸기가

Optional래퍼 클래스를 사용하는것은 따로 주제를 잡고 글을 써야할 것 같아서 이 장에선 패스.

equals()를 쓸 때 null이 나올것같은 걸 뒤에 먼저 두기

Cat myCat = new Cat();
myCat.setName("firstCat");

Cat myNewCat = new Cat();
myNewCat.setName(null);

if(myNewCat.getName().equals(myCat.getName())){
  //some logic
}

위와 같은 코드는 실행순서에 따라
널포인터 익셉션이 먼저 발생하게 되어있다.
코드의 위치를 다음과 같이 바꾸어주기만 하더라도 문제를 피할 수 있다!

Cat myCat = new Cat();
myCat.setName("firstCat");

Cat myNewCat = new Cat();
myNewCat.setName(null);

if(myCat.getName().equals( myNewCat.getName())){
  //some logic
}

소스코드 예제

https://github.com/JPWon-1/npe_example

그 외...

자바14부터는 라인뿐만 아니라 어떤 부분에서 널포인터가 발생했는지도 알려준다고 한단다..


참고

http://www.tcpschool.com/c/c_pointer_intro (포인터)
https://madplay.github.io/post/what-is-null-in-java (널은 무엇인가)
https://youtu.be/lm72_HCd17s (What is NPE?)

profile
to Infinity and b

0개의 댓글