Как найти вызывающего метода, используя трассировку стека или отражение?

392

Мне нужно найти вызывающего метода. Возможно ли использовать Stacktrace или отражение?

Sathish
источник
5
Просто интересно, а зачем тебе это делать?
Джульетта
2
У меня есть родительский класс (модель MVC) с событием уведомителя, и только установщики моих подклассов вызывают этот метод. я не хочу засорять мой код избыточным аргументом. Я бы предпочел, чтобы метод в родительском классе определил установщик, который его вызвал.
Сатиш
30
@ Sathish Похоже, вы должны переосмыслить этот дизайн
krosenvold
7
@Juliet Как часть рефакторинга большого куска кода, недавно я изменил метод, который используется многими вещами. Есть определенный способ определить, правильно ли код использовал новый метод, поэтому я печатал класс и номер строки, которые вызывали его в этих случаях. Вне регистрации я не вижу никакой реальной цели для чего-то подобного. Хотя сейчас я хочу написать API-интерфейс, в котором DontNameYourMethodFooExceptionвызывается метод, называемый foo.
Cruncher
5
Я нахожу способным сделать так, чтобы вызывающий мой метод был бесценным инструментом отладки: именно так поиск в Интернете привел меня сюда. Если мой метод вызывается из нескольких мест, вызывается ли он из нужного места в нужное время? Как отмечает @Cruncher, полезность может быть, в лучшем случае, ограничена отладкой или ведением журнала.
Огр Псалом 33

Ответы:

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

Согласно Javadocs:

Последний элемент массива представляет нижнюю часть стека, что является наименее недавним вызовом метода в последовательности.

StackTraceElementИмеет getClassName(), getFileName(), getLineNumber()и getMethodName().

Вам придется поэкспериментировать, чтобы определить, какой индекс вы хотите (вероятно, stackTraceElements[1]или [2]).

Адам Пейнтер
источник
7
Я должен отметить, что getStackTrace () все еще создает исключение, так что это не очень быстро - просто более удобно.
Майкл Майерс
42
Обратите внимание, что этот метод не даст вам вызывающего, а только тип вызывающего . У вас не будет ссылки на объект, вызывающий ваш метод.
Йоахим Зауэр
3
Просто примечание, но для 1.5 JVM Thread.currentThread (). GetStackTrace () кажется намного медленнее, чем создание нового Exception () (примерно в 3 раза медленнее). Но, как уже отмечалось, вы все равно не должны использовать подобный код в критически важной для производительности области. ;) 1.6 JVM кажется только на ~ 10% медленнее и, как сказал Software Monkey, выражает намерение лучше, чем «новое исключение».
GaZ
21
@Eelco Thread.currentThread () - это дешево. Thread.getStackTrace () стоит дорого, потому что, в отличие от Throwable.fillInStackTrace (), нет гарантии, что метод вызывается тем же потоком, который он исследует, поэтому JVM должна создать «безопасную точку» - блокировку кучи и стека. См. Этот отчет об ошибке: bugs.sun.com/bugdatabase/view_bug.do?bug_id=6375302
Дэвид
7
@JoachimSauer Знаете ли вы, как получить ссылку на объект, вызывающий метод?
Джофд
216

Альтернативное решение можно найти в комментарии к этому запросу на улучшение . Он использует getClassContext()метод custom SecurityManagerи кажется быстрее, чем метод трассировки стека.

Следующая программа проверяет скорость различных предложенных методов (наиболее интересный бит находится во внутреннем классе 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.");
  }
}

Пример вывода с моего 2,4 ГГц Intel Core 2 Duo MacBook под управлением Java 1.6.0_17:

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

Метод внутреннего отражения намного быстрее, чем другие. Получение трассировки стека от вновь созданного Throwableбыстрее, чем получение от текущего Thread. И среди не внутренних способов поиска класса вызывающего абонента обычай SecurityManagerвыглядит самым быстрым.

Обновить

Как указывает lyomi в этом комментарии, этот sun.reflect.Reflection.getCallerClass()метод был отключен по умолчанию в Java 7 update 40 и полностью удален в Java 8. Подробнее об этом читайте в этой статье в базе данных ошибок Java .

Обновление 2

Как обнаружил zammbi , Oracle был вынужден отказаться от изменений, которые удалили sun.reflect.Reflection.getCallerClass(). Это все еще доступно в Java 8 (но это устарело).

Обновление 3

3 года спустя: обновление по времени с текущей JVM.

