¿Cómo encuentro la persona que llama de un método usando stacktrace o reflexión?


392

Necesito encontrar la persona que llama de un método. ¿Es posible usar stacktrace o reflexión?


55
Solo me preguntaba, pero ¿por qué necesitarías hacer esto?
Julieta el

2
Tengo una clase Parent (modelo MVC) con un evento notificador y solo los establecedores de mis subclases llaman a este método. No quiero ensuciar mi código con un argumento redundante. Prefiero dejar que el método en la clase principal descubra el setter que lo llamó.
Sathish

30
@Sathish Suena como si debieras repensar ese diseño
krosenvold

77
@Juliet Como parte de la refactorización de una gran cantidad de código, recientemente he cambiado un método que utilizan muchas cosas. Hay una cierta manera de detectar si el código estaba usando el nuevo método correctamente, por lo que estaba imprimiendo la clase y el número de línea que lo llamó en esos casos. Fuera del registro, no veo ningún propósito real para algo como esto. Aunque ahora quiero escribir API que arrojen un DontNameYourMethodFooExceptionsi el método de llamada se llama foo.
Cruncher

55
Encuentro que la llamada de mi método es una herramienta de depuración invaluable: así es como una búsqueda en la web me trajo aquí. Si mi método se llama desde varios lugares, ¿se llama desde la ubicación correcta en el momento correcto? Fuera de la depuración o el registro, la utilidad probablemente sea limitada en el mejor de los casos, como menciona @Cruncher.
Ogre Psalm33

Respuestas:


413
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace()

De acuerdo con los Javadocs:

El último elemento de la matriz representa la parte inferior de la pila, que es la invocación de método menos reciente en la secuencia.

Una StackTraceElementtiene getClassName(), getFileName(), getLineNumber()y getMethodName().

Tendrá que experimentar para determinar qué índice desea (probablemente stackTraceElements[1]o [2]).


77
Debo señalar que getStackTrace () todavía crea una excepción, por lo que esto no es realmente más rápido, solo más conveniente.
Michael Myers

42
Tenga en cuenta que este método no le dará la persona que llama, sino solo el tipo de la persona que llama . No tendrá una referencia al objeto que llama a su método.
Joachim Sauer

3
Solo una nota al margen, pero en un 1.5 JVM Thread.currentThread (). GetStackTrace () parece ser mucho más lento que crear una nueva Excepción () (aproximadamente 3 veces más lenta). Pero como ya se señaló, de todos modos no debería usar un código como este en un área crítica para el rendimiento. ;) Un 1.6 JVM solo parece ser ~ 10% más lento y, como dijo Software Monkey, expresa la intención mejor que la forma de "nueva excepción".
GaZ

21
@Eelco Thread.currentThread () es barato. Thread.getStackTrace () es costoso porque, a diferencia de Throwable.fillInStackTrace (), no hay garantía de que el mismo hilo esté examinando el método, por lo que la JVM tiene que crear un "punto seguro": bloquear el montón y la pila. Vea este informe de error: bugs.sun.com/bugdatabase/view_bug.do?bug_id=6375302
David Moles

77
@JoachimSauer, ¿conoce alguna forma de obtener una referencia al objeto que llama al método?
jophde

216

Se puede encontrar una solución alternativa en un comentario a esta solicitud de mejora . Utiliza el getClassContext()método personalizado SecurityManagery parece ser más rápido que el método de seguimiento de la pila.

El siguiente programa prueba la velocidad de los diferentes métodos sugeridos (el bit más interesante está en la clase interna SecurityManagerMethod):

/**
 * Test the speed of various methods for getting the caller class name
 */
public class TestGetCallerClassName {

  /**
   * Abstract class for testing different methods of getting the caller class name
   */
  private static abstract class GetCallerClassNameMethod {
      public abstract String getCallerClassName(int callStackDepth);
      public abstract String getMethodName();
  }

  /**
   * Uses the internal Reflection class
   */
  private static class ReflectionMethod extends GetCallerClassNameMethod {
      public String getCallerClassName(int callStackDepth) {
          return sun.reflect.Reflection.getCallerClass(callStackDepth).getName();
      }

      public String getMethodName() {
          return "Reflection";
      }
  }

