1. Obtendo um rastreamento de pilha

Obtendo um rastreamento de pilha

A linguagem de programação Java oferece muitas maneiras para um programador obter informações sobre o que está acontecendo em um programa. E não apenas palavras.

Por exemplo, depois que os programas C++ são compilados, eles se tornam um grande arquivo cheio de código de máquina, e tudo o que está disponível para um programador em tempo de execução é o endereço do bloco de memória que contém o código de máquina que está sendo executado no momento. Não muito, digamos.

Mas para Java, mesmo depois que um programa é compilado, as classes permanecem classes, os métodos e as variáveis ​​não desaparecem, e o programador tem muitas maneiras de obter informações sobre o que está acontecendo no programa.

Rastreamento de pilha

Por exemplo, no ponto da execução de um programa, você pode descobrir a classe e o nome do método que está sendo executado. E não apenas um método — você pode obter informações sobre toda a cadeia de chamadas de método do método atual de volta ao main()método.

Uma lista que consiste no método atual e no método que o invocou, e no método que o chamou, etc., é chamada de rastreamento de pilha . Você pode obtê-lo com esta declaração:

StackTraceElement[] methods = Thread.currentThread().getStackTrace();

Você também pode escrevê-lo como duas linhas:

Thread current = Thread.currentThread();
StackTraceElement[] methods = current.getStackTrace();

O método estático currentThread()da Threadclasse retorna uma referência a um Threadobjeto, que contém informações sobre a thread atual, ou seja, a thread atual de execução. Você aprenderá mais sobre threads nos níveis 17 e 18 da missão Java Core .

Esse Threadobjeto tem um getStackTrace()método, que retorna uma matriz de StackTraceElementobjetos, cada um contendo informações sobre um método. Juntos, todos esses elementos formam um rastreamento de pilha .

Exemplo:

Código
public class Main
{
   public static void main(String[] args)
   {
      test();
   }

   public static void test()
   {
      Thread current = Thread.currentThread();
      StackTraceElement[] methods = current.getStackTrace();

      for(var info: methods)
         System.out.println(info);
   }
}
Saída do console
java.base/java.lang.Thread.getStackTrace(Thread.java:1606)
Main.test(Main.java:11)
Main.main(Main.java:5)

Como podemos ver na saída do console do exemplo, o getStackTrace()método retornou um array de três elementos:

  • getStackTrace()método da Threadclasse
  • test()método da Mainclasse
  • main()método da Mainclasse

A partir desse rastreamento de pilha, podemos concluir que:

  • O Thread.getStackTrace()método foi chamado pelo Main.test()método na linha 11 do arquivo Main.java
  • O Main.test()método foi chamado pelo Main.main()método na linha 5 do arquivo Main.java
  • Ninguém chamou o Main.main()método — este é o primeiro método na cadeia de chamadas.

A propósito, apenas algumas das informações disponíveis foram exibidas na tela. Todo o resto pode ser obtido diretamente do StackTraceElementobjeto



2.StackTraceElement

Como o próprio nome sugere, a StackTraceElementclasse foi criada para armazenar informações sobre um elemento de rastreamento de pilha , ou seja, um método no arquivo stack trace.

Esta classe tem os seguintes métodos de instância:

Método Descrição
String getClassName()
Retorna o nome da classe
String getMethodName()
Retorna o nome do método
String getFileName()
Retorna o nome do arquivo (um arquivo pode conter várias classes)
int getLineNumber()
Retorna o número da linha no arquivo onde o método foi chamado
String getModuleName()
Retorna o nome do módulo (pode ser null)
String getModuleVersion()
Retorna a versão do módulo (pode ser null)

Eles podem ajudá-lo a obter informações mais completas sobre a pilha de chamadas atual:

Código Saída do console Observação
public class Main
{
   public static void main(String[] args)
   {
      test();
   }

   public static void test()
   {
      Thread current = Thread.currentThread();
      StackTraceElement[] methods = current.getStackTrace();

      for(StackTraceElement info: methods)
      {
         System.out.println(info.getClassName());
         System.out.println(info.getMethodName());

         System.out.println(info.getFileName());
         System.out.println(info.getLineNumber());

         System.out.println(info.getModuleName());
         System.out.println(info.getModuleVersion());
         System.out.println();
      }
   }
}
java.lang.Thread
getStackTrace
Thread.java
1606
java.base
11.0.2

