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 int
e Java a inicializará 0
para você. Quando você atribui o valor de 10
na 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 Integer
referê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 new
palavra-chave é usada para instanciar (ou criar) um objeto do tipo Integer
e a variável ponteiro num
é atribuída a esse Integer
objeto.
Isso NullPointerException
ocorre 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 num
antes 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 NullPointerException
porque é 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=b
pode lançar um NPE se b for an Integer
. Há casos em que isso é confuso para depuração.
NullPointerException
problemas em seu código é usar @Nullable
e @NotNull
anotaçõ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?)
NullPointerException
s 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 NullPointerException
página javadoc.
Provavelmente, o código de exemplo mais rápido que eu poderia criar para ilustrar um NullPointerException
seria:
public class Example {
public static void main(String[] args) {
Object obj = null;
obj.hashCode();
}
}
Na primeira linha dentro main
, estou explicitamente definindo a Object
referência obj
igual 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 NullPointerException
porque 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.)
null
antes 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
NullPointerException
será 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 printString
mé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 length
mé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.name
definido? 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 setName
pode impedir que seja definido como nulo):
private String name = "";
O método print
ou printString
pode 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, null
que é a maneira como Java diz "nenhum objeto".
A NullPointerException
é lançado em tempo de execução sempre que seu programa tenta usar a null
como 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 null
referência, e isso lançará a NullPointerException
.
Existem várias maneiras de usar um null
valor que resultará em a NullPointerException
. De fato, as únicas coisas que você pode fazer null
sem 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 javac
compilador 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:
main
método da Test
classe.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 foo
tiver o valor null
. Em seguida, tentamos executar o length()
método null
e ... 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.String
classe e a linha 4 de Test.java
seria a segunda linha "at".
Então, de onde isso null
veio? 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:
bar
for null
, bar[pos]
lançará um NPE.bar[pos]
estiver null
chamando 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 bar
vem? É um parâmetro para a test
chamada do método e, se observarmos como test
foi chamado, podemos ver que ele vem da foo
variável estática. Além disso, podemos ver claramente que inicializamos foo
com 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 NullPointerException
que 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:
null
objeto.null
objeto.null
como se fosse uma matriz.null
como se fosse uma matriz.null
como se fosse um valor jogável.Os aplicativos devem lançar instâncias dessa classe para indicar outros usos ilegais do null
objeto.
Referência: http://docs.oracle.com/javase/8/docs/api/java/lang/NullPointerException.html
null
como o alvo de um synchronized
bloco, 2) usando a null
como o alvo de a switch
, e unboxing null
.
Um null
ponteiro é 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 null
ponteiro, 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 null
em 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 NullPointerException
s.
Veja também: Uma boa lista de práticas recomendadas
Eu acrescentaria, muito importante, fazer um bom uso do final
modificador.
Usando o modificador "final" sempre que aplicável em Java
Resumo:
final
modificador para impor uma boa inicialização.@NotNull
e@Nullable
if("knownObject".equals(unknownObject)
valueOf()
mais toString()
.StringUtils
métodos seguros nulos StringUtils.isEmpty(null)
.@Nullable
conforme 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 NullPointerException
surge.
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 this
referê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 reference
e otherReference
ambos 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 NullPointerException
ocorre 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 .equals
em 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));
}
Optional
era retornar nulo. A palavra-chave está correta. Saber se proteger contra isso é fundamental. Isso oferece uma ocorrência comum e maneiras de mitigá-lo.