문제

Null 포인터 예외는 무엇입니까 (java.lang.NullPointerException) 그리고 그들을 원인하는 것은 무엇입니까?

프로그램이 조기에 종료되지 않도록 원인을 결정하기 위해 어떤 방법/도구를 사용할 수 있습니까?

도움이 되었습니까?

해결책

참조 변수 (예 : 객체)를 선언 할 때 실제로 객체에 대한 포인터를 만들고 있습니다. 원시 유형의 변수를 선언하는 다음 코드를 고려하십시오. int:

int x;
x = 10;

이 예에서는 변수입니다 x 이다 int 그리고 Java는이를 초기화합니다 0 당신을 위한. 당신이 그것을 할당 할 때 10 두 번째 줄에서 귀하의 값 10 참조 된 메모리 위치에 기록됩니다 x.

그러나 참조를 선언하려고 할 때 유형, 다른 일이 일어납니다. 다음 코드를 선택하십시오.

Integer num;
num = new Integer(10);

첫 번째 줄은 변수 이름을 선언합니다 num, 그러나 실제로는 아직 원시적 값을 포함하지 않습니다. 대신, 그것은 포인터가 포함되어 있습니다 (유형이 Integer 참조 유형). 당신이 아직 무엇을 가리킬지 말하지 않았기 때문에 Java는 그것을 설정합니다. null, 즉 "나는 지적하고있다 아무것도 아님".

두 번째 줄에서 new 키워드는 유형의 객체를 인스턴스화 (또는 작성)하는 데 사용됩니다. Integer 그리고 포인터 변수 num 그것에 할당됩니다 Integer 물체.

그만큼 NullPointerException 변수를 선언하지만 객체를 생성하지 않았을 때 발생합니다. 그래서 당신은 실제로 존재하지 않는 것을 가리키고 있습니다.

당신이 불신을 시도하는 경우 num 객체를 만들기 전에 a NullPointerException. 가장 사소한 경우, 컴파일러는 문제를 해결하고이를 알려줍니다. "num may not have been initialized, "그러나 때로는 객체를 직접 생성하지 않는 코드를 작성할 수도 있습니다.

예를 들어 다음과 같이 메소드가있을 수 있습니다.

public void doSomething(SomeObject obj) {
   //do something to obj
}

어떤 경우에는 객체를 만들지 않습니다. obj, 오히려 그것이 전에 만들어 졌다고 가정합니다. doSomething() 방법이 호출되었습니다. 다음과 같은 방법을 호출 할 수 있습니다.

doSomething(null);

어떤 경우에는 obj ~이다 null. 이 방법이 통과 된 물체에 무언가를 수행하려는 경우 NullPointerException 프로그래머 오류이며 프로그래머는 디버깅 목적으로 해당 정보가 필요하기 때문입니다.

대안 적으로, 방법의 목적이 전달 된 객체에서만 작동하지 않는 경우가있을 수 있으므로 널 매개 변수가 허용 될 수있다. 이 경우 확인해야합니다. 널 매개 변수 다르게 행동합니다. 문서에서도 설명해야합니다. 예를 들어, 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
    }
}

드디어, 스택 추적을 사용하여 예외를 정확히 찾아 내고 원인

다른 팁

NullPointerExceptions는 객체를 참조하는 것처럼 메모리 (null)의 위치가없는 참조를 사용하려고 할 때 발생하는 예외입니다. NULL 참조에서 메소드를 호출하거나 NULL 참조 필드에 액세스하려고 시도하면 NullPointerException. 이것들은 가장 일반적이지만 다른 방법은 NullPointerException Javadoc 페이지.

아마도 가장 빠른 예제 코드는 NullPointerException 다음과 같습니다.

public class Example {

    public static void main(String[] args) {
        Object obj = null;
        obj.hashCode();
    }

}

내부의 첫 번째 줄에 main, 나는 명시 적으로 설정하고 있습니다 Object 참조 obj 동일 null. 이것은 참조가 있음을 의미하지만 어떤 객체도 지적하지 않습니다. 그 후, 나는 메소드를 호출하여 객체를 가리키는 것처럼 참조를 취급하려고합니다. 이것은 a NullPointerException 참조가 가리키는 위치에서 실행할 코드가 없기 때문입니다.

