Como localizo o chamador de um método usando stacktrace ou reflexão?


392

Eu preciso encontrar o chamador de um método. É possível usar stacktrace ou reflexão?


5
Apenas imaginando, mas por que você precisaria fazer isso?
Julieta

2
Eu tenho uma classe Parent (modelo MVC) com um evento notificador e apenas setters das minhas subclasses chamam esse método. Eu não quero jogar meu código com um argumento redundante. Prefiro deixar o método na classe pai descobrir o setter que o chamou.
Sathish

30
Sons @Sathish como você deve re-pensar que o design
krosenvold

7
@ Juliet Como parte da refatoração de um grande bloco de código, recentemente mudei um método que é usado por muitas coisas. Existe uma certa maneira de detectar se o código estava usando o novo método corretamente, então eu estava imprimindo o número da classe e da linha que o chamava nesses casos. Fora do registro, não vejo nenhum objetivo real para algo assim. Embora eu queira escrever APIs agora que jogam um DontNameYourMethodFooExceptionse o método de chamada é nomeado foo.
Cruncher

5
Acho que é possível obter do chamador do meu método uma ferramenta de depuração inestimável: foi assim que uma pesquisa na web me trouxe aqui. Se meu método está sendo chamado de vários locais, está sendo chamado do local certo, na hora certa? Fora da depuração ou registro, a utilidade provavelmente é limitada, na melhor das hipóteses, como o @Cruncher menciona.
Ogre Psalm33

Respostas:


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

De acordo com os Javadocs:

O último elemento da matriz representa a parte inferior da pilha, que é a invocação de método menos recente na sequência.

Um StackTraceElementtem getClassName(), getFileName(), getLineNumber()e getMethodName().

Você precisará experimentar para determinar qual índice deseja (provavelmente stackTraceElements[1]ou [2]).


7
Devo observar que getStackTrace () ainda cria uma exceção, portanto isso não é realmente mais rápido - apenas mais conveniente.
Michael Myers

42
Observe que esse método não fornecerá o chamador, mas apenas o tipo de chamador . Você não terá uma referência ao objeto que chama seu método.
Joachim Sauer

3
Apenas uma observação, mas em uma 1.5 JVM Thread.currentThread (). GetStackTrace () parece ser muito mais lenta do que criar uma nova Exception () (aproximadamente três vezes mais lenta). Mas, como já observado, você não deve usar código como este em uma área crítica de desempenho de qualquer maneira. ;) Uma JVM de 1,6 parece ser apenas 10% mais lenta e, como disse o Software Monkey, expressa melhor a intenção do que a maneira da "nova exceção".
Gaz

21
@Eelco Thread.currentThread () é barato. Thread.getStackTrace () é caro porque, ao contrário de Throwable.fillInStackTrace (), não há garantia de que o método seja chamado pelo mesmo encadeamento que está examinando, portanto a JVM precisa criar um "ponto seguro" - bloqueando a pilha e a pilha. Consulte este relatório de bug: bugs.sun.com/bugdatabase/view_bug.do?bug_id=6375302
David Moles

7
@JoachimSauer, você conhece uma maneira de obter uma referência ao objeto que chama o método?
jophde

216

Uma solução alternativa pode ser encontrada em um comentário a esta solicitação de aprimoramento . Ele usa o getClassContext()método de um costume SecurityManagere parece ser mais rápido que o método de rastreamento de pilha.

O programa a seguir testa a velocidade dos diferentes métodos sugeridos (o bit mais interessante está na classe 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.");
  }
}

Um exemplo da saída do meu MacBook Intel Core 2 Duo de 2,4 GHz executando Java 1.6.0_17:

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

O método interno de reflexão é muito mais rápido que os outros. Obter um rastreamento de pilha de um recém-criado Throwableé mais rápido do que obtê-lo do atual Thread. E entre as formas não internas de encontrar a classe de quem chama, o costume SecurityManagerparece ser o mais rápido.

Atualizar

Como lyomi aponta neste comentário, o sun.reflect.Reflection.getCallerClass()método foi desativado por padrão na atualização 40 do Java 7 e removido completamente no Java 8. Leia mais sobre isso nesta edição no banco de dados de erros do Java .

Atualização 2

Como o zammbi descobriu, a Oracle foi forçada a desistir da mudança que removeu o sun.reflect.Reflection.getCallerClass(). Ele ainda está disponível no Java 8 (mas está obsoleto).

