O que é um NullPointerException e como faço para corrigi-lo?

Oct 20 2008

O que são exceções de ponteiro nulo ( java.lang.NullPointerException) e o que as causa?

Quais métodos / ferramentas podem ser usados ​​para determinar a causa de forma que você impeça a exceção de fazer com que o programa seja encerrado prematuramente?

Respostas

3848 VincentRamdhanie Oct 20 2008 at 20:54

Quando você declara uma variável de referência (ou seja, um objeto), você está, na verdade, criando um ponteiro para um objeto. Considere o seguinte código onde você declara uma variável do tipo primitivo int:

int x;
x = 10;

Neste exemplo, a variável xé um inte o Java a inicializará para 0para você. Quando você atribui a ele o valor de 10na segunda linha, seu valor de 10é escrito no local da memória referido por x.

Mas, quando você tenta declarar um tipo de referência , algo diferente acontece. Pegue o seguinte código:

Integer num;
num = new Integer(10);

A primeira linha declara uma variável chamada num, mas na verdade ainda não contém um valor primitivo. Em vez disso, ele contém um ponteiro (porque o tipo Integeré um tipo de referência). Como você ainda não disse o que apontar, Java o define como null, o que significa " Não estou apontando para nada ".

Na segunda linha, a newpalavra-chave é usada para instanciar (ou criar) um objeto do tipo Integer, e a variável de ponteiro numé atribuída a esse Integerobjeto.

A NullPointerExceptionocorre quando você declarar uma variável, mas não criar um objeto e atribuí-lo à variável antes de tentar utilizar o conteúdo da variável (chamado dereferencing ). Então você está apontando para algo que realmente não existe.

A desreferenciação geralmente acontece ao usar .para acessar um método ou campo, ou usar [para indexar um array.

Se você tentar cancelar a referência numANTES de criar o objeto, obterá um NullPointerException. Nos casos mais triviais, o compilador detectará o problema e informará que " num may not have been initialized," mas às vezes você pode escrever um código que não cria diretamente o objeto.

Por exemplo, você pode ter um método como segue:

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

Nesse caso, você não está criando o objeto obj, mas presumindo que ele foi criado antes de o doSomething()método ser chamado. Observe que é possível chamar o método assim:

doSomething(null);

Nesse caso, objé null, e a instrução obj.myMethod()lançará um NullPointerException.

Se o método se destina a fazer algo para o objeto passado como o método acima faz, é apropriado lançar o NullPointerExceptionporque é um erro do programador e o programador precisará dessa informação para fins de depuração.

Além de NullPointerExceptions lançados como resultado da lógica do método, você também pode verificar os argumentos do método para nullvalores e lançar NPEs explicitamente adicionando algo como o seguinte perto do início de um método:

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

Observe que é útil dizer claramente em sua mensagem de erro qual objeto não pode ser null. A vantagem de fazer uma validação como essa é que 1) você pode retornar suas próprias mensagens de erro mais claras e 2) para o resto do método, você sabe que, a menos que objseja reatribuído, não é nulo e pode ser desreferenciado com segurança.

Alternativamente, pode haver casos em que o propósito do método não é apenas operar no objeto passado e, portanto, um parâmetro nulo pode ser aceitável. Nesse caso, você precisaria verificar se há um parâmetro nulo e se comportar de maneira diferente. Você também deve explicar isso na documentação. Por exemplo, doSomething()pode ser escrito como:

