Pergunta

O que são exceções de ponteiro nulo (java.lang.NullPointerException) eo que faz com que eles?

Que métodos / ferramentas podem ser usadas para determinar a causa para que você pare a exceção de causar o programa para terminar prematuramente?

Foi útil?

Solução

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

int x;
x = 10;

Neste exemplo, o x variável é um int e Java irá inicializar-lo para 0 para você. Quando você atribui a ela o valor de 10 na segunda linha, o seu valor de 10 é escrito no local de memória referido por x.

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

Integer num;
num = new Integer(10);

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

Na segunda linha, a chave new é usada para instanciar (ou criar) um objecto do tipo Integer e a variável num ponteiro é atribuído a esse objecto Integer.

O NullPointerException ocorre quando você declarar uma variável, mas não criar um objeto. Então você está apontando para algo que na verdade não existe.

Se você tentar num dereference antes de criar o objeto que você obter um NullPointerException. Na maioria dos casos triviais, o compilador irá pegar o problema e que você saiba que "num may not have been initialized", mas às vezes você pode escrever código que não cria diretamente o objeto.

Por exemplo, você pode ter um método da seguinte forma:

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

Nesse caso, você não está criando o objeto obj, mas assumindo que ele foi criado antes que o método doSomething() foi chamado. Note, é possível chamar o método como este:

doSomething(null);

Nesse caso, obj é null. Se o método tem a intenção de fazer algo para o passado-in objeto, ele é apropriado para lançar a NullPointerException porque é um erro de programação eo programador terá essa informação para fins de depuração.

Em alternativa, pode haver casos em que o objectivo do método não é exclusivamente para operar no passado no objecto, e, portanto, um parâmetro nulo pode ser aceitável. Neste caso, você precisa verificar se há um nulo parâmetro e se comportam de forma diferente. Você também deve explicar isso na documentação. Por exemplo, doSomething() poderia ser escrita 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 causa de exceção e usando Stack Trace

Outras dicas

NullPointerExceptions são exceções que ocorrem quando você tentar usar uma referência que aponta para qualquer local na memória (null) como se estivesse fazendo referência a um objeto. Chamar um método em uma referência nula ou tentar aceder a um campo de uma referência nula irá desencadear uma NullPointerException. Estes são os mais comuns, mas outras maneiras estão listados no NullPointerException página javadoc.

Provavelmente o exemplo de código mais rápido que eu poderia vir para cima com para ilustrar um NullPointerException seria:

public class Example {

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

}

No primeiro main linha de dentro, eu estou definindo explicitamente a Object referência obj igual a null. Isso significa que eu tenha uma referência, mas não está a apontar para qualquer objeto. Depois disso, eu tento tratar a referência como se ele aponta para um objeto chamando um método nele. Isso resulta em uma NullPointerException porque não há código para executar no local que a referência está apontando.

(Esta é uma questão técnica, mas acho que vale a pena mencionar: uma referência que aponta para nulo não é o mesmo como um ponteiro C que aponta para um local de memória inválido um ponteiro nulo literalmente não está apontando em qualquer lugar. , que é sutilmente diferente do que apontar para um local que passa a ser inválido.)

O que é um NullPointerException?

Um bom lugar para começar é o JavaDocs . Eles têm esta coberta:

Lançada quando uma aplicação tenta usar null em um caso em que um objecto é requerido. Estes incluem:

  • Chamar o método instância de um objeto nulo.
  • aceder ou a modificação do campo de um objecto nulo.
  • Tomando o comprimento de null, como se fosse uma matriz.
  • Acesso ou modificando as ranhuras de nulo, como se fosse uma matriz.
  • Jogando nulo como se fosse um valor Throwable.

As candidaturas devem jogar instâncias desta classe para indicar outra usos ilegais do objeto nulo.

É também o caso de que se você tentar usar uma referência nula com synchronized, que também irá lançar essa exceção, per os JLS :

SynchronizedStatement:
    synchronized ( Expression ) Block
  • Caso contrário, se o valor da expressão é nulo, uma NullPointerException é lançada.

Como faço para corrigir isso?

Então você tem um NullPointerException. Como você conserta isso? Vamos tomar 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();
    }
}

Identificar os valores nulos

O primeiro passo é identificar exatamente o cujos valores estão causando a exceção . Para isso, precisamos fazer alguma depuração. É importante aprender a ler um stacktrace . Isto irá mostrar-lhe 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 método printString). Olhe para a linha e verificar quais os valores são nulos por acrescentando login declarações ou usando um depurador . Nós descobrimos que s é nulo, e chamar o método length sobre ele lança a exceção. Podemos ver que o programa pára de lançar a exceção quando s.length() é removido do método.

Traço em que esses valores vêm de

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

Traço em que esses valores devem ser definidos

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

Este é o suficiente para nos dar uma solução:. Adicione uma chamada para printer.setName() antes de chamar printer.print()

Outras correções

A variável pode ter um valor padrão (e setName pode impedi-lo de ser conjunto para null):

