1. Ottenere una traccia dello stack

Ottenere una traccia dello stack

Il linguaggio di programmazione Java offre a un programmatore molti modi per ottenere informazioni su ciò che sta accadendo in un programma. E non solo parole.

Ad esempio, dopo che i programmi C++ sono stati compilati, diventano un file di grandi dimensioni pieno di codice macchina e tutto ciò che è disponibile per un programmatore in fase di esecuzione è l'indirizzo del blocco di memoria che contiene il codice macchina attualmente in esecuzione. Non molto, diciamo.

Ma per Java, anche dopo che un programma è stato compilato, le classi rimangono classi, i metodi e le variabili non scompaiono e il programmatore ha molti modi per ottenere informazioni su ciò che sta accadendo nel programma.

Traccia dello stack

Ad esempio, in un punto dell'esecuzione di un programma, puoi scoprire la classe e il nome del metodo attualmente in esecuzione. E non solo un metodo: puoi ottenere informazioni sull'intera catena di chiamate di metodo dal metodo corrente al main()metodo.

Un elenco che consiste nel metodo corrente, nel metodo che lo ha richiamato e nel metodo che lo ha chiamato, ecc. è chiamato stack trace . Puoi ottenerlo con questa dichiarazione:

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

Puoi anche scriverlo in due righe:

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

Il metodo statico currentThread()della Threadclasse restituisce un riferimento a un Threadoggetto, che contiene informazioni sul thread corrente, ovvero il thread corrente di esecuzione. Imparerai di più sui thread nei livelli 17 e 18 della ricerca Java Core .

Questo Threadoggetto ha un getStackTrace()metodo, che restituisce un array di StackTraceElementoggetti, ognuno dei quali contiene informazioni su un metodo. Presi insieme, tutti questi elementi formano una traccia dello stack .

Esempio:

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

Come possiamo vedere nell'output della console dell'esempio, il getStackTrace()metodo ha restituito un array di tre elementi:

  • getStackTrace()metodo della Threadclasse
  • test()metodo della Mainclasse
  • main()metodo della Mainclasse

Da questa traccia dello stack, possiamo concludere che:

  • Il Thread.getStackTrace()metodo è stato chiamato dal Main.test()metodo alla riga 11 del file Main.java
  • Il Main.test()metodo è stato chiamato dal Main.main()metodo alla riga 5 del file Main.java
  • Nessuno ha chiamato il Main.main()metodo: questo è il primo metodo nella catena di chiamate.

A proposito, sullo schermo sono state visualizzate solo alcune delle informazioni disponibili. Tutto il resto può essere ottenuto direttamente StackTraceElementdall'oggetto



2.StackTraceElement

Come suggerisce il nome, la StackTraceElementclasse è stata creata per memorizzare informazioni su un elemento di traccia dello stack , ovvero un metodo nel stack trace.

Questa classe ha i seguenti metodi di istanza:

Metodo Descrizione
String getClassName()
Restituisce il nome della classe
String getMethodName()
Restituisce il nome del metodo
String getFileName()
Restituisce il nome del file (un file può contenere più classi)
int getLineNumber()
Restituisce il numero di riga nel file in cui è stato chiamato il metodo
String getModuleName()
Restituisce il nome del modulo (può essere null)
String getModuleVersion()
Restituisce la versione del modulo (può essere null)

Possono aiutarti a ottenere informazioni più complete sullo stack di chiamate corrente:

Codice Uscita console 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
nome classe nome
metodo nome
file
numero riga
nome
modulo versione

modulo
nome classe nome metodo
nome file
numero riga nome modulo versione modulo nome classe nome metodo nome file numero riga nome modulo versione modulo










3. Impila

Sai già cos'è una traccia dello stack , ma cos'è uno stack (classe Stack)?

Uno stack è una struttura di dati a cui è possibile aggiungere elementi e da cui è possibile recuperare elementi. Così facendo, puoi prendere solo gli elementi dalla fine: prendi prima l'ultimo aggiunto, poi il penultimo aggiunto, ecc.

La stessa pila di nomi suggerisce questo comportamento, come il modo in cui interagiresti con una pila di fogli. Se metti in pila i fogli 1, 2 e 3, devi recuperarli in ordine inverso: prima il terzo foglio, poi il secondo e solo dopo il primo.

Java ha anche una speciale classe di raccolta Stack con lo stesso nome e comportamento. Questa classe condivide molti comportamenti con ArrayListe LinkedList. Ma ha anche metodi che implementano il comportamento dello stack:

Metodi Descrizione
T push(T obj)
Aggiunge l' objelemento in cima allo stack
T pop()
Prende l'elemento dalla cima della pila (la profondità della pila diminuisce)
T peek()
Restituisce l'elemento in cima alla pila (la pila non cambia)
boolean empty()
Controlla se la raccolta è vuota
int search(Object obj)
Cerca un oggetto nella raccolta e lo restituisceindex

Esempio:

Codice Contenuto della pila (la parte superiore della pila è a destra)
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]

Gli stack sono usati abbastanza spesso nella programmazione. Quindi questa è una raccolta utile.



4. Visualizzazione di un'analisi dello stack durante la gestione delle eccezioni

Perché un elenco di chiamate di metodo è chiamato traccia dello stack ? Perché se pensi all'elenco dei metodi come a una pila di fogli di carta con i nomi dei metodi, quando chiami il metodo successivo, aggiungi un foglio con il nome di quel metodo alla pila. E il foglio di carta successivo va oltre, e così via.

Quando un metodo termina, il foglio in cima alla risma viene rimosso. Non è possibile rimuovere un foglio dal centro della risma senza rimuovere tutti i fogli sopra di esso. Analogamente, non è possibile terminare un metodo nel mezzo di una catena di chiamate senza terminare tutti i metodi che ha chiamato.

Eccezioni

Un altro uso interessante degli stack è durante la gestione delle eccezioni.

Quando si verifica un errore in un programma e viene generata un'eccezione , l'eccezione contiene l' analisi dello stack corrente , un array costituito da un elenco di metodi che iniziano, dal metodo principale e terminano con il metodo in cui si è verificato l'errore. C'è anche la linea in cui è stata lanciata l'eccezione!

Questa traccia dello stack è memorizzata all'interno dell'eccezione e può essere facilmente recuperata da essa utilizzando il seguente metodo:StackTraceElement[] getStackTrace()

Esempio:

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




Rileva l'eccezione

Ottieni l'analisi dello stack che esisteva quando si è verificato l'errore.

Questo è un metodo della Throwableclasse, quindi tutti i suoi discendenti (cioè tutte le eccezioni) hanno il getStackTrace()metodo. Super conveniente, eh?

Visualizza l'analisi dello stack dell'eccezione

A proposito, la Throwableclasse ha un altro metodo per lavorare con le tracce dello stack, un metodo che visualizza tutte le informazioni sulla traccia dello stack memorizzate all'interno dell'eccezione. Si chiama printStackTrace().

Abbastanza convenientemente, puoi chiamarlo su qualsiasi eccezione.

Esempio:

Codice
try
{
   // An exception may occur here
}
catch(Exception e)
{
   e.printStackTrace();
}
Uscita console
java.base/java.lang.Thread.getStackTrace(Thread.java:1606)
Main.test(Main.java:11)
Main.main(Main.java:5)