Main
test
Main.java
11
null
null

Main
main
Main.java
5
null
null
nome da classe nome
do método nome
do arquivo
número da linha
nome
do módulo versão do módulo

nome da classe
nome do método
nome do arquivo
número da linha
nome do
módulo versão do módulo

nome da classe nome
do método nome do
arquivo
número da linha
nome do módulo
versão do módulo


3. Pilha

Você já sabe o que é um rastreamento de pilha , mas o que é uma pilha (classe Stack)?

Uma pilha é uma estrutura de dados à qual você pode adicionar elementos e da qual pode recuperar elementos. Ao fazer isso, você só pode pegar elementos do final: você primeiro pega o último adicionado, depois o penúltimo adicionado, etc.

A própria pilha de nomes sugere esse comportamento, como você interagiria com uma pilha de papéis. Se você colocar as folhas 1, 2 e 3 em uma pilha, deverá recuperá-las na ordem inversa: primeiro a terceira folha, depois a segunda e só depois a primeira.

Java ainda tem uma classe especial de coleção Stack com o mesmo nome e comportamento. Essa classe compartilha muitos comportamentos com ArrayListe LinkedList. Mas também possui métodos que implementam o comportamento da pilha:

Métodos Descrição
T push(T obj)
Adiciona o objelemento ao topo da pilha
T pop()
Pega o elemento do topo da pilha (a profundidade da pilha diminui)
T peek()
Retorna o item no topo da pilha (a pilha não muda)
boolean empty()
Verifica se a coleção está vazia
int search(Object obj)
Procura um objeto na coleção e retorna seuindex

Exemplo:

Código Conteúdo da pilha (o topo da pilha está à direita)
Stack<Integer> stack = new Stack<Integer>();
stack.push(1);
stack.push(2);
stack.push(3);
int x = stack.pop();
stack.push(4);
int y = stack.peek();
stack.pop();
stack.pop();

[1]
[1, 2]
[1, 2, 3]
[1, 2]
[1, 2, 4]
[1, 2, 4]
[1, 2]
[1]

As pilhas são usadas com bastante frequência na programação. Portanto, esta é uma coleção útil.



4. Exibindo um rastreamento de pilha durante o tratamento de exceções

Por que uma lista de chamadas de método é chamada de rastreamento de pilha ? Porque se você pensar na lista de métodos como uma pilha de folhas de papel com nomes de métodos, quando chamar o próximo método, você adicionará uma folha com o nome desse método à pilha. E a próxima folha de papel vai em cima dessa, e assim por diante.

Quando um método termina, a planilha no topo da pilha é removida. Você não pode remover uma folha do meio da pilha sem remover todas as folhas acima dela. Da mesma forma, você não pode encerrar um método no meio de uma cadeia de chamadas sem encerrar todos os métodos que ele chamou.

Exceções

Outro uso interessante para pilhas é durante o tratamento de exceções.

Quando ocorre um erro em um programa e uma exceção é lançada , a exceção contém o rastreamento de pilha atualuma matriz que consiste em uma lista de métodos começando, do método principal e terminando com o método onde ocorreu o erro. Existe até a linha onde a exceção foi lançada!

Este rastreamento de pilha é armazenado dentro da exceção e pode ser facilmente recuperado usando o seguinte método:StackTraceElement[] getStackTrace()

Exemplo:

Código Observação
try
{
   // An exception may occur here
}
catch(Exception e)
{
   StackTraceElement[] methods = e.getStackTrace()
}




Capture a exceção

Obtenha o rastreamento de pilha que existia quando o erro ocorreu.

Este é um método da Throwableclasse, então todos os seus descendentes (ou seja, todas as exceções) possuem o getStackTrace()método. Super conveniente, né?

Exibir o rastreamento de pilha da exceção

A propósito, a Throwableclasse tem outro método para trabalhar com rastreamentos de pilha, um método que exibe todas as informações de rastreamento de pilha armazenadas na exceção. É chamado printStackTrace().

Muito convenientemente, você pode chamá-lo em qualquer exceção.

Exemplo:

Código
try
{
   // An exception may occur here
}
catch(Exception e)
{
   e.printStackTrace();
}
Saída do console
java.base/java.lang.Thread.getStackTrace(Thread.java:1606)
Main.test(Main.java:11)
Main.main(Main.java:5)