(이것은 기술적이지만, 나는 그것이 언급하고 있다고 생각합니다. null을 가리키는 참조는 유효하지 않은 메모리 위치를 가리키는 C 포인터와 동일하지 않습니다. 널 포인터는 말 그대로 포인팅되지 않습니다. 어딘가에, 이것은 유효하지 않은 위치를 가리키는 것과 미묘하게 다릅니다.)

NullPointerException이란 무엇입니까?

시작하기에 좋은 곳은 Javadocs. 그들은 이것을 다루었습니다.

객체가 필요한 경우 응용 프로그램이 NULL을 사용하려고 시도 할 때 던졌습니다. 여기에는 다음이 포함됩니다.

  • 널 객체의 인스턴스 메소드를 호출합니다.
  • 널 물체의 필드에 액세스하거나 수정합니다.
  • 널의 길이를 배열 인 것처럼 취합니다.
  • 널의 슬롯에 배열 인 것처럼 액세스하거나 수정합니다.
  • 마치 던지기 가능한 가치 인 것처럼 널 던지는 것.

응용 프로그램은이 클래스의 사례를 던져서 널 객체의 다른 불법 사용을 나타냅니다.

당신이 널 참조를 사용하려고 시도하는 경우도 synchronized, 그것은 또한이 예외를 던질 것입니다. JLS에 따라:

SynchronizedStatement:
    synchronized ( Expression ) Block
  • 그렇지 않으면, 표현의 값이 무인이라면 NullPointerException 던져졌다.

어떻게 수정합니까?

그래서 당신은 a NullPointerException. 어떻게 고치나요? a를 던지는 간단한 예를 들어합시다 NullPointerException:

public class Printer {
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    public void print() {
        printString(name);
    }

    private void printString(String s) {
        System.out.println(s + " (" + s.length() + ")");
    }

    public static void main(String[] args) {
        Printer printer = new Printer();
        printer.print();
    }
}

널 값을 식별하십시오

첫 번째 단계는 정확히 식별하는 것입니다 예외를 일으키는 값. 이를 위해서는 디버깅을해야합니다. 읽는 법을 배우는 것이 중요합니다 스택 트레이스. 이것은 예외가 발생한 위치를 보여줄 것입니다.

Exception in thread "main" java.lang.NullPointerException
    at Printer.printString(Printer.java:13)
    at Printer.print(Printer.java:9)
    at Printer.main(Printer.java:19)

여기서 우리는 예외가 13 행에 던져진다는 것을 알 수 있습니다 ( printString 방법). 줄을보고 추가하여 어떤 값이 무효인지 확인하십시오. 로깅 진술 또는 a 디버거. 우리는 그것을 알게됩니다 s 무효이며 length 그것에 대한 메소드는 예외를 던졌습니다. 우리는 프로그램이 예외를 던지는 것을 멈추는 것을 알 수 있습니다. s.length() 방법에서 제거됩니다.

이 값이 어디에서 왔는지 추적하십시오

다음 으로이 값이 어디에서 왔는지 확인하십시오. 이 방법의 발신자를 따르면 우리는 s 함께 통과됩니다 printString(name) 에서 print() 방법 및 this.name NULL입니다.

이 값을 설정 해야하는 추적

어디에 this.name 세트? 에서 setName(String) 방법. 더 많은 디버깅을 통해이 방법이 전혀 호출되지 않는다는 것을 알 수 있습니다. 방법이 호출되면 확인하십시오. 주문하다 이러한 방법이 호출되고 세트 방법이 호출되지 않습니다. ~ 후에 인쇄 방법.

이것은 우리에게 해결책을 제공하기에 충분합니다 : 전화 추가 printer.setName() 전화하기 전에 printer.print().

다른 수정

변수에는 a 기본값 (그리고 setName 널로 설정되는 것을 방지 할 수 있습니다) :

