Respostas:
Quando você declara uma variável de referência (ou seja, um objeto), está realmente criando um ponteiro para um objeto. Considere o seguinte código em que você declara uma variável do tipo primitivo int:
int x;
x = 10;
Neste exemplo, a variável xé an inte Java a inicializará 0para você. Quando você atribui o valor de 10na segunda linha, seu valor de 10é gravado no local de 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 ainda não contém um valor primitivo. Em vez disso, ele contém um ponteiro (porque o tipo é o tipo de Integerreferência). Como você ainda não disse o que apontar, o Java 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 Integere a variável ponteiro numé atribuída a esse Integerobjeto.
Isso NullPointerExceptionocorre quando você declara uma variável, mas não cria um objeto e atribui à variável antes de tentar usar o conteúdo da variável (chamada de desreferenciação ). Então você está apontando para algo que realmente não existe.
A desreferenciação geralmente ocorre quando se usa .para acessar um método ou campo, ou se usa [para indexar uma matriz.
Se você tentar remover a referência numantes de criar o objeto, receberá um NullPointerException. Nos casos mais triviais, o compilador detectará o problema e informará " 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 da seguinte maneira:
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 da doSomething()chamada do método. Observe que é possível chamar o método assim:
doSomething(null);
Nesse caso, objé null. Se o método se destina a fazer algo com o objeto passado, é apropriado lançar o arquivo NullPointerExceptionporque é um erro do programador e o programador precisará dessas informações para fins de depuração. Inclua o nome da variável de objeto na mensagem de exceção, como
Objects.requireNonNull(a, "a");
Como alternativa, pode haver casos em que o objetivo do método não é operar apenas no objeto passado e, portanto, um parâmetro nulo pode ser aceitável. Nesse caso, você precisaria verificar um parâmetro nulo e se comportar de maneira diferente. Você também deve explicar isso na documentação. Por exemplo, doSomething()poderia 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
}
}
Por fim, como identificar a exceção e a causa usando o rastreamento de pilha
Quais métodos / ferramentas podem ser usados para determinar a causa e impedir que a exceção faça com que o programa seja encerrado prematuramente?
O sonar com findbugs pode detectar o NPE. O sonar pode capturar exceções de ponteiro nulo causadas pela JVM Dynamically
int a=bpode lançar um NPE se b for an Integer. Há casos em que isso é confuso para depuração.
NullPointerExceptionproblemas em seu código é usar @Nullablee @NotNullanotações. A resposta a seguir tem mais informações sobre isso. Embora essa resposta seja específica sobre o IntelliJ IDE, ela também é aplicável a outras ferramentas, como é o aparato dos comentários. (BTW eu não estou autorizado a editar esta resposta diretamente, talvez o autor pode adicioná-lo?)
NullPointerExceptions são exceções que ocorrem quando você tenta usar uma referência que 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 acionará a NullPointerException. Essas são as mais comuns, mas outras maneiras estão listadas na NullPointerExceptionpágina 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 dentro main, estou explicitamente definindo a Objectreferência objigual a null. Isso significa que eu tenho uma referência, mas 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 que 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 é a mesma que um ponteiro C que aponta para um local de memória inválido. Um ponteiro nulo literalmente não está apontando para nenhum lugar , que é sutilmente diferente de apontando para um local que é inválido.)
nullantes de usá-la, assim . Com variáveis locais, o compilador capturaria esse erro, mas neste caso não. Talvez isso faça uma adição útil à sua resposta?
Um bom lugar para começar é o 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.
- Tomando o comprimento de null como se fosse uma matriz.
- Acessando ou modificando os slots nulos como se fosse uma matriz.
- Lançar nulo como se fosse um valor Throwable.
Os aplicativos devem lançar instâncias dessa classe para indicar outros usos ilegais do objeto nulo.
Também é o caso que, se você tentar usar uma referência nula synchronized, isso também lançará essa 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.
Então você tem um NullPointerException. Como você conserta 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 log 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 ele 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. Se o método foi chamado, verifique 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 é suficiente para nos dar uma solução: adicione uma chamada printer.setName()antes de ligar printer.print().
A variável pode ter um valor padrão (e setNamepode impedir que seja definido como nulo):
private String name = "";
O método printou printStringpode procurar nulo , por exemplo:
printString((name == null) ? "" : name);
Ou você pode criar a classe para 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:
Se você tentou depurar o problema e ainda não tem uma solução, pode postar uma pergunta para obter mais ajuda, mas inclua 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 ).
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 como Java diz "nenhum objeto".
A NullPointerExceptioné lançado em tempo de execução sempre que seu programa tenta usar a 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 lançará a NullPointerException.
Existem várias maneiras de usar um nullvalor que resultará em a NullPointerException. De fato, as únicas coisas que você pode fazer nullsem causar um NPE são:
==ou !=operadores, ou instanceof.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 gera 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 é bobagem. É um rastreamento de pilha ... e fornece informações vitais que o ajudarão a rastrear o erro no seu código, se você ler atentamente.
Então, vejamos o que diz:
Exception in thread "main" java.lang.NullPointerException
A primeira linha do rastreamento da pilha informa várias coisas:
java.lang.NullPointerException.NullPointerExceptioné incomum nesse sentido, porque raramente tem 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 várias coisas:
mainmétodo da Testclasse.Se você contar as linhas no arquivo acima, a linha 4 é a que eu identifiquei 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 "at") informará onde o NPE foi lançado 1 .
Em resumo, o rastreamento da pilha nos dirá inequivocamente qual declaração do programa lançou o NPE.
1 - Não é bem verdade. Existem coisas chamadas exceções aninhadas ...
Esta é a parte difícil. A resposta curta é aplicar inferência lógica às evidências fornecidas pelo rastreamento de pilha, o código-fonte e a documentação relevante da API.
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 gerar um NPE?
De fato, existe apenas uma maneira: isso só pode acontecer se footiver o valor null. Em seguida, tentamos executar o length()método nulle ... BANG!
Mas (eu ouvi você dizer) e se o NPE fosse lançado dentro da length()chamada de método?
Bem, se isso acontecesse, o rastreamento da pilha ficaria diferente. A primeira linha "at" diria que a exceção foi lançada em alguma linha da java.lang.Stringclasse e a linha 4 de Test.javaseria a segunda linha "at".
Então, de onde isso nullveio? Nesse caso, é óbvio e é óbvio o que precisamos fazer para corrigi-lo. (Atribua 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)
$
Então agora temos duas linhas "at". 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 gerar um NPE? Existem duas maneiras:
barfor null, bar[pos]lançará um NPE.bar[pos]estiver nullchamando length(), 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 observarmos como testfoi chamado, podemos ver que ele vem da foovariável estática. Além disso, podemos ver claramente que inicializamos foocom um valor não nulo. Isso é suficiente para ignorar provisoriamente essa explicação. (Em teoria, algo mais poderia mudar foo para null... mas isso não está acontecendo aqui.)
E o 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 inicializadosnull . Depois disso, não alteramos o conteúdo de foo... foo[1]ainda será null.
É como se você estivesse tentando acessar um objeto que é null. Considere o exemplo abaixo:
TypeA objA;
No momento, você acabou de declarar esse objeto, mas não foi inicializado ou instanciado . E sempre que você tentar acessar qualquer propriedade ou método nele, será lançado o NullPointerExceptionque faz sentido.
Veja também este exemplo abaixo:
String a = null;
System.out.println(a.toString()); // NullPointerException will be thrown
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:
nullobjeto.nullobjeto.nullcomo se fosse uma matriz.nullcomo se fosse uma matriz.nullcomo se fosse um valor jogável.Os aplicativos devem lançar instâncias dessa classe para indicar outros usos ilegais do nullobjeto.
Referência: http://docs.oracle.com/javase/8/docs/api/java/lang/NullPointerException.html
nullcomo o alvo de um synchronizedbloco, 2) usando a nullcomo o alvo de a switch, e unboxing null.
Um nullponteiro é aquele que aponta para lugar nenhum. Quando você desreferencia um ponteiro p, diz "me dê os dados no local armazenado em" p ". Quando pé um nullponteiro, o local armazenado pé nowhere, você está dizendo" me dê os dados no local 'em nenhum lugar' ". Obviamente, ele não pode fazer isso, então lança a null pointer exception.
Em geral, é porque algo não foi inicializado corretamente.
NULLé escrito como nullem java. E é uma coisa sensível a maiúsculas e minúsculas.
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 NullPointerExceptions.
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:
finalmodificador para impor uma boa inicialização.@NotNulle@Nullableif("knownObject".equals(unknownObject)valueOf()mais toString().StringUtilsmétodos seguros nulos StringUtils.isEmpty(null).@Nullableconforme listado acima) e alertam sobre possíveis erros. Também é possível inferir e gerar essas anotações (por exemplo, o IntelliJ pode fazer isso) com base na estrutura de código existente.
if (obj==null). Se for nulo, você deve escrever um código para lidar com isso também.
Em Java, tudo (excluindo tipos primitivos) está na forma de uma classe.
Se você deseja usar qualquer objeto, então você tem duas fases:
Exemplo:
Object object;object = new Object();O mesmo para o conceito de matriz:
Item item[] = new Item[5];item[0] = new Item();Se você não está fornecendo a seção de inicialização, então NullPointerExceptionsurge.
Uma exceção de ponteiro nulo é um indicador de que você está usando um objeto sem inicializá-lo.
Por exemplo, abaixo está uma turma de alunos que a utilizará 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 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 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, todas as variáveis que você declara são na verdade "referências" aos objetos (ou primitivas) e não aos objetos em si.
Quando você tenta executar um método de objeto, a referência solicita que o objeto ativo execute esse método. Mas se a referência estiver fazendo referência a NULL (nada, zero, vazio, nada), não há como o método ser executado. Em seguida, o tempo de execução permite que você saiba disso lançando uma NullPointerException.
Sua referência é "apontando" para nulo, portanto "Nulo -> Ponteiro".
O objeto reside no espaço de memória da VM e a única maneira de acessá-lo é usando 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 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...
Isso é uma coisa importante a saber - quando não houver mais referências a um objeto (no exemplo acima, quando referencee otherReferenceambos apontarem para nulo), o objeto estará "inacessível". Não há como trabalhar com ele; portanto, esse objeto está pronto para ser coletado como lixo e, em algum momento, a VM liberará a memória usada por esse objeto e alocará outro.
Outra ocorrência de a NullPointerExceptionocorre quando alguém declara uma matriz de objetos e tenta imediatamente desreferenciar elementos dentro dela.
String[] phrases = new String[10];
String keyPhrase = "Bird";
for(String phrase : phrases) {
System.out.println(phrase.equals(keyPhrase));
}
Esse NPE em particular pode ser evitado se a ordem de comparação for revertida; ou seja, use .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 matriz 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));
}
Optionalera retornar nulo. A palavra-chave está correta. Saber se proteger contra isso é fundamental. Isso oferece uma ocorrência comum e maneiras de mitigá-lo.