Atualização 3

3 anos depois: Atualização sobre o tempo com a JVM atual.

> 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.

5
Sim, parece que sim. Mas observe que o tempo que dou no exemplo é para um milhão de chamadas - portanto, dependendo de como você está usando isso, pode não ser um problema.
Johan Kaving

11
Para mim, remover a reflexão do meu projeto resultou em um aumento de velocidade de 10x.
Kevin Parker

11
Sim, a reflexão geralmente é lenta (consulte, por exemplo, stackoverflow.com/questions/435553/java-reflection-performance ), mas nesse caso específico, usando a classe interna sun.reflect.Reflection é a mais rápida.
Johan Kaving

11
Na verdade, não precisa. Você pode verificá-lo modificando o código acima para imprimir o className retornado (e sugiro reduzir a contagem de loop para 1). Você verá que todos os métodos retornam o mesmo className - TestGetCallerClassName.
Johan Kaving

11
getCallerClass é preterido e será removido em 7u40 .. triste :(
lyomi

36

Parece que você está tentando evitar passar uma referência para thiso método. Passar thisé muito melhor do que encontrar o chamador através do rastreamento de pilha atual. Refatorar para um design mais OO é ainda melhor. Você não precisa conhecer o chamador. Passe um objeto de retorno de chamada, se necessário.


6
++ Conhecer o chamador é muita informação. Se necessário, você pode transmitir uma interface, mas há uma boa chance de que seja necessária uma grande refatoração. @satish deve postar seu código e deixe-nos ter algum divertimento com ele :)
Bill K

15
Existem razões válidas para querer fazer isso. Eu tive algumas ocasiões em que achei útil durante os testes, por exemplo.
Eelco

2
@chillenious Eu sei :) Eu mesmo fiz isso para criar um método como o LoggerFactory.getLogger(MyClass.class)qual eu não precisei passar na classe literal. Ainda raramente é a coisa certa a fazer.
Craig P. Motlin 02/02/10

6
Este é um bom conselho em geral, mas não responde à pergunta.
Navin

11
Um exemplo concreto de quando pode ser a decisão de design RIGHT obter informações sobre o chamador é ao implementar a INotifyPropertyChangedinterface .NET . Embora este exemplo específico não esteja em Java, o mesmo problema pode se manifestar ao tentar modelar campos / getters como seqüências de caracteres para o Reflection.
precisa saber é o seguinte

31

Java 9 - JEP 259: API Stack-Walking

O JEP 259 fornece uma API padrão eficiente para movimentação de pilha que permite filtragem fácil e acesso lento às informações nos rastreamentos de pilha. Antes da API Stack-Walking, formas comuns de acessar quadros de pilha eram:

Throwable::getStackTracee Thread::getStackTraceretorne uma matriz de StackTraceElementobjetos, que contém o nome da classe e o nome do método de cada elemento de rastreamento de pilha.

SecurityManager::getClassContexté um método protegido, que permite que uma SecurityManagersubclasse acesse o contexto da classe.

sun.reflect.Reflection::getCallerClassMétodo interno do JDK que você não deve usar de qualquer maneira

O uso dessas APIs geralmente é ineficiente:

Essas APIs exigem que a VM capture um instantâneo de toda a pilha e retorne informações que representam a pilha inteira. Não há como evitar o custo de examinar todos os quadros se o chamador estiver interessado apenas nos poucos quadros superiores da pilha.

Para encontrar a classe do chamador imediato, primeiro obtenha um StackWalker:

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

Em seguida, ligue para getCallerClass():

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

ou walkos StackFramee obtenha o primeiro precedente StackFrame:

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

15

Oneliner :

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

Observe que pode ser necessário substituir o 2 por 1.


10

Esse método faz a mesma coisa, mas um pouco mais simples e possivelmente um pouco mais eficiente e, no caso de você estar usando reflexão, ele pula esses quadros automaticamente. O único problema é que ele pode não estar presente em JVMs que não são da Sun, embora esteja incluído nas classes de tempo de execução do JRockit 1.4 -> 1.6. (O ponto é que não é uma classe 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);

Quanto ao realFramesToSkipvalor, nas versões Sun 1.5 e 1.6 VM de java.lang.System, existe um método protegido por pacote chamado getCallerClass () que chama sun.reflect.Reflection.getCallerClass(3), mas, na minha classe de utilitário auxiliar, usei 4, pois há o quadro adicional da classe auxiliar invocação.