private String name = "";

어느 쪽이든 print 또는 printString 방법 null을 확인하십시오, 예를 들어:

printString((name == null) ? "" : name);

또는 수업을 설계 할 수 있습니다 name 항상 널이 아닌 값이 있습니다:

public class Printer {
    private final String name;

    public Printer(String name) {
        this.name = Objects.requireNonNull(name);
    }

    public void print() {
        printString(name);
    }

    private void printString(String s) {
        System.out.println(s + " (" + s.length() + ")");
    }

    public static void main(String[] args) {
        Printer printer = new Printer("123");
        printer.print();
    }
}

또한보십시오:

나는 아직도 문제를 찾을 수 없다

문제를 디버깅하려고했지만 여전히 해결책이없는 경우 더 많은 도움을 받기 위해 질문을 게시 할 수 있지만 지금까지 시도한 내용을 포함시켜야합니다. 최소한, 스택 트레이스를 포함하십시오 질문에서 중요한 줄 번호를 표시하십시오 코드에서. 또한 먼저 코드를 단순화하십시오 (참조 SSCCE).

질문 : 원인의 원인 NullPointerException (NPE)?

아시다시피, 자바 유형은 원시 유형 (boolean, int, 등) 및 참조 유형. Java의 참조 유형은 특별 값을 사용할 수 있습니다. null "대상 없음"을 말하는 자바 방법입니다.

NullPointerException 프로그램이 사용하려고 할 때마다 런타임에 던져집니다. null 마치 실제 참조 인 것처럼. 예를 들어, 이것을 작성하는 경우 :

public class Test {
    public static void main(String[] args) {
        String foo = null;
        int length = foo.length();   // HERE
    }
}

"Here"로 표시된 진술은 length() a null 참조, 이것은 a NullPointerException.

사용할 수있는 방법에는 여러 가지가 있습니다 null a NullPointerException. 사실, 당신이 유일한 것들 ~할 수 있다 a null NPE를 유발하지 않고 :

  • 참조 변수에 할당하거나 참조 변수에서 읽으십시오.
  • 배열 요소에 할당하거나 배열 요소에서 읽으십시오 (배열 참조 자체가 NULL이 아닌 경우),
  • 매개 변수로 전달하거나 결과적으로 반환하거나
  • 그것을 사용하여 테스트하십시오 == 또는 != 운영자, 또는 instanceof.

질문 : NPE 스택 트레이스를 어떻게 읽습니까?

위의 프로그램을 컴파일하고 실행한다고 가정 해보십시오.

$ javac Test.java 
$ java Test
Exception in thread "main" java.lang.NullPointerException
    at Test.main(Test.java:4)
$

첫 번째 관찰 : 컴파일이 성공합니다! 프로그램의 문제는 컴파일 오류가 아닙니다. 이것은 실행 시간 오류. (일부 IDES는 귀하의 프로그램에 항상 예외를 던질 것이라고 경고 할 수 있습니다 ... 그러나 표준 javac 컴파일러는 그렇지 않습니다.)

두 번째 관찰 : 프로그램을 실행할 때 두 줄의 "Gobbledy-Gook"을 출력합니다. 잘못된!! 그것은 gobbleded-gook가 아닙니다. 그것은 스택 트레이스입니다 ... 그리고 그것은 제공합니다 중요한 정보 시간이 걸리면 신중하게 읽는 경우 코드의 오류를 추적하는 데 도움이됩니다.

그래서 그것이 무엇을 말하는지 보자.

Exception in thread "main" java.lang.NullPointerException

스택 추적의 첫 번째 줄은 여러 가지를 알려줍니다.

  • 예외가 발생한 Java 스레드의 이름을 알려줍니다. 하나의 스레드가있는 간단한 프로그램의 경우 (이와 같이) "메인"이됩니다. 계속하자 ...
  • 그것은 당신에게 던진 예외의 전체 이름을 알려줍니다. 즉 java.lang.NullPointerException.
  • 예외에 관련 오류 메시지가있는 경우 예외 이름 후에 출력됩니다. NullPointerException 오류 메시지가 거의 없기 때문에 이러한 점에서 드문 일입니다.