private String name = "";

De qualquer método print ou printString podem verificar se há nula , por exemplo:

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

Ou você pode criar a classe para que name sempre tem 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:

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 você já tentou até agora. No mínimo, incluir o stacktrace na pergunta, e marcam os números de linha importantes no código. Além disso, tente simplificar o código primeiro (ver SSCCE ).

Pergunta: O que faz com que um NullPointerException (NPE)

?

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

A NullPointerException é jogado em tempo de execução sempre que suas tentativas de programa para usar um null como se fosse uma referência real. Por exemplo, se você escrever o seguinte:

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

a instrução rotulada "aqui" vai tentar executar o método length() em uma referência null, e isso vai jogar um NullPointerException.

Há muitas maneiras que você pode usar um valor null que irá resultar em uma NullPointerException. Na verdade, as únicas coisas que você pode fazer com um null sem causar um NPE são:

  • atribuí-la a uma variável de referência ou lê-lo a partir de uma variável de referência,
  • atribuí-la a um elemento de matriz ou lê-lo a partir de um elemento de matriz (desde que referência de matriz em si não é nulo!),
  • passá-lo como um parâmetro ou devolvê-lo, como resultado, ou
  • testá-lo usando os operadores == ou !=, ou instanceof.

Pergunta: Como faço para ler o stacktrace NPE

Suponha que eu compilar e executar 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 for bem-sucedido! O problema no programa não é um erro de compilação. É um tempo de execução erro. (Alguns IDEs pode advertir o seu programa de sempre lançar uma exceção ... mas o compilador javac padrão não.)

Segundo observação: quando eu executar o programa, ele exibe duas linhas de "gobbledy-gook". ERRADA !! Isso não é gobbledy-gook. É um stacktrace ... e fornece informações vitais que irá ajudá-lo a rastrear o erro no seu código, se você tomar o tempo para lê-lo com cuidado.

Então, vamos olhar para o que ele diz:

Exception in thread "main" java.lang.NullPointerException

A primeira linha do rastreamento de pilha diz-lhe uma série de coisas:

  • Diz-lhe o nome do thread Java no qual a exceção foi lançada. Para um programa simples com uma thread (como este), será "main". Vamos seguir em frente ...
  • Diz-lhe o nome completo da exceção que foi lançada; ou seja java.lang.NullPointerException.
  • Se a exceção tem uma mensagem de erro associado, que será emitido após o nome da exceção. NullPointerException é incomum a este respeito, porque ele raramente tem uma mensagem de erro.

A segunda linha é o mais importante no diagnóstico de uma NPE.

at Test.main(Test.java:4)

Isto diz-nos uma série de coisas:

  • "no Test.main" diz que estávamos no método main da classe Test.
  • "Test.java:4" dá o nome do arquivo fonte da classe, e nos diz que a declaração em que isso ocorreu é na linha 4 do arquivo.

Se você contar as linhas no arquivo acima, a linha 4 é a que eu rotulado com o "aqui" comentário.

Note que em um exemplo mais complicado, haverá lotes de linhas no rastreamento de pilha NPE. Mas você pode ter certeza que a segunda linha (a primeira "at" line) irá dizer-lhe onde o NPE foi jogado 1 .

Em suma, o rastreamento de pilha vai nos dizer de forma inequívoca que declaração do programa tem jogado o NPE.

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

Pergunta: Como faço para rastrear a causa da exceção NPE no meu código

Esta é a parte mais difícil. A resposta curta é aplicar inferência lógica para as provas fornecidas pelo rastreamento de pilha, o código fonte e documentação da API relevante.

Vamos ilustrar com o exemplo simples (acima) em primeiro lugar. Nós começar por olhar para o line que o rastreamento de pilha nos disse é onde o NPE aconteceu:

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

Como isso pode lançar uma NPE?

Na verdade só há um caminho: ela só pode acontecer se foo tem o valor null. Em seguida, tentar executar o método length() em null e .... BANG!

Mas (eu ouço você dizer) e se o NPE foi jogado dentro da chamada de método length()?

Bem, se isso acontecesse, o rastreamento de pilha seria diferente. O primeiro "a" linha diria que a exceção foi lançada em alguma linha na classe java.lang.String e linha 4 do Test.java seria o segundo "na" linha.

Então, onde é que isso null vem? Neste caso, é óbvio, e é óbvio que precisamos fazer para corrigi-lo. (Atribuir um valor não nulo para foo.)

OK, então vamos tentar um exemplo um pouco mais complicado. Isso vai 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)
$ 

Então, agora temos dois "at" linhas. O primeiro é para esta linha:

return args[pos].length();

eo segundo é para esta linha:

int length = test(foo, 1);

Olhando para a primeira linha, como isso poderia lançar uma NPE? Há duas maneiras:

  • Se o valor da bar é null então bar[pos] irá lançar uma NPE.
  • Se o valor da bar[pos] é null seguida, chamar length() sobre ele irá lançar uma NPE.