  /**
   * Get a stack trace from the current thread
   */
  private static class ThreadStackTraceMethod extends GetCallerClassNameMethod {
      public String  getCallerClassName(int callStackDepth) {
          return Thread.currentThread().getStackTrace()[callStackDepth].getClassName();
      }

      public String getMethodName() {
          return "Current Thread StackTrace";
      }
  }

  /**
   * Get a stack trace from a new Throwable
   */
  private static class ThrowableStackTraceMethod extends GetCallerClassNameMethod {

      public String getCallerClassName(int callStackDepth) {
          return new Throwable().getStackTrace()[callStackDepth].getClassName();
      }

      public String getMethodName() {
          return "Throwable StackTrace";
      }
  }

  /**
   * Use the SecurityManager.getClassContext()
   */
  private static class SecurityManagerMethod extends GetCallerClassNameMethod {
      public String  getCallerClassName(int callStackDepth) {
          return mySecurityManager.getCallerClassName(callStackDepth);
      }

      public String getMethodName() {
          return "SecurityManager";
      }

      /** 
       * A custom security manager that exposes the getClassContext() information
       */
      static class MySecurityManager extends SecurityManager {
          public String getCallerClassName(int callStackDepth) {
              return getClassContext()[callStackDepth].getName();
          }
      }

      private final static MySecurityManager mySecurityManager =
          new MySecurityManager();
  }

  /**
   * Test all four methods
   */
  public static void main(String[] args) {
      testMethod(new ReflectionMethod());
      testMethod(new ThreadStackTraceMethod());
      testMethod(new ThrowableStackTraceMethod());
      testMethod(new SecurityManagerMethod());
  }

  private static void testMethod(GetCallerClassNameMethod method) {
      long startTime = System.nanoTime();
      String className = null;
      for (int i = 0; i < 1000000; i++) {
          className = method.getCallerClassName(2);
      }
      printElapsedTime(method.getMethodName(), startTime);
  }

  private static void printElapsedTime(String title, long startTime) {
      System.out.println(title + ": " + ((double)(System.nanoTime() - startTime))/1000000 + " ms.");
  }
}

Un ejemplo de la salida de mi MacBook Intel Core 2 Duo de 2.4 GHz con Java 1.6.0_17:

Reflection: 10.195 ms.
Current Thread StackTrace: 5886.964 ms.
Throwable StackTrace: 4700.073 ms.
SecurityManager: 1046.804 ms.

El método interno de reflexión es mucho más rápido que los demás. Obtener un seguimiento de pila de un recién creado Throwablees más rápido que obtenerlo de la actual Thread. Y entre las formas no internas de encontrar la clase de llamada, la costumbre SecurityManagerparece ser la más rápida.

Actualizar

Como Lyomi señala en este comentario, el sun.reflect.Reflection.getCallerClass()método se ha deshabilitado de forma predeterminada en la actualización 40 de Java 7 y se eliminó por completo en Java 8. Lea más sobre esto en este tema en la base de datos de errores de Java .

Actualización 2

Como descubrió zammbi , Oracle se vio obligado a retirarse del cambio que eliminó el sun.reflect.Reflection.getCallerClass(). Todavía está disponible en Java 8 (pero está en desuso).

Actualización 3

3 años después: Actualización sobre el tiempo con JVM actual.

> java -version
java version "1.8.0"
Java(TM) SE Runtime Environment (build 1.8.0-b132)
Java HotSpot(TM) 64-Bit Server VM (build 25.0-b70, mixed mode)
> java TestGetCallerClassName
Reflection: 0.194s.
Current Thread StackTrace: 3.887s.
Throwable StackTrace: 3.173s.
SecurityManager: 0.565s.

55
Sí, eso parece. Pero tenga en cuenta que los tiempos que doy en el ejemplo son para un millón de llamadas, por lo que, dependiendo de cómo esté usando esto, podría no ser un problema.
Johan Kaving

1
Para mí, eliminar el reflejo de mi proyecto resultó en un aumento de velocidad de 10x.
Kevin Parker

1
Sí, la reflexión en general es lenta (ver, por ejemplo, stackoverflow.com/questions/435553/java-reflection-performance ), pero en este caso específico, usar la clase interna sun.reflect.Reflection es la más rápida.
Johan Kaving