두 번째 줄은 NPE를 진단하는 데 가장 중요한 줄입니다.

at Test.main(Test.java:4)

이것은 우리에게 여러 가지를 알려줍니다.

  • "테스트에서. 메인"은 우리가 main 방법의 방법 Test 수업.
  • "test.java:4"는 클래스의 소스 파일 이름을 제공하며, 이런 일이 발생한 진술이 파일의 4 행에 있음을 알려줍니다.

위의 파일의 줄을 계산하면 4 행은 "여기"주석으로 레이블을 지정 한 것입니다.

더 복잡한 예에서는 NPE 스택 추적에 많은 라인이있을 것입니다. 그러나 두 번째 줄 (첫 번째 "라인")이 NPE가 어디로 던져 졌는지 알려줄 수 있습니다.1.

간단히 말해 스택 추적은 프로그램의 진술이 NPE를 던져 넣은 것을 명백하게 알려줍니다.

1- 사실이 아닙니다. 중첩 예외라고 불리는 것들이 있습니다 ...

질문 : 내 코드에서 NPE 예외의 원인을 어떻게 추적합니까?

이것은 어려운 부분입니다. 짧은 대답은 스택 추적, 소스 코드 및 관련 API 문서가 제공 한 증거에 논리적 추론을 적용하는 것입니다.

먼저 간단한 예제 (위)를 설명해 봅시다. 우리는 스택 트레이스가 우리에게 NPE가 일어난 곳이라고 말한 줄을 살펴 보는 것으로 시작합니다.

int length = foo.length(); // HERE

어떻게 NPE를 던질 수 있습니까?

실제로 한 가지 방법 만 있습니다. foo 가치가 있습니다 null. 우리는 다음을 실행하려고합니다 length() 메소드 켜짐 null 그리고 .... 뱅!

하지만 (나는 당신이 말하는 것을 들었습니다) NPE가 length() 방법 호출?

글쎄, 그렇게되면 스택 추적이 다르게 보일 것입니다. 첫 번째 "At"라인은 예외가 java.lang.String 클래스 및 4 행 Test.java 두 번째 "AT"라인이 될 것입니다.

그래서 어디서 null 나왔어? 이 경우 분명하고, 우리가 그것을 고치기 위해 무엇을 해야하는지 분명합니다. (널이 아닌 값을 할당하십시오 foo.)

좋아, 그래서 약간 더 까다로운 예를 시도해 봅시다. 이것은 약간 필요합니다 논리적 공제.

public class Test {

    private static String[] foo = new String[2];

    private static int test(String[] bar, int pos) {
        return bar[pos].length();
    }

    public static void main(String[] args) {
        int length = test(foo, 1);
    }
}

$ javac Test.java 
$ java Test
Exception in thread "main" java.lang.NullPointerException
    at Test.test(Test.java:6)
    at Test.main(Test.java:10)
$ 

이제 우리는 두 개의 "에"라인이 있습니다. 첫 번째는이 줄입니다.

return args[pos].length();

그리고 두 번째는이 줄입니다.

int length = test(foo, 1);

첫 번째 줄을 보면 어떻게 NPE를 던질 수 있습니까? 두 가지 방법이 있습니다.

  • 값의 경우 bar ~이다 null 그 다음에 bar[pos] NPE를 던질 것입니다.
  • 값의 경우 bar[pos] ~이다 null 그런 다음 전화합니다 length() 그것에 NPE를 던질 것입니다.

다음으로, 우리는 어떤 시나리오 중 어떤 시나리오가 실제로 일어나고 있는지 설명해야합니다. 우리는 첫 번째를 탐색하는 것으로 시작합니다.

어디에 bar 나왔어? 그것은 매개 변수입니다 test 방법 호출, 그리고 우리가 방법을 보면 test 우리는 그것이 foo 정적 변수. 또한, 우리는 초기화를 분명히 알 수 있습니다. foo 널이 아닌 값으로. 이 설명을 잠정적으로 기각하기에 충분합니다. (이론적으로는 다른 것이 할 수 있습니다 변화 foo 에게 null ...하지만 여기서는 일어나지 않습니다.)