> 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
Да, похоже так. Но обратите внимание, что время, которое я привожу в этом примере, относится к миллиону звонков - поэтому в зависимости от того, как вы используете это, это может не быть проблемой.
Йохан Кавинг
1
Для меня удаление отражения из моего проекта привело к увеличению скорости в 10 раз.
Кевин Паркер
1
Да, отражение в целом медленное (см., Например, stackoverflow.com/questions/435553/java-reflection-performance ), но в данном конкретном случае использование внутреннего класса sun.reflect.Reflection является самым быстрым.
Йохан Кавинг
1
Это на самом деле не нужно. Вы можете проверить это, изменив код выше, чтобы напечатать возвращенное className (и я предлагаю уменьшить количество циклов до 1). Вы увидите, что все методы возвращают одно и то же className - TestGetCallerClassName.
Йохан Кавинг
1
getCallerClass устарела и будет удалена в 7u40 .. sad :(
lyomi
36

Похоже, вы пытаетесь избежать передачи ссылки thisна метод. Передача thisнамного лучше, чем поиск вызывающего по текущей трассировке стека. Рефакторинг к более оригинальному дизайну еще лучше. Вам не нужно знать звонящего. Передайте объект обратного вызова при необходимости.

Крейг П. Мотлин
источник
6
++ Знание вызывающего абонента - это слишком много информации. Если нужно, вы можете передать интерфейс, но есть большая вероятность, что потребуется серьезный рефакторинг. @satish должен опубликовать свой код и позволить нам повеселиться с ним :)
Билл К
15
Существуют веские причины для желания сделать это. У меня было несколько случаев, когда я находил это полезным, например, во время тестирования.
Eelco
2
@chillenious я знаю :) Я сделал это сам, чтобы создать метод, подобный тому, LoggerFactory.getLogger(MyClass.class)где мне не нужно было проходить в литерале класса. Это все еще редко правильная вещь.
Крейг П. Мотлин
6
Это хороший совет в целом, но он не отвечает на вопрос.
Навин
1
Конкретным примером того, когда может быть ПРАВИЛЬНОЕ проектное решение получить информацию о вызывающем абоненте, является реализация INotifyPropertyChangedинтерфейса .NET . Хотя этот конкретный пример отсутствует в Java, та же проблема может проявиться при попытке смоделировать поля / получатели как строки для Reflection.
Крис Керекес
31

Java 9 - JEP 259: API стека

JEP 259 предоставляет эффективный стандартный API для обхода стека, который позволяет легко фильтровать и легко получать доступ к информации в следах стека. До использования Stack-Walking API распространенными способами доступа к фреймам стека были:

Throwable::getStackTraceи Thread::getStackTraceвозвращает массив StackTraceElementобъектов, которые содержат имя класса и имя метода каждого элемента трассировки стека.

SecurityManager::getClassContextявляется защищенным методом, который позволяет SecurityManagerподклассу обращаться к контексту класса.

JDK-внутренний sun.reflect.Reflection::getCallerClassметод, который вы не должны использовать в любом случае

Использование этих API обычно неэффективно:

Эти API требуют, чтобы виртуальная машина охотно делала снимок всего стека , и они возвращают информацию, представляющую весь стек. Невозможно избежать затрат на проверку всех кадров, если вызывающий абонент интересуется только несколькими верхними кадрами в стеке.

Чтобы найти класс непосредственного абонента, сначала получите StackWalker:

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

Затем либо позвоните getCallerClass():

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

или walkв StackFrames и получить первый предшествующий StackFrame:

walker.walk(frames -> frames
      .map(StackWalker.StackFrame::getDeclaringClass)
      .skip(1)
      .findFirst());
Али Дехгани
источник
15

Oneliner :

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

Обратите внимание, что вам может понадобиться заменить 2 на 1.

Нир Дуан
источник
10

Этот метод делает то же самое, но немного проще и, возможно, немного более производительно, и, если вы используете отражение, он автоматически пропускает эти кадры. Единственная проблема заключается в том, что он может отсутствовать в JVM, отличных от Sun, хотя он включен в классы выполнения JRockit 1.4 -> 1.6. (Дело в том, что это не публичный класс).

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

Насколько это realFramesToSkipдолжно быть, в версиях Sun 1.5 и VM java.lang.System, есть метод с защитой пакетов, называемый getCallerClass (), который вызывает sun.reflect.Reflection.getCallerClass(3), но в своем вспомогательном служебном классе я использовал 4, поскольку есть добавленный фрейм вспомогательного класса. призывание.

