1. Obtener un seguimiento de la pila

Obtener un seguimiento de la pila

El lenguaje de programación Java ofrece muchas formas para que un programador obtenga información sobre lo que está sucediendo en un programa. Y no solo palabras.

Por ejemplo, después de compilar los programas C++, se convierten en un gran archivo lleno de código de máquina, y todo lo que está disponible para un programador en tiempo de ejecución es la dirección del bloque de memoria que contiene el código de máquina que se está ejecutando actualmente. No mucho, digamos.

Pero para Java, incluso después de compilar un programa, las clases siguen siendo clases, los métodos y las variables no desaparecen, y el programador tiene muchas formas de obtener información sobre lo que sucede en el programa.

Rastreo de pila

Por ejemplo, en un momento de la ejecución de un programa, puede averiguar la clase y el nombre del método que se está ejecutando actualmente. Y no solo un método: puede obtener información sobre toda la cadena de llamadas de método desde el método actual hasta el main()método.

Una lista que consiste en el método actual, el método que lo invocó y el método que lo llamó, etc. se denomina seguimiento de pila . Puedes obtenerlo con esta declaración:

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

También puedes escribirlo en dos líneas:

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

currentThread()El método estático de la Threadclase devuelve una referencia a un Threadobjeto, que contiene información sobre el hilo actual, es decir, el hilo actual de ejecución. Aprenderá más sobre subprocesos en los niveles 17 y 18 de la búsqueda de Java Core .

Este Threadobjeto tiene un getStackTrace()método, que devuelve una matriz de StackTraceElementobjetos, cada uno de los cuales contiene información sobre un método. En conjunto, todos estos elementos forman un rastro de pila .

Ejemplo:

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);
   }
}
Salida de consola
java.base/java.lang.Thread.getStackTrace(Thread.java:1606)
Main.test(Main.java:11)
Main.main(Main.java:5)

Como podemos ver en la salida de la consola del ejemplo, el getStackTrace()método devolvió una matriz de tres elementos:

  • getStackTrace()metodo de la Threadclase
  • test()metodo de la Mainclase
  • main()metodo de la Mainclase

A partir de este seguimiento de la pila, podemos concluir que:

  • El Thread.getStackTrace()método fue llamado por el Main.test()método en la línea 11 del archivo Main.java
  • El Main.test()método fue llamado por el Main.main()método en la línea 5 del archivo Main.java
  • Nadie llamó al Main.main()método: este es el primer método en la cadena de llamadas.

Por cierto, solo parte de la información disponible se mostró en la pantalla. Todo lo demás se puede obtener directamente del StackTraceElementobjeto.



2.StackTraceElement

Como sugiere su nombre, la StackTraceElementclase se creó para almacenar información sobre un elemento de seguimiento de la pila , es decir, un método en el archivo stack trace.

Esta clase tiene los siguientes métodos de instancia:

Método Descripción
String getClassName()
Devuelve el nombre de la clase.
String getMethodName()
Devuelve el nombre del método.
String getFileName()
Devuelve el nombre del archivo (un archivo puede contener varias clases)
int getLineNumber()
Devuelve el número de línea en el archivo donde se llamó al método
String getModuleName()
Devuelve el nombre del módulo (puede ser null)
String getModuleVersion()
Devuelve la versión del módulo (puede ser null)

Pueden ayudarlo a obtener información más completa sobre la pila de llamadas actual:

Código Salida de consola Nota
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
nombre de clase nombre
de método nombre de
archivo
número de línea
nombre
de módulo versión de módulo

nombre de clase
nombre de método
nombre de archivo
número de línea
nombre de
módulo versión de módulo

nombre de clase nombre de
método nombre de
archivo
número de línea
nombre de módulo
versión de módulo


3. Apilar

Ya sabes qué es un seguimiento de pila , pero ¿qué es una pila (clase Stack)?

Una pila es una estructura de datos a la que puede agregar elementos y desde la cual puede recuperar elementos. Al hacerlo, solo puede tomar elementos del final: primero toma el último agregado, luego el penúltimo agregado, etc.

La pila de nombres en sí sugiere este comportamiento, como la forma en que interactuaría con una pila de papeles. Si coloca las hojas 1, 2 y 3 en una pila, debe recuperarlas en orden inverso: primero la tercera hoja, luego la segunda y solo luego la primera.

Java incluso tiene una clase especial de colección Stack con el mismo nombre y comportamiento. Esta clase comparte muchos comportamientos con ArrayListy LinkedList. Pero también tiene métodos que implementan el comportamiento de la pila:

Métodos Descripción
T push(T obj)
Agrega el objelemento a la parte superior de la pila.
T pop()
Toma el elemento de la parte superior de la pila (la profundidad de la pila disminuye)
T peek()
Devuelve el elemento en la parte superior de la pila (la pila no cambia)
boolean empty()
Comprueba si la colección está vacía.
int search(Object obj)
Busca un objeto en la colección y devuelve suindex

Ejemplo:

Código Contenido de la pila (la parte superior de la pila está a la derecha)
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]

Las pilas se utilizan con bastante frecuencia en la programación. Así que esta es una colección útil.



4. Mostrar un seguimiento de la pila durante el manejo de excepciones

¿ Por qué una lista de llamadas a métodos se denomina seguimiento de pila ? Porque si piensa en la lista de métodos como una pila de hojas de papel con nombres de métodos, cuando llama al siguiente método, agrega una hoja con el nombre de ese método a la pila. Y la siguiente hoja de papel va encima de eso, y así sucesivamente.

Cuando finaliza un método, se elimina la hoja en la parte superior de la pila. No puede quitar una hoja del medio de la pila sin quitar todas las hojas que están encima. De manera similar, no puede terminar un método en medio de una cadena de llamadas sin terminar todos los métodos que ha llamado.

Excepciones

Otro uso interesante para las pilas es durante el manejo de excepciones.

Cuando ocurre un error en un programa y se lanza una excepción , la excepción contiene el seguimiento de la pila actual : una matriz que consta de una lista de métodos que comienzan, desde el método principal y terminan con el método donde ocurrió el error. ¡Incluso está la línea donde se lanzó la excepción!

Este seguimiento de la pila se almacena dentro de la excepción y se puede recuperar fácilmente mediante el siguiente método:StackTraceElement[] getStackTrace()

Ejemplo:

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




Capturar la excepción

Obtenga el seguimiento de la pila que existía cuando ocurrió el error.

Este es un método de la Throwableclase, por lo que todos sus descendientes (es decir, todas las excepciones) tienen el getStackTrace()método. Súper conveniente, ¿eh?

Mostrar el seguimiento de la pila de la excepción

Por cierto, la Throwableclase tiene otro método para trabajar con seguimientos de pila, un método que muestra toda la información de seguimiento de pila almacenada dentro de la excepción. se llama printStackTrace().

Muy convenientemente, puede llamarlo en cualquier excepción.

Ejemplo:

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