16
Uso de classes de implementação JVM é um realmente má idéia.
3206 Lawrence Dol

7
Notado. Especifiquei que não é uma classe pública e o método protegido getCallerClass () em java.lang.System está presente em todas as mais de 1,5 VMs que observei, incluindo IBM, JRockit e Sun, mas sua afirmação é conservadora .
Nicholas

6
@ Monkey Software, bem como de costume, "tudo depende". Fazer algo assim para ajudar na depuração ou no log de teste - especialmente se ele nunca terminar no código de produção - ou se o destino da implantação for estritamente o PC do desenvolvedor, provavelmente estará bem. Qualquer pessoa que ainda pensa de outra forma, mesmo em tais casos: você precisa realmente explicar o " muito má idéia" raciocinar melhor do que apenas dizendo que é ruim ...

8
Além disso, pela lógica semelhante, você também poderia argumentar que a qualquer momento você usar um recurso específico do Hibernate que não é JPA compatível, que é sempre um " muito má idéia". Ou se você estiver indo para usar recursos específicos do Oracle, que não estão disponíveis em outros bancos de dados, é uma " muito má idéia". Claro que é uma mentalidade mais segura e definitivamente um bom conselho para certos usos, mas descartando automaticamente ferramentas úteis apenas porque não funcionará com uma configuração de software que você não está usando ? Isso é um pouco inflexível e meio bobo.

5
O uso desprotegido de classes específicas do fornecedor apresentará uma maior probabilidade de problemas, mas deve-se determinar um caminho para degradar normalmente se a classe em questão não estiver presente (ou for proibida por algum motivo). Uma política de recusa direta em usar qualquer classe específica de fornecedor é, na minha opinião, um pouco ingênua. Examine o código-fonte de algumas das bibliotecas que você usa na produção e veja se alguma delas faz isso. (? sun.misc.Unsafe talvez)
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 exemplo, se você tentar obter a linha do método de chamada para fins de depuração, precisará passar pela classe Utility na qual codifica esses métodos estáticos:
(código java1.4 antigo, apenas para ilustrar um uso potencial 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;
      }

Eu precisava de algo que funcionasse com o Java 1.4 e essa resposta foi muito útil! Obrigado!
RGO 07/03

6

Eu já fiz isso antes. Você pode simplesmente criar uma nova exceção e pegar o rastreamento de pilha sem lançá-lo e, em seguida, examinar o rastreamento de pilha. Como a outra resposta diz, é extremamente caro - não faça isso em um loop apertado.

Eu já fiz isso antes para um utilitário de registro em um aplicativo em que o desempenho não importava muito (o desempenho raramente importa muito, na verdade - desde que você mostre o resultado de uma ação como clicar rapidamente em um botão).

Foi antes que você pudesse obter o rastreamento de pilha, as exceções tinham apenas .printStackTrace (), então eu tive que redirecionar System.out para um fluxo de minha própria criação, depois (new Exception ()). PrintStackTrace (); Redirecione System.out para trás e analise o fluxo. Coisas divertidas.


Legal; você não precisa jogá-lo?
krosenvold

Não, pelo menos é assim que me lembro, não faço isso há alguns anos, mas tenho certeza de que lançar uma exceção está apenas criando um objeto e lançá-la não faz nada a não ser passar para a cláusula catch ().
Bill Bill

Arrumado. Eu estava inclinado a jogá-lo para simular uma exceção real.
Sathish

Não, desde o Java 5, existe um método no Thread para obter a pilha atual como uma matriz de StackTraceElements; ainda não é barato, mas mais barato que a antiga solução de análise de exceção.
3206 Lawrence Dol

@ Software Monkey Embora eu tenha certeza de que é mais apropriado, o que faz você dizer que é mais barato? Eu diria que o mesmo mecanismo seria usado e, se não, por que tornar alguém mais lento quando faz a mesma coisa?
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

Aqui está uma parte do código que eu criei com base nas dicas mostradas neste tópico. Espero que ajude.

(Sinta-se à vontade para fazer sugestões para melhorar esse código, informe-me)

O 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);
    }

    (...)
}

E o 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);
    }
}

OU

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());

Exemplo de chamada do método Code is here: -

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();
    }
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.