Em seguida, precisamos descobrir qual desses cenários explica o que está realmente acontecendo. Vamos começar por explorar o primeiro:

Onde é que bar vem? É um parâmetro para a chamada de método test, e se nós olhamos como test foi chamado, podemos ver que se trata da variável foo estática. Além disso, podemos ver claramente que nós inicializado foo para um valor não nulo. Isso é suficiente para descartar provisoriamente esta explicação. (Em teoria, outra coisa poderia mudança foo para null ... mas isso não é acontecendo aqui.)

Assim que sobre o nosso segundo cenário? Bem, podemos ver que pos é 1, o que significa que foo[1] deve ser null. Isso é possível?

Na verdade, é! E esse é o problema. Quando inicializar assim:

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

alocamos um String[] com dois elementos que são inicializados para null . Depois disso, nós não mudamos o conteúdo da foo ... então foo[1] ainda será null.

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

TypeA objA;

Neste momento você tem apenas declarou este objeto, mas não inicializado ou instanciado . E sempre que você tentar acessar qualquer propriedade ou método nele, ele vai jogar NullPointerException que faz sentido.

Veja este exemplo abaixo, bem como:

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

Uma excepção ponteiro nulo quando é lançada uma aplicação tenta utilização nula em um caso onde um objecto é requerido. Estes incluem:

  1. Chamar o método instância de um objeto null.
  2. aceder ou a modificação do campo de um objecto null.
  3. Tomando o comprimento de null como se fosse uma matriz.
  4. Acesso ou modificando as ranhuras de null como se fosse uma matriz.
  5. Jogando null como se fosse um valor Throwable.

As candidaturas devem jogar instâncias desta classe para indicar outros usos ilegais do objeto null.

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

Um ponteiro null é aquele que aponta para lugar nenhum. Quando você excluir a referência um p ponteiro, você diz "dá-me os dados no local armazenado em 'p'. Quando p é um ponteiro null, a localização armazenados em p é nowhere, você está dizendo" me dá os dados no local 'nenhum lugar'". 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.

Um monte de explicações já estão presentes para explicar como isso acontece e como corrigi-lo, mas você também deve seguir melhores práticas para evitar NullPointerException em tudo.

Veja também: Uma boa lista das melhores práticas

Gostaria de acrescentar, muito importante, fazer um bom uso do modificador final. Usando o modificador "final", sempre que aplicável em Java

Resumo:

  1. Use o modificador final que sejam utilizadas boas inicialização.
  2. Evite retornando null em métodos, por exemplo retornando coleções vazias quando aplicável.
  3. Use anotações @NotNull e @Nullable
  4. Falha rápido e utilização afirma para evitar a propagação de objetos nulos através de toda a aplicação quando não deve ser nulo.
  5. Use iguala com um objeto conhecido primeiro: if("knownObject".equals(unknownObject)
  6. Prefere valueOf() sobre toString ().
  7. Use nula segura métodos StringUtils StringUtils.isEmpty(null).

A exceção de ponteiro nulo é um indicador de que você está usando um objeto sem inicializa-la.

Por exemplo, a seguir é uma classe estudante que vai usá-lo 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 abaixo dá 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");
        }
    }
}

Como você está usando student, mas você 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");
        }
    }
}

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

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

  1. Declare
  2. A inicialização

Exemplo:

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

Mesmo para o conceito de matriz:

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

Se você não está dando a seção de inicialização, em seguida, surgem a NullPointerException.

Na Java todas as variáveis ??que você declare são realmente "referências" para o objetos (ou primitivos) e não os próprios objetos.

Quando você tenta executar um método de objeto, a referência pede o objeto vivo para executar esse método. Mas se a referência é referência nula (Nada, zero, vazio, nada), então não há nenhuma maneira o método é executado. Em seguida, o tempo de execução que você saiba esta jogando um NullPointerException.

A sua referência é "apontando" para nulo, portanto, "Null -> Pointer".

As vidas de objetos no espaço de memória VM ea única maneira de acessá-lo é usando referências this. 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 no 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...

Isto uma coisa importante saber - quando não há mais referências a um objeto (no exemplo acima, quando reference e otherReference ambos apontam para null), em seguida, o objeto é "inacessível". Não há nenhuma maneira podemos trabalhar com ele, então este objeto está pronto para ser lixo coletado, e em algum momento, a VM irá liberar a memória usada por este objeto e irá alocar outro.

Outra ocorrência de um NullPointerException ocorre quando um objecto declara uma matriz, em seguida, tenta imediatamente a elementos desreferenciar no interior do mesmo.

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

Esta NPE particular pode ser evitado se a ordem de comparação é invertida; ou seja, o uso .equals em um objeto não nulo garantida.

Todos os elementos no interior de uma matriz são inicializados para o seu valor inicial comum ; para qualquer tipo de matriz de objeto, o que significa que todos os elementos são null.

Você deve inicializar os elementos na matriz antes acesso ou dereferencing-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));
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top