1
En realidad no necesita hacerlo. Puede verificarlo modificando el código anterior para imprimir el className devuelto (y sugiero reducir el recuento de bucles a 1). Verá que todos los métodos devuelven el mismo className - TestGetCallerClassName.
Johan Kaving

1
getCallerClass está en desuso y se eliminará en 7u40 .. triste :(
lyomi

36

Parece que estás tratando de evitar pasar una referencia al thismétodo. Pasar thises mucho mejor que encontrar a la persona que llama a través del seguimiento de la pila actual. Refactorizar a un diseño más OO es aún mejor. No deberías necesitar conocer a la persona que llama. Pase un objeto de devolución de llamada si es necesario.


66
++ Conocer a la persona que llama es demasiada información. Si debe hacerlo, puede pasar una interfaz, pero existe una buena posibilidad de que se necesite una refactorización importante. @satish debe publicar su código y vamos a tener un buen rato con él :)
Bill K

15
Existen razones válidas para querer hacer esto. He tenido algunas ocasiones en las que me pareció útil durante las pruebas, por ejemplo.
Eelco

2
@chillenious Lo sé :) Lo hice yo mismo para crear un método como LoggerFactory.getLogger(MyClass.class)donde no tuve que pasar el literal de la clase. Rara vez es lo correcto.
Craig P. Motlin

66
Este es un buen consejo en general, pero no responde la pregunta.
Navin

1
Un ejemplo concreto de cuándo podría ser la decisión de diseño CORRECTA obtener información sobre la persona que llama es cuando se implementa la INotifyPropertyChangedinterfaz .NET . Si bien este ejemplo específico no está en Java, el mismo problema puede manifestarse cuando se intenta modelar campos / captadores como cadenas para Reflection.
Chris Kerekes

31

Java 9 - JEP 259: API para caminar sobre la pila

JEP 259 proporciona una API estándar eficiente para el recorrido de la pila que permite un filtrado fácil y un acceso lento a la información en los seguimientos de la pila. Antes de Stack-Walking API, las formas comunes de acceder a los marcos de pila eran:

Throwable::getStackTracey Thread::getStackTracedevolver una matriz de StackTraceElementobjetos, que contienen el nombre de la clase y el nombre del método de cada elemento de seguimiento de pila.

SecurityManager::getClassContextes un método protegido, que permite que una SecurityManagersubclase acceda al contexto de la clase.

sun.reflect.Reflection::getCallerClassMétodo interno JDK que no debe usar de todos modos

El uso de estas API suele ser ineficiente:

Estas API requieren que la máquina virtual capture con entusiasmo una instantánea de toda la pila , y devuelven información que representa la pila completa. No hay forma de evitar el costo de examinar todos los fotogramas si la persona que llama solo está interesada en los primeros fotogramas de la pila.

Para encontrar la clase de la persona que llama inmediatamente, primero obtenga un StackWalker:

StackWalker walker = StackWalker
                           .getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);

Luego, llame al getCallerClass():

Class<?> callerClass = walker.getCallerClass();

o walkel StackFramesy obtener el primer precedente StackFrame:

walker.walk(frames -> frames
      .map(StackWalker.StackFrame::getDeclaringClass)
      .skip(1)
      .findFirst());

15

Oneliner :

Thread.currentThread().getStackTrace()[2].getMethodName()

Tenga en cuenta que es posible que deba reemplazar el 2 con 1.


10

Este método hace lo mismo, pero un poco más simple y posiblemente un poco más eficaz y, en caso de que esté utilizando la reflexión, omite esos cuadros automáticamente. El único problema es que puede no estar presente en JVM que no sean Sun, aunque está incluido en las clases de tiempo de ejecución de JRockit 1.4 -> 1.6. (El punto es que no es una clase pública ).

sun.reflect.Reflection

    /** Returns the class of the method <code>realFramesToSkip</code>
        frames up the stack (zero-based), ignoring frames associated
        with java.lang.reflect.Method.invoke() and its implementation.
        The first frame is that associated with this method, so
        <code>getCallerClass(0)</code> returns the Class object for
        sun.reflect.Reflection. Frames associated with
        java.lang.reflect.Method.invoke() and its implementation are
        completely ignored and do not count toward the number of "real"
        frames skipped. */
    public static native Class getCallerClass(int realFramesToSkip);