그렇다면 두 번째 시나리오는 어떻습니까? 글쎄, 우리는 그것을 볼 수 있습니다 pos ~이다 1, 그것은 의미가 있습니다 foo[1] 해야합니다 null. 그게 가능합니까?

실제로! 그리고 그것이 문제입니다. 다음과 같이 초기화 할 때 :

private static String[] foo = new String[2];

우리는 할당 a String[] 두 가지 요소로 초기화됩니다 null. 그 후, 우리는의 내용을 변경하지 않았습니다. foo ... 그래서 foo[1] 여전히 될 것입니다 null.

그것은 당신이 null. 아래 예를 고려하십시오 :

TypeA objA;

이때 당신은 그냥 가지고 있습니다 선언 이 대상이지만 그렇지 않습니다 초기화 또는 인스턴스화. 그리고 당신이 그 속성이나 방법에 액세스하려고 할 때마다 던질 것입니다. NullPointerException 의미가 있습니다.

아래 예제도 참조하십시오.

String a = null;
System.out.println(a.toString()); // NullPointerException will be thrown

객체가 필요한 경우 응용 프로그램이 NULL을 사용하려고 시도 할 때 NULL 포인터 예외가 발생합니다. 여기에는 다음이 포함됩니다.

  1. 인스턴스 방법을 호출합니다 null 물체.
  2. a의 필드에 액세스하거나 수정합니다 null 물체.
  3. 길이를 가져옵니다 null 마치 배열 인 것처럼.
  4. 슬롯에 액세스하거나 수정합니다 null 마치 배열 인 것처럼.
  5. 던지기 null 마치 던질 수있는 가치 인 것처럼.

응용 프로그램은이 클래스의 사례를 던져야합니다. null 물체.

참조: http://docs.oracle.com/javase/8/docs/api/java/lang/nullpointerexception.html

null 포인터는 아무데도 가리키는 것입니다. 포인터를 불신 할 때 p, 당신은 ""p "에 저장된 위치에 데이터를 줘. p a null 포인터, 위치가 저장되었습니다 p ~이다 nowhere, 당신은 " '어디에서나'위치에 데이터를 줘"라고 말하고 있습니다. 분명히, 그것은 이것을 할 수 없으므로, 그것은 null pointer exception.

일반적으로 무언가가 제대로 초기화되지 않았기 때문입니다.

어떻게 발생하는지, 어떻게 해결하는지 설명하기 위해 많은 설명이 있습니다. 그러나 당신은 또한 따라야합니다. 모범 사례 피하기 위해 NullPointerException 조금도.

또한보십시오:모범 사례의 좋은 목록

나는 매우 중요하고, final 수정 자.Java에 적용 할 때마다 "최종"수정 자 사용

