Indice
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 para que você impeça a exceção de fazer com que o programa seja encerrado prematuramente?
REPOSTA 1:
Existem dois tipos abrangentes de variáveis em Java:
- Primitivas : variáveis que contêm dados. Se você deseja manipular os dados em uma variável primitiva, pode manipular essa variável diretamente. Por convenção, os tipos primitivos começam com uma letra minúscula. Por exemplo, variáveis do tipo
int
ouchar
são primitivas. - Referências : variáveis que contêm o endereço de memória de um
Object
, ou seja, variáveis que se referem a umObject
. Se você quiser manipular oObject
que uma variável de referência se refere, você deve desreferenciá -la. Desreferenciar geralmente envolve usar.
para acessar um método ou campo, ou usar[
para indexar uma matriz. Por convenção, os tipos de referência geralmente são indicados com um tipo que começa em maiúsculas. Por exemplo, variáveis do tipoObject
são referências.
Considere o seguinte código onde você declara uma variável de tipo primitivo int
e não a inicializa:
int x;
int y = x + x;
Essas duas linhas travarão o programa porque nenhum valor foi especificado x
e estamos tentando usar o valor de x
para especificar y
. Todas as primitivas devem ser inicializadas com um valor utilizável antes de serem manipuladas.
Agora é aqui que as coisas ficam interessantes. As variáveis de referência null
podem ser definidas para o que significa ” Não estou fazendo referência a nada “. Você pode obter um valor null
em uma variável de referência se defini-la explicitamente dessa maneira, ou uma variável de referência não foi inicializada e o compilador não a detectou (o Java definirá automaticamente a variável como null
).
Se uma variável de referência for definida como nula explicitamente por você ou por meio de Java automaticamente, e você tentar desreferenciá -la, obterá um arquivo NullPointerException
.
O NullPointerException
(NPE) normalmente ocorre quando você declara uma variável, mas não cria um objeto e o atribui à variável antes de tentar usar o conteúdo da variável. Então você tem uma referência a algo que na verdade não existe.
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 de referência. Como você ainda não disse o que apontar, o Java o define como null
.
Na segunda linha, a new
palavra-chave é usada para instanciar (ou criar) um objeto do tipo Integer
, e a variável de referência num
é atribuída a esse Integer
objeto.
Se você tentar cancelar a referência num
antes de criar o objeto, obterá um arquivo 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 o objeto diretamente.
Por exemplo, você pode ter um método da seguinte forma:
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 assumindo 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
is null
, e a instrução obj.myMethod()
lançará um NullPointerException
.
Se o método destina-se a fazer algo para o objeto passado como o método acima faz, é apropriado lançar NullPointerException
porque é um erro do programador e o programador precisará dessa informação para fins de depuração.
Além de NullPointerException
s lançados como resultado da lógica do método, você também pode verificar os argumentos do método em busca de valores null
e lançar NPEs explicitamente adicionando algo como o seguinte próximo ao 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 validar isso é que 1) você pode retornar suas próprias mensagens de erro mais claras e 2) para o restante do método, você sabe que, a menos que obj
seja reatribuído, não é nulo e pode ser desreferenciado com segurança.
Como alternativa, pode haver casos em que o objetivo 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 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
}
}
Quais métodos/ferramentas podem ser usados para determinar a causa para que você impeça a exceção de fazer com que o programa seja encerrado prematuramente?
Sonar com encontrar bugs pode detectar NPE. O sonar pode capturar exceções de ponteiro nulo causadas pela JVM dinamicamente
Agora o Java 14 adicionou um novo recurso de linguagem para mostrar a causa raiz de NullPointerException. Esse recurso de linguagem faz parte do SAP JVM comercial desde 2006.
No Java 14, o exemplo a seguir é uma mensagem de exceção NullPointerException:
no thread “main” java.lang.NullPointerException: não é possível invocar “java.util.List.size()” porque “list” é nulo
Lista de situações que causam NullPointerException
a ocorrência de um
Aqui estão todas as situações em que NullPointerException
ocorre, que são diretamente mencionadas pela Java Language Specification JLS:
- Acessando (ou seja, obtendo ou configurando) um campo de instância de uma referência nula. (campos estáticos não contam!)
- Chamar um método de instância de uma referência nula. (métodos estáticos não contam!)
throw null;
- Acessando elementos de um array nulo.
- Sincronizando em null –
synchronized (someNullReference) { ... }
- Qualquer operador de ponto flutuante/inteiro pode lançar um
NullPointerException
s e um de seus operandos for uma referência nula em caixa - Uma conversão unboxing gera um
NullPointerException
s e o valor in a box for nulo. - Chamar
super
uma referência nula gera umNullPointerException
. Se você está confuso, isso está falando sobre invocações de construtor de superclasse qualificadas:
class Outer {
class Inner {}
}
class ChildOfInner extends Outer.Inner {
ChildOfInner(Outer o) {
o.super(); // if o is null, NPE gets thrown
}
}
RESPOSTA 2
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 referenciando um objeto. Chamar um método em uma referência nula ou tentar acessar um campo de uma referência nula acionará um NullPointerException
. Essas são as mais comuns, mas outras formas estão listadas na NullPointerException
página javadoc.
Provavelmente, o código de exemplo mais rápido que eu poderia criar para ilustrar NullPointerException
seria:
public class Example {
public static void main(String[] args) {
Object obj = null;
obj.hashCode();
}
}
Na primeira linha dentro main
de , estou definindo explicitamente a Object
referência obj
igual a null
. Isso significa que tenho uma referência, mas não está apontando para nenhum objeto. Depois disso, tento tratar a referência como se apontasse para um objeto chamando um método nele. Isso resulta em um NullPointerException
porque não há código para executar 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 lugar nenhum , o que é sutilmente diferente de apontando para um local que é inválido.)
REPOSTA 3
O que é um NullPointerException?
Um bom lugar para começar é o JavaDocs .
Lançado quando um aplicativo tenta usar nulo 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 um array.
- Acessando ou modificando os slots de null como se fosse um array.
- Lançando null 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 com 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, um
NullPointerException
será lançado.
Como faço para corrigir isso?
Então você tem um arquivo 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 stacktrace . 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 método length
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 a origem desses 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.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, 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 print.
Isso é o suficiente para nos dar uma solução: adicione uma chamada a printer.setName()
antes de chamar printer.print()
.
Outras correções
A variável pode ter um valor padrão (e setName
pode impedir que seja definida como nula):
private String name = "";
O método print
ou printString
pode verificar null , por exemplo:
printString((name == null) ? "" : name);
Ou você pode projetar 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();}
}
Fonte: stackoverflow
Quer aprende mais sobre programação?
8 truques interessantes do Python