En cuanto a cuál realFramesToSkipdebería ser el valor, las versiones de VM de Sun 1.5 y 1.6 java.lang.System, hay un método de paquete protegido llamado getCallerClass () que llama sun.reflect.Reflection.getCallerClass(3), pero en mi clase de utilidad auxiliar utilicé 4 ya que existe el marco agregado de la clase auxiliar invocación.


16
El uso de clases de implementación de JVM es una muy mala idea.
Lawrence Dol

77
Célebre. Especifiqué que no es una clase pública, y el método protegido getCallerClass () en java.lang.System está presente en todas las máquinas virtuales 1.5+ que he visto, incluidos IBM, JRockit y Sun, pero su afirmación es conservadoramente sólida .
Nicholas

66
@Software Monkey, como de costumbre, "todo depende". Hacer algo como esto para ayudar a depurar o probar el registro, especialmente si nunca termina en código de producción, o si el objetivo de implementación es estrictamente la PC del desarrollador, probablemente estará bien. Cualquier persona que todavía piensa de otro modo, incluso en estos casos: que había necesidad de explicar la realidad " muy mala idea" razonar mejor que simplemente diciendo que es malo ...

8
Además, con una lógica similar, también podría argumentar que cada vez que utiliza una función específica de Hibernate que no es compatible con JPA, esa siempre es una " muy mala idea". O si va a utilizar características específicas de Oracle que no están disponibles en otras bases de datos, es una " muy mala idea". Claro que es una mentalidad más segura y definitivamente un buen consejo para ciertos usos, pero desechando automáticamente herramientas útiles solo porque no funcionará con una configuración de software que, uh ... ¿ no estás usando en absoluto ? Eso es un poco inflexible y un poco tonto.

55
El uso sin protección de clases específicas del proveedor presentará una mayor probabilidad de problemas, pero uno debe determinar una ruta para degradarse con gracia si la clase en cuestión no está presente (o está prohibida por alguna razón). Una política de rechazo rotundo a usar clases específicas de proveedores es, en mi opinión, un poco ingenua. Mire el código fuente de algunas de las bibliotecas que usa en producción y vea si alguna de ellas lo hace. (sun.misc. ¿Inseguro quizás?)
Nicholas

7
     /**
       * Get the method name for a depth in call stack. <br />
       * Utility function
       * @param depth depth in the call stack (0 means current method, 1 means call method, ...)
       * @return method name
       */
      public static String getMethodName(final int depth)
      {
        final StackTraceElement[] ste = new Throwable().getStackTrace();

        //System. out.println(ste[ste.length-depth].getClassName()+"#"+ste[ste.length-depth].getMethodName());
        return ste[ste.length - depth].getMethodName();
      }

