Noobs e StackTraces

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. #1 by Nat! on 22/02/2010 - 21:38

    Muito bom o texto, Macoli!
    Agora stacktraces sao minhas amigas! :p

  2. #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!!!

Deixe uma resposta

Preencha os seus dados abaixo ou clique em um ícone para log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Sair / Alterar )

Imagem do Twitter

You are commenting using your Twitter account. Sair / Alterar )

Foto do Facebook

You are commenting using your Facebook account. Sair / Alterar )

Connecting to %s

Seguir

Obtenha todo post novo entregue na sua caixa de entrada.