요약:

  1. 사용 final 좋은 초기화를 시행하기위한 수정 자.
  2. 예를 들어 해당되는 경우 빈 컬렉션을 반환하는 등 메서드에서 Null을 반환하지 마십시오.
  3. 주석을 사용하십시오 @NotNull 그리고 @Nullable
  4. 빠르게 실패하고 Assers는 무효가되어서는 안되는 경우 전체 응용 프로그램을 통해 NULL 물체의 전파를 피하기 위해 Assers를 사용합니다.
  5. 먼저 알려진 객체와 동일하게 사용하십시오. if("knownObject".equals(unknownObject)
  6. 선호하다 valueOf() TOSTRING () 이상.
  7. Null Safe를 사용하십시오 StringUtils 행동 양식 StringUtils.isEmpty(null).

널 포인터 예외는 객체를 초기화하지 않고 사용하고 있다는 표시기입니다.

예를 들어, 아래는 코드에서 사용하는 학생 수업입니다.

public class Student {

    private int id;

    public int getId() {
        return this.id;
    }

    public setId(int newId) {
        this.id = newId;
    }
}

아래 코드는 널 포인터 예외를 제공합니다.

public class School {

    Student student;

    public School() {
        try {
            student.getId();
        }
        catch(Exception e) {
            System.out.println("Null pointer exception");
        }
    }
}

당신이 사용하고 있기 때문에 student, 그러나 아래에 표시된 올바른 코드에서와 같이 초기화하는 것을 잊었습니다.

public class School {

    Student student;

    public School() {
        try {
            student = new Student();
            student.setId(12);
            student.getId();
        }
        catch(Exception e) {
            System.out.println("Null pointer exception");
        }
    }
}

Java에서는 모든 (원시 유형 제외)는 클래스 형태입니다.

객체를 사용하려면 두 단계가 있습니다.

  1. 선언하다
  2. 초기화

예시:

  • 선언: Object object;
  • 초기화 : object = new Object();

배열 개념에 대해서도 동일합니다.

  • 선언: Item item[] = new Item[5];
  • 초기화 : item[0] = new Item();

초기화 섹션을 제공하지 않으면 NullPointerException 생기다.

~ 안에 자바 당신이 선언하는 모든 변수는 실제로 객체 자체가 아니라 객체 (또는 프리미티브)에 대한 "참조"입니다.

하나의 객체 메소드를 실행하려고 할 때, 참조는 Living Object에 해당 메소드를 실행하도록 요청합니다. 그러나 참조가 참조 NULL (아무것도, 0, void, nada)를 참조하는 경우 방법이 실행되는 방법이 없습니다. 그런 다음 런타임은 NullPointerException을 던져서 이것을 알려줍니다.

당신의 참조는 "null -> pointer"로 "포인팅"입니다.

객체는 VM 메모리 공간에 거주하며 액세스하는 유일한 방법은 사용하는 것입니다. this 참조. 이 예를 들어보세요 :

public class Some {
    private int id;
    public int getId(){
        return this.id;
    }
    public setId( int newId ) {
        this.id = newId;
    }
}

그리고 코드의 다른 장소에서 :

Some reference = new Some();    // Point to a new object of type Some()
Some otherReference = null;     // Initiallly this points to NULL

reference.setId( 1 );           // Execute setId method, now private var id is 1

System.out.println( reference.getId() ); // Prints 1 to the console

otherReference = reference      // Now they both point to the only object.

reference = null;               // "reference" now point to null.

// But "otherReference" still point to the "real" object so this print 1 too...
System.out.println( otherReference.getId() );

// Guess what will happen
System.out.println( reference.getId() ); // :S Throws NullPointerException because "reference" is pointing to NULL remember...

이것은 알아야 할 중요한 것 - 객체에 대한 더 이상 언급이 없을 때 (위의 예에서 reference 그리고 otherReference 둘 다 Null을 가리 킵니다. 우리가 작업 할 수있는 방법이 없으므로이 개체는 쓰레기를 수집 할 준비가되어 있으며 어느 시점에서 VM 은이 객체에서 사용되는 메모리를 제거하고 다른 것을 할당합니다.

A의 또 다른 사건 NullPointerException 객체 배열을 선언 할 때 발생 한 다음 즉시 그 내부의 반대 요소를 시도합니다.

String[] phrases = new String[10];
String keyPhrase = "Bird";
for(String phrase : phrases) {
    System.out.println(phrase.equals(keyPhrase));
}

비교 순서가 반전되면이 특정 NPE를 피할 수 있습니다. 즉, 사용 .equals 보장되지 않은 널리 객체.

배열 내부의 모든 요소 공통 초기 값으로 초기화됩니다; 모든 유형의 객체 배열의 경우 모든 요소가 null.

~ 해야 하다 배열에서 요소를 초기화하십시오 ~ 전에 그들을 접근하거나 해석하는 것.

String[] phrases = new String[] {"The bird", "A bird", "My bird", "Bird"};
String keyPhrase = "Bird";
for(String phrase : phrases) {
    System.out.println(phrase.equals(keyPhrase));
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top