/**
  * @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
    }
}

Finalmente, como identificar a exceção e a causa usando Stack Trace

Quais métodos / ferramentas podem ser usados ​​para determinar a causa de forma que você impeça a exceção de fazer com que o programa seja encerrado prematuramente?

O sonar com encontrar bugs pode detectar o NPE. O sonar pode capturar exceções de ponteiro nulo causadas por JVM dinamicamente

Agora o Java 14 adicionou um novo recurso de linguagem para mostrar a causa raiz de NullPointerException. Este recurso de linguagem faz parte do JVM comercial da SAP desde 2006. A seguir, 2 minutos de leitura para entender esse incrível recurso de linguagem.

https://jfeatures.com/blog/NullPointerException

Em java 14, a seguir está uma mensagem de exceção NullPointerException de amostra:

no encadeamento "main" java.lang.NullPointerException: Não é possível invocar "java.util.List.size ()" porque "list" é nula

898 BilltheLizard Oct 20 2008 at 20:20

NullPointerExceptions são exceções que ocorrem quando você tenta usar uma referência que não aponta para nenhum local na memória (nulo) como se estivesse fazendo referência a um objeto. Chamar um método em uma referência nula ou tentar acessar um campo de uma referência nula irá disparar a NullPointerException. Esses são os mais comuns, mas outras formas estão listadas na NullPointerExceptionpágina do javadoc.

Provavelmente, o código de exemplo mais rápido que eu poderia criar para ilustrar um NullPointerExceptionseria:

public class Example {

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

}

Na primeira linha interna main, estou definindo explicitamente a Objectreferência objigual a null. Isso significa que tenho uma referência, mas ela não está apontando para nenhum objeto. Depois disso, tento tratar a referência como se ela apontasse para um objeto, chamando um método nele. Isso resulta em um NullPointerExceptionporque não há código a ser executado no local para o qual a referência está apontando.

(Isso é um detalhe técnico, mas acho que vale a pena mencionar: uma referência que aponta para nulo não é o mesmo que um ponteiro C que aponta para um local de memória inválido. Um ponteiro nulo literalmente não aponta para nenhum lugar , o que é sutilmente diferente de apontando para um local que é inválido.)

709 fgb Jun 08 2014 at 02:22

O que é um NullPointerException?

Um bom lugar para começar são os JavaDocs . Eles têm isso coberto:

Lançado quando um aplicativo tenta usar null em um caso em que um objeto é necessário. Esses incluem:

  • Chamando o método de instância de um objeto nulo.
  • Acessando ou modificando o campo de um objeto nulo.
  • Obtendo o comprimento de null como se fosse uma matriz.
  • Acessando ou modificando os slots de null como se fosse um array.
  • Jogando null como se fosse um valor Throwable.

Os aplicativos devem lançar instâncias desta classe para indicar outros usos ilegais do objeto nulo.

Também é o caso em que se você tentar usar uma referência nula com synchronized, isso também lançará esta exceção, de acordo com o JLS :

SynchronizedStatement:
    synchronized ( Expression ) Block
  • Caso contrário, se o valor da Expressão for nulo, a NullPointerExceptionserá lançado.

Como faço para corrigir isso?

Então você tem um NullPointerException. Como você corrige isso? Vamos dar um exemplo simples que lança um 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();
    }
}

Identifique os valores nulos

A primeira etapa é identificar exatamente quais valores estão causando a exceção . Para isso, precisamos fazer alguma depuração. É importante aprender a ler um rastreamento de pilha . Isso mostrará onde a exceção foi lançada:

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)

Aqui, vemos que a exceção é lançada na linha 13 (no printStringmétodo). Observe a linha e verifique quais valores são nulos adicionando instruções de registro ou usando um depurador . Descobrimos que sé nulo e chamar o lengthmétodo nele lança a exceção. Podemos ver que o programa para de lançar a exceção quando s.length()é removido do método.

Rastreie de onde vêm esses valores

Em seguida, verifique de onde vem esse valor. Seguindo os chamadores do método, vemos que sé passado com printString(name)no print()método e this.nameé nulo.

Rastreie onde esses valores devem ser definidos

Onde está this.namedefinido? No setName(String)método. Com um pouco mais de depuração, podemos ver que esse método não é chamado de forma alguma. Se o método foi chamado, certifique-se de verificar a ordem em que esses métodos são chamados e o método set não é chamado após o método de impressão.

Isso é o suficiente para nos dar uma solução: adicione uma chamada para printer.setName()antes de ligar printer.print().

Outras correções

A variável pode ter um valor padrão (e setNamepode evitar que seja definida como nula):

private String name = "";

O método printou printStringpode verificar se há nulo , por exemplo:

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

Ou você pode projetar a classe de modo que name sempre tenha um valor não nulo :

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();
    }
}

Veja também:

  • Evitando instruções “! = Null” em Java?

Eu ainda não consigo encontrar o problema

Se você tentou depurar o problema e ainda não tem uma solução, você pode postar uma pergunta para obter mais ajuda, mas certifique-se de incluir o que tentou até agora. No mínimo, inclua o rastreamento de pilha na pergunta e marque os números de linha importantes no código. Além disso, tente simplificar o código primeiro (consulte SSCCE ).

516 StephenC Jun 22 2014 at 09:16

Pergunta: O que causa um NullPointerException(NPE)?

Como você deve saber, tipos Java são divididos em tipos primitivos ( boolean, int, etc.) e tipos de referência . Os tipos de referência em Java permitem que você use o valor especial nullque é a maneira Java de dizer "nenhum objeto".

A NullPointerExceptioné lançado em tempo de execução sempre que seu programa tenta usar um nullcomo se fosse uma referência real. Por exemplo, se você escrever isto:

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

a instrução rotulada "AQUI" tentará executar o length()método em uma nullreferência e isso gerará um NullPointerException.

Há muitas maneiras de usar um nullvalor que resultará em a NullPointerException. Na verdade, as únicas coisas que você pode fazer com um nullsem causar um NPE são:

  • atribuí-lo a uma variável de referência ou lê-lo de uma variável de referência,
  • atribua-o a um elemento de matriz ou leia-o de um elemento de matriz (desde que a própria referência de matriz não seja nula!),
  • passe-o como um parâmetro ou retorne-o como resultado, ou
  • testá-lo usando as ==ou !=operadores, ou instanceof.

Pergunta: Como leio o stacktrace do NPE?

Suponha que eu compile e execute o programa acima:

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

Primeira observação: a compilação foi bem-sucedida! O problema no programa NÃO é um erro de compilação. É um erro de tempo de execução. (Alguns IDEs podem avisar que seu programa sempre lançará uma exceção ... mas o javaccompilador padrão não.)

Segunda observação: quando executo o programa, ele produz duas linhas de "gobbledy-gook". ERRADO!! Isso não é gobbledy-gook. É um rastreamento de pilha ... e fornece informações vitais que o ajudarão a rastrear o erro em seu código, se você reservar um tempo para lê-lo com atenção.

Então, vamos dar uma olhada no que diz:

Exception in thread "main" java.lang.NullPointerException

A primeira linha do rastreamento de pilha informa uma série de coisas:

  • Ele informa o nome do encadeamento Java no qual a exceção foi lançada. Para um programa simples com um thread (como este), ele será "principal". Vamos continuar ...
  • Ele informa o nome completo da exceção que foi lançada; ie java.lang.NullPointerException.
  • Se a exceção tiver uma mensagem de erro associada, ela será emitida após o nome da exceção. NullPointerExceptioné incomum a esse respeito, porque raramente contém uma mensagem de erro.

A segunda linha é a mais importante no diagnóstico de um NPE.

at Test.main(Test.java:4)

Isso nos diz uma série de coisas:

  • "at Test.main" diz que estávamos no mainmétodo da Testaula.
  • "Test.java:4" fornece o nome do arquivo fonte da classe E nos diz que a instrução onde isso ocorreu está na linha 4 do arquivo.

Se você contar as linhas do arquivo acima, a linha 4 é aquela que rotulei com o comentário "AQUI".

Observe que, em um exemplo mais complicado, haverá muitas linhas no rastreamento de pilha do NPE. Mas você pode ter certeza de que a segunda linha (a primeira linha "arroba") dirá onde o NPE foi lançado 1 .

Em suma, o rastreamento de pilha nos dirá sem ambigüidade qual instrução do programa lançou o NPE.

1 - Não é bem verdade. Existem coisas chamadas exceções aninhadas ...

Pergunta: Como rastrear a causa da exceção NPE em meu código?

Esta é a parte difícil. A resposta curta é aplicar inferência lógica à evidência fornecida pelo rastreamento de pilha, o código-fonte e a documentação API relevante.

Vamos ilustrar com o exemplo simples (acima) primeiro. Começamos olhando para a linha que o rastreamento de pilha nos disse é onde o NPE aconteceu:

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

Como isso pode lançar um NPE?

Na verdade, só existe uma maneira: só pode acontecer se footiver o valor null. Em seguida, tentamos executar o length()método nulle ... BANG!

Mas (eu ouço você dizer) e se o NPE fosse lançado dentro da length()chamada do método?

Bem, se isso acontecesse, o rastreamento de pilha pareceria diferente. A primeira linha "arroba" diria que a exceção foi lançada em alguma linha na java.lang.Stringclasse e a linha 4 de Test.javaseria a segunda linha "arroba".

Então de onde veio isso null? Nesse caso, é óbvio e é óbvio o que precisamos fazer para consertar. (Atribuir um valor não nulo a foo.)

OK, então vamos tentar um exemplo um pouco mais complicado. Isso exigirá alguma dedução lógica .

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) $ 

Portanto, agora temos duas linhas "arroba". O primeiro é para esta linha:

return args[pos].length();

e o segundo é para esta linha:

int length = test(foo, 1);

Olhando para a primeira linha, como isso poderia lançar um NPE? Existem duas maneiras:

  • Se o valor de barfor, nullentão bar[pos]lançará um NPE.
  • Se o valor de bar[pos]for, nullentão, chamá length()-lo lançará um NPE.

Em seguida, precisamos descobrir qual desses cenários explica o que realmente está acontecendo. Começaremos explorando o primeiro:

De onde barvem? É um parâmetro para a testchamada do método, e se olharmos como testfoi chamado, podemos ver que vem da foovariável estática. Além disso, podemos ver claramente que inicializamos foocom um valor não nulo. Isso é suficiente para rejeitar provisoriamente essa explicação. (Em teoria, outra coisa poderia mudar foo para null... mas isso não está acontecendo aqui.)

E quanto ao nosso segundo cenário? Bem, podemos ver que posé 1, então isso significa que foo[1]deve ser null. Isso é possível?

De fato é! E esse é o problema. Quando inicializamos assim:

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

alocamos a String[]com dois elementos que são inicializados comnull . Depois disso, não alteramos o conteúdo de foo... então foo[1]ainda será null.

432 RakeshBurbure Oct 20 2008 at 20:21

É como se você estivesse tentando acessar um objeto que é null. Considere o exemplo abaixo:

TypeA objA;

Neste momento, você acabou de declarar este objeto, mas não inicializou ou instanciou . E sempre que você tentar acessar qualquer propriedade ou método nele, isso fará NullPointerExceptionsentido.

Veja este exemplo abaixo também:

String a = null;
System.out.println(a.toString()); // NullPointerException will be thrown
365 nathan1138 Apr 17 2013 at 09:57

Uma exceção de ponteiro nulo é lançada quando um aplicativo tenta usar nulo em um caso em que um objeto é necessário. Esses incluem:

  1. Chamando o método de instância de um nullobjeto.
  2. Acessando ou modificando o campo de um nullobjeto.
  3. Medindo o comprimento de nullcomo se fosse uma matriz.
  4. Acessando ou modificando os slots de nullcomo se fosse um array.
  5. Jogando nullcomo se fosse um valor Throwable.

Os aplicativos devem lançar instâncias desta classe para indicar outros usos ilegais do nullobjeto.

Referência: http://docs.oracle.com/javase/8/docs/api/java/lang/NullPointerException.html

341 MrZebra Oct 20 2008 at 20:25

Um nullponteiro é aquele que aponta para lugar nenhum. Ao cancelar a referência de um ponteiro p, você diz "forneça os dados no local armazenado em" p ". Quando pé um nullponteiro, o local armazenado em pé nowhere, você está dizendo" forneça os dados no local 'em lugar nenhum' ". Obviamente, ele não pode fazer isso, então ele lança um null pointer exception.

Em geral, é porque algo não foi inicializado corretamente.

329 L.G. Jun 25 2014 at 18:17

Muitas explicações já estão presentes para explicar como isso acontece e como corrigi-lo, mas você também deve seguir as práticas recomendadas para evitar problemas NullPointerException.

Veja também: Uma boa lista de práticas recomendadas

Eu acrescentaria, muito importante, fazer um bom uso do finalmodificador. Usando o modificador "final" sempre que aplicável em Java

Resumo:

  1. Use o finalmodificador para garantir uma boa inicialização.
  2. Evite retornar nulo em métodos, por exemplo, retornar coleções vazias quando aplicável.
  3. Use anotações @NotNulle@Nullable
  4. Falha rápido e usa declarações para evitar a propagação de objetos nulos por todo o aplicativo quando eles não deveriam ser nulos.
  5. Use igual a um objeto conhecido primeiro: if("knownObject".equals(unknownObject)
  6. Prefere valueOf()mais toString().
  7. Use StringUtilsmétodos seguros nulos StringUtils.isEmpty(null).
  8. Use Java 8 opcional como valor de retorno em métodos, a classe opcional fornece uma solução para representar valores opcionais em vez de referências nulas.
323 ashishbhatt Jan 28 2012 at 13:45

Em Java, tudo (excluindo os tipos primitivos) está na forma de uma classe.

Se quiser usar qualquer objeto, você tem duas fases:

  1. Declarar
  2. Inicialização

Exemplo:

  • Declaração: Object object;
  • Inicialização: object = new Object();

O mesmo para o conceito de array:

  • Declaração: Item item[] = new Item[5];
  • Inicialização: item[0] = new Item();

Se você não está fornecendo a seção de inicialização, então NullPointerExceptionsurge.

322 javidpiprani Sep 24 2013 at 13:01

Uma exceção de ponteiro nulo é um indicador de que você está usando um objeto sem inicializá-lo.

Por exemplo, abaixo está uma classe de aluno que usará em nosso código.

public class Student {

    private int id;

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

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

O código a seguir fornece uma exceção de ponteiro nulo.

public class School {

    Student student;

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

Porque você está usando student, mas se esqueceu de inicializá-lo como no código correto mostrado abaixo:

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");
        }
    }
}
315 OscarRyz Oct 21 2008 at 03:05

Em Java, todas as variáveis ​​que você declara são, na verdade, "referências" aos objetos (ou primitivas) e não aos próprios objetos.

Quando você tenta executar um método de objeto, a referência pede ao objeto vivo para executar esse método. Mas se a referência estiver referenciando NULL (nada, zero, vazio, nada), então não há como o método ser executado. Em seguida, o tempo de execução permite que você saiba disso lançando um NullPointerException.

Sua referência está "apontando" para nulo, portanto, "Nulo -> Ponteiro".

O objeto reside no espaço de memória da VM e a única forma de acessá-lo é por meio de thisreferências. Veja este exemplo:

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

E em outro lugar em seu código:

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...

Isso é importante saber - quando não há mais referências a um objeto (no exemplo acima, quando referencee otherReferenceambos apontam para nulo), então o objeto está "inacessível". Não há como trabalharmos com ele, então este objeto está pronto para ser coletado no lixo e, em algum momento, a VM irá liberar a memória usada por este objeto e alocará outro.

287 Makoto May 25 2014 at 13:11

Outra ocorrência de a NullPointerExceptionocorre quando alguém declara um array de objetos e imediatamente tenta desreferenciar os elementos dentro dele.

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

Esse NPE específico pode ser evitado se a ordem de comparação for invertida; ou seja, uso .equalsem um objeto não nulo garantido.

Todos os elementos dentro de uma matriz são inicializados com seu valor inicial comum ; para qualquer tipo de array de objetos, isso significa que todos os elementos são null.

Você deve inicializar os elementos na matriz antes de acessá-los ou desreferenciá-los.

String[] phrases = new String[] {"The bird", "A bird", "My bird", "Bird"};
String keyPhrase = "Bird";
for(String phrase : phrases) {
    System.out.println(phrase.equals(keyPhrase));
}