Николас
источник
16
Использование классов реализации JVM - очень плохая идея.
Лоуренс Дол
7
Отметил. Я указал, что это не публичный класс, и защищенный метод getCallerClass () в java.lang.System присутствует на всех более чем 1,5 виртуальных машинах, на которые я смотрел, включая IBM, JRockit и Sun, но ваше утверждение консервативно обосновано. ,
Николас
6
@ Обезьяна программного обеспечения, как обычно, "все зависит". Выполнение чего-либо подобного для помощи в отладке или ведении журнала тестирования, особенно если оно никогда не заканчивается в рабочем коде, или если целью развертывания является исключительно компьютер разработчика, вероятно, будет хорошо. Любой, кто все еще думает иначе даже в таких случаях: вам нужно было бы на самом деле объяснить
8
Кроме того, по аналогичной логике вы также можете утверждать, что всякий раз, когда вы используете специфичную для Hibernate функцию, которая не совместима с JPA, это всегда « действительно плохая идея». Или, если вы собираетесь использовать специфичные для Oracle функции, которых нет в других базах данных, это « действительно плохая идея». Конечно, это безопасное мышление и, безусловно, полезный совет для определенных целей, но он автоматически выбрасывает полезные инструменты только потому, что он не будет работать с конфигурацией программного обеспечения, которую вы не используете вообще ? Это слишком негибко и немного глупо.
5
Неохраняемое использование классов, специфичных для поставщика, будет представлять более высокую вероятность возникновения проблем, но следует определить путь постепенного ухудшения качества, если рассматриваемый класс отсутствует (или по какой-либо причине запрещен). На мой взгляд, политика категорического отказа от использования классов, специфичных для конкретного поставщика, немного наивна. Изучите исходный код некоторых библиотек, которые вы используете в производстве, и посмотрите, сделает ли это какая-либо из них. (sun.misc. Возможно, небезопасно?)
Николас
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();
      }

Например, если вы пытаетесь получить строку вызывающего метода для целей отладки, вам нужно пройти класс Utility, в котором вы
кодируете эти статические методы: (старый код java1.4, просто чтобы проиллюстрировать потенциальное использование 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;
      }
VonC
источник
Мне нужно что-то, что работает с Java 1.4, и этот ответ был очень полезным! Спасибо!
RGO
6

Я делал это раньше. Вы можете просто создать новое исключение и получить трассировку стека, не выбрасывая его, а затем изучить трассировку стека. Как говорится в другом ответе, это чрезвычайно дорого - не делайте это в узком кругу.

Я делал это раньше для утилиты журналирования в приложении, где производительность не имела большого значения (вообще-то, производительность вообще не имеет большого значения - если вы отображаете результат для действия, например, быстрого нажатия кнопки).

Это было до того, как вы смогли получить трассировку стека, исключения имели только .printStackTrace (), поэтому мне пришлось перенаправить System.out в поток моего собственного создания, затем (new Exception ()). PrintStackTrace (); Перенаправьте System.out обратно и проанализируйте поток. Прикольные вещи.

Билл К
источник
Круто; ты не должен бросать это?
Кросенволд
Нет, по крайней мере, так я это помню, я не делал этого в течение нескольких лет, но я почти уверен, что создание исключения - это просто создание объекта, а создание исключения ничего с ним не делает, кроме pass это к предложению catch ().
Билл К
Ухоженная. Я был склонен бросить это, чтобы симулировать фактическое исключение.
Сатиш
Нет, поскольку в Java 5 есть метод Thread, позволяющий получить текущий стек в виде массива StackTraceElements; это все еще не дешево, но дешевле, чем старое решение для анализа исключений.
Лоуренс Дол
@ Обезьяна программного обеспечения Хотя я уверен, что это более уместно, что заставляет вас говорить, что это дешевле? Я бы предположил, что будет использоваться тот же механизм, и если нет, то зачем замедлять его, когда он делает то же самое?
Билл К
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() );
         }
      }
   }
AZ_
источник
0

Вот часть кода, который я сделал на основе подсказок, показанных в этой теме. Надеюсь, поможет.

(Не стесняйтесь делать какие-либо предложения по улучшению этого кода, пожалуйста, сообщите мне)

Счетчик:

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

    (...)
}

И объект:

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

    (...)



}
Pmt
источник
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);
    }
}

ИЛИ

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

используйте этот метод: -

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

Вызывающий метод примера кода здесь: -

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();
    }
}
Рейган Миранда
источник