Por ejemplo, si intenta obtener la línea del método de llamada para fines de depuración, debe pasar la clase de utilidad en la que codifica esos métodos estáticos:
(antiguo código java1.4, solo para ilustrar un posible uso de StackTraceElement)

        /**
          * Returns the first "[class#method(line)]: " of the first class not equal to "StackTraceUtils". <br />
          * From the Stack Trace.
          * @return "[class#method(line)]: " (never empty, first class past StackTraceUtils)
          */
        public static String getClassMethodLine()
        {
            return getClassMethodLine(null);
        }

        /**
          * Returns the first "[class#method(line)]: " of the first class not equal to "StackTraceUtils" and aclass. <br />
          * Allows to get past a certain class.
          * @param aclass class to get pass in the stack trace. If null, only try to get past StackTraceUtils. 
          * @return "[class#method(line)]: " (never empty, because if aclass is not found, returns first class past StackTraceUtils)
          */
        public static String getClassMethodLine(final Class aclass)
        {
            final StackTraceElement st = getCallingStackTraceElement(aclass);
            final String amsg = "[" + st.getClassName() + "#" + st.getMethodName() + "(" + st.getLineNumber()
            +")] <" + Thread.currentThread().getName() + ">: ";
            return amsg;
        }

     /**
       * Returns the first stack trace element of the first class not equal to "StackTraceUtils" or "LogUtils" and aClass. <br />
       * Stored in array of the callstack. <br />
       * Allows to get past a certain class.
       * @param aclass class to get pass in the stack trace. If null, only try to get past StackTraceUtils. 
       * @return stackTraceElement (never null, because if aClass is not found, returns first class past StackTraceUtils)
       * @throws AssertionFailedException if resulting statckTrace is null (RuntimeException)
       */
      public static StackTraceElement getCallingStackTraceElement(final Class aclass)
      {
        final Throwable           t         = new Throwable();
        final StackTraceElement[] ste       = t.getStackTrace();
        int index = 1;
        final int limit = ste.length;
        StackTraceElement   st        = ste[index];
        String              className = st.getClassName();
        boolean aclassfound = false;
        if(aclass == null)
        {
            aclassfound = true;
        }
        StackTraceElement   resst = null;
        while(index < limit)
        {
            if(shouldExamine(className, aclass) == true)
            {
                if(resst == null)
                {
                    resst = st;
                }
                if(aclassfound == true)
                {
                    final StackTraceElement ast = onClassfound(aclass, className, st);
                    if(ast != null)
                    {
                        resst = ast;
                        break;
                    }
                }
                else
                {
                    if(aclass != null && aclass.getName().equals(className) == true)
                    {
                        aclassfound = true;
                    }
                }
            }
            index = index + 1;
            st        = ste[index];
            className = st.getClassName();
        }
        if(resst == null) 
        {
            //Assert.isNotNull(resst, "stack trace should null"); //NO OTHERWISE circular dependencies 
            throw new AssertionFailedException(StackTraceUtils.getClassMethodLine() + " null argument:" + "stack trace should null"); //$NON-NLS-1$
        }
        return resst;
      }

      static private boolean shouldExamine(String className, Class aclass)
      {
          final boolean res = StackTraceUtils.class.getName().equals(className) == false && (className.endsWith("LogUtils"
            ) == false || (aclass !=null && aclass.getName().endsWith("LogUtils")));
          return res;
      }

      static private StackTraceElement onClassfound(Class aclass, String className, StackTraceElement st)
      {
          StackTraceElement   resst = null;
          if(aclass != null && aclass.getName().equals(className) == false)
          {
              resst = st;
          }
          if(aclass == null)
          {
              resst = st;
          }
          return resst;
      }

¡Necesitaba algo que funcionara con Java 1.4 y esta respuesta fue muy útil! ¡Gracias!
RGO

6

He hecho esto antes. Puede crear una nueva excepción y tomar el seguimiento de la pila sin lanzarla, luego examinar el seguimiento de la pila. Sin embargo, como dice la otra respuesta, es extremadamente costoso, no lo hagas en un círculo cerrado.

Lo he hecho antes para una utilidad de registro en una aplicación donde el rendimiento no importaba mucho (el rendimiento rara vez importa mucho, en realidad, siempre que muestre el resultado de una acción, como hacer clic rápidamente en un botón).

Antes de que pudiera obtener el seguimiento de la pila, las excepciones solo tenían .printStackTrace (), así que tuve que redirigir System.out a una secuencia de mi propia creación, luego (nueva Excepción ()). PrintStackTrace (); Redirigir System.out hacia atrás y analizar la secuencia. Cosas divertidas.


Frio; no tienes que tirarlo?
krosenvold

No, al menos así es como lo recuerdo, no lo he hecho en algunos años, pero estoy bastante seguro de que renovar una excepción es solo crear un objeto, y lanzar la excepción no hace nada más que pasar a la cláusula catch ().
Bill K

Ordenado. Estaba inclinado a tirarlo para simular una excepción real.
Sathish el

No, desde Java 5 hay un método en Thread para obtener la pila actual como una matriz de StackTraceElements; todavía no es barato, pero es más barato que la antigua solución de análisis de excepciones.
Lawrence Dol

@Software Monkey Aunque estoy seguro de que es más apropiado, ¿qué te hace decir que es más barato? Supongo que se usaría el mismo mecanismo, y si no, ¿por qué hacerlo más lento cuando hace lo mismo?
Bill K

