Vejo por aí muitos casos de pessoas em listas de discussão e fóruns sobre desenvolvimento Java postarem uns problemas no mínimo absurdos. Não absurdos de difíceis, mas absurdos de serem postados. São problemas do tipo IndexOutOfBounds, NullPointers bobos e até FileNotFoundException. Tudo bem que alguém que está começando pode ficar assustado quando vê seu terminal cuspindo linhas e linhas repetitivas, cheios de ‘at’ e ’caused by’. Algumas dão até náusea, mas vamos lá, Stack Traces existem pra ajudar e não para trazer uma mensagem de satã dizendo que seu computador será destruído.
O problema é que muita gente nem se dá ao trabalho de ler o que está escrito alí. Vê aquele monte de caracteres intimidadores no terminal e acha que é algo sério demais para sua cabeça processar. Se você se encontrar numa situação dessas, não se desespere e corra pro fórum/lista mais próximo. Respire fundo. Pense que poderia ser pior: Seu programa sai do nada, sem dizer nada, do mesmo jeito de quando sua namorada fica calada, com aquela cara de bolada e você pergunta “O que foi, amor” e recebe um convincente “Nada!”. Não ia dar nem pra correr pra algum lugar. Imagine um post numa lista:
Subject: U R G E N T E!!!! AJUDA POR FAVOR!!!
Gente, tava usando meu programa maneiro aqui aí quando eu cliquei no botão XPTO ele saiu! Alguem sabe o que é?
Meio dificil de responder, convenhamos. Mas o Java não te deixa na mão. Seu programa falha, mas a stack trace está lá, pra te apontar exatamente onde ocorreu o erro fatal. Isso facilita a vida do desenvolvedor e do noob, que vai correr atrás de alguém pra ajudar.
Agora vamos lá! Noob que é noob não quer mais ser noob. Faca nos dentes e vamos tentar aprender a ler Stack Traces.
Considere a seguinte classe:
package br.stacktraces.exemplo;
public class Exemplo {
public static void main(String[] args) {
Exemplo ex = new Exemplo();
ex.foo();
}
public void foo() {
bar();
}
public void bar() {
throw new RuntimeException("OMG! StackTraces!");
}
}
Se rodarmos o main, teremos o seguinte output:
Exception in thread "main" java.lang.RuntimeException: OMG! StackTraces! at br.stacktraces.exemplo.Exemplo.bar(Exemplo.java:15) at br.stacktraces.exemplo.Exemplo.foo(Exemplo.java:11) at br.stacktraces.exemplo.Exemplo.main(Exemplo.java:7)
Agora vamos lá, como nosso velho amigo Jack: por partes.
Exception in thread "main" java.lang.RuntimeException: OMG! StackTraces!
A sintaxe é: Exception in thread “<Nome da thread>” <Nome da exceção>: <Mensagem da exceção>
- Nome da thread: se você usa multithreading no seu programa, isso te ajuda a descobrir em qual thread ocorreu a exceção.
- Tipo da Exceção: a classe que representa a exceção. Geralmente é um nome curto que especifica o tipo do problema, como NumberFormatException e FileNotFoundException.
- Mensagem da Exceção: geralmente uma explicação mais específica do problema.
at br.stacktraces.exemplo.Exemplo.bar(Exemplo.java:15) at br.stacktraces.exemplo.Exemplo.foo(Exemplo.java:11) at br.stacktraces.exemplo.Exemplo.main(Exemplo.java:7)
A sintaxe é: at <Nome completo do método>(<Arquivo fonte>:<linha>)
- Nome completo do método: nome completo é a concatenação dos nomes do pacote, da classe e do método.
- Arquivo fonte: o arquivo no qual se encontra o código do método em questão.
- Linha: a linha na qual o ocorreu o erro.
Agora vamos aprender a ler esse bagulho. O nome Stack não é a toa. Se você notar, o texto que contém os “at” forma uma pilha de chamadas, ou seja, é todo o caminho das chamadas de métodos que seu programa fez até a exceção ser lançada.
No topo, temos a linha onde a exceção foi lançada:
throw new RuntimeException("OMG! StackTraces!");
Logo abaixo, a linha em que chamamos o método que lançou a exceção:
bar();
E assim vai até o método main, que é onde ocorre a chamada que desencadeia a exceção:
ex.foo();
Ler esta Stack Trace é fácil. Ela só indica métodos que nós mesmos escrevemos. E quando ela faz referências a métodos que não escrevemos e nem sabíamos que existiam?
Considere o próximo exemplo:
public class Exemplo {
public static void main(String[] args) {
Exemplo ex = new Exemplo();
ex.getNumeroUm();
}
public Number getNumeroUm() {
return new Integer("um");
}
}
Com o output:
Exception in thread "main" java.lang.NumberFormatException: For input string: "um" at java.lang.NumberFormatException.forInputString(Unknown Source) at java.lang.Integer.parseInt(Unknown Source) at java.lang.Integer.<init>(Unknown Source) at br.stacktraces.exemplo.Exemplo.getNumeroUm(Exemplo.java:19) at br.stacktraces.exemplo.Exemplo.main(Exemplo.java:7)
Vejamos o que mudou na Stack Trace do exemplo anterior:
Exception in thread "main" java.lang.NumberFormatException: For input string: "um"
- Tipo da Exceção: java.lang.NumberFormatException. Opa! Agora já sabemos que é uma exceção de formato de número.
- Mensagem da Exceção: For input string: “um”. Parece que o Java não sabe criar números a partir de Strings por extenso.
Agora já temos noção de sobre o que se trata o erro. Vamos descobrir onde ele está e consertá-lo:
at java.lang.NumberFormatException.forInputString(Unknown Source)
O método que lançou a exceção se chama forInputString. Não fomos nós que escrevemos e não temos o código fonte dele, mas sabemos que é um método java.lang, então o problema não deve ser alí. O que fazer agora? Vamos para a linha seguinte:
at java.lang.Integer.parseInt(Unknown Source)
Não fomos felizes desta vez. Próxima linha:
at java.lang.Integer.<init>(Unknown Source)
Outro java.lang… Próxima:
at br.stacktraces.exemplo.Exemplo.getNumeroUm(Exemplo.java:19)
Opa! Esse é meu! Vamos olhar:
return new Integer("um");
É, acho que o java não cria números com Strings por extenso…
Pronto! Achamos nosso problema e agora podemos consertá-lo. Graças à quem? À mensageira demoníaca, dona Stack Trace.
Agora falta mostrar apenas mais um componente da famigerada StackTrace: o caused by.
Este é bem simples, como os outros. Quando uma exceção é lançada por causa de outra exceção, o caused by aparece. Aproveitando o último exemplo, considere a seguinte modificação ao método getNumeroUm():
public Number getNumeroUm() {
try {
return new Integer("um");
} catch (NumberFormatException nfe) {
throw new RuntimeException("Deu ruim!", nfe);
}
}
Que nos gera o output:
Exception in thread "main" java.lang.RuntimeException: Deu ruim! at br.stacktraces.exemplo.Exemplo.getNumeroUm(Exemplo.java:22) at br.stacktraces.exemplo.Exemplo.main(Exemplo.java:7) Caused by: java.lang.NumberFormatException: For input string: "um" at java.lang.NumberFormatException.forInputString(Unknown Source) at java.lang.Integer.parseInt(Unknown Source) at java.lang.Integer.<init>(Unknown Source) at br.stacktraces.exemplo.Exemplo.getNumeroUm(Exemplo.java:20)
Agora repare que nossa RuntimeException não apenas diz que “Deu ruim!”, mas também nos indica que “Deu ruim!” por causa de uma outra exceção: a NumberFormatException.
Caused by’s são comuns em exceções lançadas por frameworks, ou métodos que encapsulam outras tarefas. Imagine que o nosso getNumeroUm() fizesse parte de um framework de uma determinada aplicação e dado o erro de formato de número, precisássemos também lançar uma exceção proprietária, para notificar a aplicação que houve um erro interno. O caused by seria uma informação mais “underground” sobre o problema ocorrido e facilitaria a vida do estagiário que tentaria resolver o problema.
Espero que agora a nossa amiga Stack Trace deixe de ser uma mensageira de satã e passe a ser sua companheira na hora de corrigir erros no seu código. Tenha em mente as seguintes dicas:
- É muito, mas muito provável mesmo que alguém já teve o mesmo problema que você e tenha buscado ajuda. Google é seu amigo. Pesquise bastante antes de procurar ajuda. A informação que você procura pode ser dificil, mas lendo casos semelhantes, mesmo que não resolvam especificamente o seu problema, agregam conhecimento.
- Quando a Stack Trace for muito cabeluda, do tipo de dezenas ou centenas de linhas, treine seu olho para procurar nomes de pacotes que você criou, como o dos exemplos: br.stacktraces. Assim você pode passar o olho rápido perto dos “at” até encontrar um nome familiar e começar a investigar dali.
- Stack Traces genéricas. Essas são um pesadelo. São aquelas que são tão úteis quanto “java.lang.RuntimeException: Deu ruim!”. Na dificuldade de debugar alguma dessas, considere utilizar uma ferramenta de debug. O eclipse tem uma ótima e é muito facil de usar.
- Uma possibilidade pouco provável, mas vale nota. Se você usa algum framework em desenvolvimento, já tentou de tudo pra consertar seu código e o erro persiste, considere baixar o código do framework (se disponível) e dê uma olhada. Ja presenciei casos de horas e horas perdidas por bug no framework.
- Quando ao desenvolver uma aplicação houver necessidade de lançar uma exceção, procure bastante uma exceção que especifique a classe do problema, se não achar ou tiver preguiça, crie uma e seja absolutamente claro no texto da mensagem.
Lembre-se: StackTraces estão aí pra ajudar. Ficar assustado no início é perfeitamente normal, mas coragem e esforço pra lê-las pagam. Depois de um curto tempo você não será mais o cara que pergunta, mas o cara que responde
.
#1 by Nat! on 22/02/2010 - 21:38
Muito bom o texto, Macoli!
Agora stacktraces sao minhas amigas! :p
#2 by vitor pinel on 14/01/2012 - 14:26
Como noobs sempre tive medo desses tal de Stack ai que vc falou mais agora que sei entender-lo melhor ele realmente ajuda muito!!! Muito bom o text Macoli espero um dia estar respondendo tb!!!