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 e impedir que a exceção faça com que o programa seja encerrado prematuramente?
fonte
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 e impedir que a exceção faça com que o programa seja encerrado prematuramente?
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 anInteger
. 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á aNullPointerException
. Essas são as mais comuns, mas outras maneiras estão listadas naNullPointerException
página javadoc.Provavelmente, o código de exemplo mais rápido que eu poderia criar para ilustrar um
NullPointerException
seria:Na primeira linha dentro
main
, estou explicitamente definindo aObject
referênciaobj
igual anull
. 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 umNullPointerException
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.)
fonte
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?O que é uma NullPointerException?
Um bom lugar para começar é o JavaDocs . Eles têm isso coberto:
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 :Como faço para corrigir isso?
Então você tem um
NullPointerException
. Como você conserta isso? Vamos dar um exemplo simples que lança umNullPointerException
: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:
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 ques
é nulo, e chamar olength
método nele lança a exceção. Podemos ver que o programa para de lançar a exceção quandos.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 comprintString(name)
noprint()
método ethis.name
é nulo.Rastreie onde esses valores devem ser definidos
Onde está
this.name
definido? NosetName(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 ligarprinter.print()
.Outras correções
A variável pode ter um valor padrão (e
setName
pode impedir que seja definido como nulo):O método
print
ouprintString
pode procurar nulo , por exemplo:Ou você pode criar a classe para que
name
sempre tenha um valor não nulo :Veja também:
Ainda não consigo encontrar o problema
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 ).
fonte
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,null
que é a maneira como Java diz "nenhum objeto".A
NullPointerException
é lançado em tempo de execução sempre que seu programa tenta usar anull
como se fosse uma referência real. Por exemplo, se você escrever isto:a instrução rotulada "AQUI" tentará executar o
length()
método em umanull
referência, e isso lançará aNullPointerException
.Existem várias maneiras de usar um
null
valor que resultará em aNullPointerException
. De fato, as únicas coisas que você pode fazernull
sem causar um NPE são:==
ou!=
operadores, ouinstanceof
.Pergunta: Como leio o NPE StackTrace?
Suponha que eu compile e execute o programa acima:
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:
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.
Isso nos diz várias coisas:
main
método daTest
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 ...
Pergunta: Como rastrear a causa da exceção NPE no meu código?
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:
Como isso pode gerar um NPE?
De fato, existe apenas uma maneira: isso só pode acontecer se
foo
tiver o valornull
. Em seguida, tentamos executar olength()
métodonull
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 deTest.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 afoo
.)OK, então vamos tentar um exemplo um pouco mais complicado. Isso exigirá alguma dedução lógica .
Então agora temos duas linhas "at". O primeiro é para esta linha:
e o segundo é para esta linha:
Olhando para a primeira linha, como isso poderia gerar um NPE? Existem duas maneiras:
bar
fornull
,bar[pos]
lançará um NPE.bar[pos]
estivernull
chamandolength()
, 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 atest
chamada do método e, se observarmos comotest
foi chamado, podemos ver que ele vem dafoo
variável estática. Além disso, podemos ver claramente que inicializamosfoo
com um valor não nulo. Isso é suficiente para ignorar provisoriamente essa explicação. (Em teoria, algo mais poderia mudarfoo
paranull
... mas isso não está acontecendo aqui.)E o nosso segundo cenário? Bem, podemos ver que
pos
é1
, então isso significa quefoo[1]
deve sernull
. Isso é possível?De fato é! E esse é o problema. Quando inicializamos assim:
alocamos a
String[]
com dois elementos inicializadosnull
. Depois disso, não alteramos o conteúdo defoo
...foo[1]
ainda seránull
.fonte
É como se você estivesse tentando acessar um objeto que é
null
. Considere o exemplo abaixo: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:
fonte
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
fonte
null
como o alvo de umsynchronized
bloco, 2) usando anull
como o alvo de aswitch
, e unboxingnull
.Um
null
ponteiro é aquele que aponta para lugar nenhum. Quando você desreferencia um ponteirop
, diz "me dê os dados no local armazenado em" p ". Quandop
é umnull
ponteiro, o local armazenadop
énowhere
, você está dizendo" me dê os dados no local 'em nenhum lugar' ". Obviamente, ele não pode fazer isso, então lança anull pointer exception
.Em geral, é porque algo não foi inicializado corretamente.
fonte
NULL
é escrito comonull
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 JavaResumo:
final
modificador para impor uma boa inicialização.@NotNull
e@Nullable
if("knownObject".equals(unknownObject)
valueOf()
maistoString()
.StringUtils
métodos seguros nulosStringUtils.isEmpty(null)
.fonte
@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.fonte
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.
O código abaixo fornece uma exceção de ponteiro nulo.
Porque você está usando
student
, mas esqueceu de inicializá-lo como no código correto mostrado abaixo:fonte
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:E em outro lugar no seu código:
Isso é uma coisa importante a saber - quando não houver mais referências a um objeto (no exemplo acima, quando
reference
eotherReference
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.fonte
Outra ocorrência de a
NullPointerException
ocorre quando alguém declara uma matriz de objetos e tenta imediatamente desreferenciar elementos dentro dela.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.
fonte
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.