1
private void parseExceptionContents(
      final Exception exception,
      final OutputStream out)
   {
      final StackTraceElement[] stackTrace = exception.getStackTrace();
      int index = 0;
      for (StackTraceElement element : stackTrace)
      {
         final String exceptionMsg =
              "Exception thrown from " + element.getMethodName()
            + " in class " + element.getClassName() + " [on line number "
            + element.getLineNumber() + " of file " + element.getFileName() + "]";
         try
         {
            out.write((headerLine + newLine).getBytes());
            out.write((headerTitlePortion + index++ + newLine).getBytes() );
            out.write((headerLine + newLine).getBytes());
            out.write((exceptionMsg + newLine + newLine).getBytes());
            out.write(
               ("Exception.toString: " + element.toString() + newLine).getBytes());
         }
         catch (IOException ioEx)
         {
            System.err.println(
                 "IOException encountered while trying to write "
               + "StackTraceElement data to provided OutputStream.\n"
               + ioEx.getMessage() );
         }
      }
   }

0

Aquí hay una parte del código que hice en base a las sugerencias que se muestran en este tema. Espero eso ayude.

(No dude en hacer sugerencias para mejorar este código, por favor dígame)

El contador:

public class InstanceCount{
    private static Map<Integer, CounterInstanceLog> instanceMap = new HashMap<Integer, CounterInstanceLog>();
private CounterInstanceLog counterInstanceLog;


    public void count() {
        counterInstanceLog= new counterInstanceLog();
    if(counterInstanceLog.getIdHashCode() != 0){
    try {
        if (instanceMap .containsKey(counterInstanceLog.getIdHashCode())) {
         counterInstanceLog= instanceMap .get(counterInstanceLog.getIdHashCode());
    }

    counterInstanceLog.incrementCounter();

            instanceMap .put(counterInstanceLog.getIdHashCode(), counterInstanceLog);
    }

    (...)
}

Y el objeto:

public class CounterInstanceLog{
    private int idHashCode;
    private StackTraceElement[] arrayStackTraceElements;
    private int instanceCount;
    private String callerClassName;

    private StackTraceElement getProjectClasses(int depth) {
      if(depth< 10){
        getCallerClassName(sun.reflect.Reflection.getCallerClass(depth).getName());
        if(getCallerClassName().startsWith("com.yourproject.model")){
            setStackTraceElements(Thread.currentThread().getStackTrace());
            setIdHashCode();
        return arrayStackTraceElements[depth];
        }
        //+2 because one new item are added to the stackflow
        return getProjectClasses(profundidade+2);           
      }else{
        return null;
      }
    }

    private void setIdHashCode() {
        if(getNomeClasse() != null){
            this.idHashCode = (getCallerClassName()).hashCode();
        }
    }

    public void incrementaContador() {
    this.instanceCount++;
}

    //getters and setters

    (...)



}

0
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintWriter;

class DBConnection {
    String createdBy = null;

    DBConnection(Throwable whoCreatedMe) {
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        PrintWriter pw = new PrintWriter(os);
        whoCreatedMe.printStackTrace(pw);
        try {
            createdBy = os.toString();
            pw.close();
            os.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

public class ThrowableTest {

    public static void main(String[] args) {

        Throwable createdBy = new Throwable(
                "Connection created from DBConnectionManager");
        DBConnection conn = new DBConnection(createdBy);
        System.out.println(conn.createdBy);
    }
}

O

public static interface ICallback<T> { T doOperation(); }


public class TestCallerOfMethod {

    public static <T> T callTwo(final ICallback<T> c){
        // Pass the object created at callee to the caller
        // From the passed object we can get; what is the callee name like below.
        System.out.println(c.getClass().getEnclosingMethod().getName());
        return c.doOperation();
    }

    public static boolean callOne(){
        ICallback callBackInstance = new ICallback(Boolean){
            @Override
            public Boolean doOperation() 
            {
                return true;
            }
        };
        return callTwo(callBackInstance);
    }

    public static void main(String[] args) {
         callOne();
    }
}

0

use este método:

 StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace();
 stackTraceElement e = stacktrace[2];//maybe this number needs to be corrected
 System.out.println(e.getMethodName());

El que llama del ejemplo de método Code está aquí: -

public class TestString {

    public static void main(String[] args) {
        TestString testString = new TestString();
        testString.doit1();
        testString.doit2();
        testString.doit3();
        testString.doit4();
    }

    public void doit() {
        StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace();
        StackTraceElement e = stacktrace[2];//maybe this number needs to be corrected
        System.out.println(e.getMethodName());
    }

    public void doit1() {
        doit();
    }

    public void doit2() {
        doit();
    }

    public void doit3() {
        doit();
    }

    public void doit4() {
        doit();
    }
}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.