Как я могу определить, вызывается ли Java-класс своим собственным main () или из другого класса? - PullRequest
1 голос
/ 08 июня 2009

У меня есть класс Java, который выглядит следующим образом:

public class My_ABC
{
  int a=0;
  boolean B=true;

  static  // Initialize and load existing data only once at start-up
  {
     // need to know if it's called from its own main()
     // or by another class to conditionally set veriables
  }

  public My_ABC(int AA,boolean BB)
  {

  }

  public static void main(String[] args) 
  {
    My_ABC my_abc = new My_ABC(3,true);
  }
}

Поскольку статический блок запускается при загрузке класса, как я могу определить, вызывается ли он из своего собственного main() или из другого класса для условно установленных переменных?


Я понимаю, когда некоторые из вас говорили: "Раздаются все виды колоколов!" Ну, это потому, что у меня возникла ситуация: я разрабатываю класс, который должен загружать много данных до предела моего ПК (4G Ram), и я использую 32-битную версию Java, которая может использовать только 1.5G Ram Max; поэтому, когда я тестирую этот класс сам по себе, мне нужно загрузить как можно больше данных, чтобы протестировать все возможные ситуации, но когда он вызывается из нескольких других классов, он не может этого сделать (приведет к ошибке из-за недостатка пространства в куче), поэтому он должен загружать только мин. данные необходимы. И все же, поскольку все данные должны загружаться только один раз при запуске, они должны находиться в статическом блоке; в противном случае мне нужно реализовать дополнительную логику, чтобы определить, загружается ли он в первый раз (нужно загружать данные) или во 2-й, 3-й раз (не следует загружать данные снова и снова). И если я реализую дополнительную логику для этого и переместу код загрузки данных из статического блока, это вызовет ненужную сложность, потому что если я перейду на 64-битную версию Java (надеюсь, скоро), эта дополнительная сложность будет дополнительной нагрузкой У меня будет достаточно места для загрузки всех данных даже при вызове из других классов). Поэтому временное быстрое решение состоит в том, чтобы обнаружить его в статическом блоке и соответственно обработать разницу, когда у меня будет достаточно места, просто закомментируйте их без необходимости изменения логической структуры программирования.

Спасибо за все ответы и советы, я попробовал подход "StackTraceElement", он прекрасно работает! Это решило мою проблему.

Ответы [ 6 ]

3 голосов
/ 08 июня 2009

Я думаю, что вы определенно должны изменить свой подход.

Но так как вы спросили что-то конкретное , вот оно (обобщение от других).

public class X { 
    static { 
        System.out.println("Static free block");
        StackTraceElement [] st  = new RuntimeException("").getStackTrace();
        if( st.length == 1 ) {
            System.out.println("Invoked from main");
        } else { 
            System.out.println("Invoked from somewhere else");
        }
    }
    public static void main( String [] args ) { 
        System.out.println("Main");
    }
}

Используйте это, чтобы проверить это:

public class Y  { 
    public static void main( String [] args ) { 
        X x = new X();
    }
}

p.s. Я не знаю, почему Джозеф удалил свой ответ, он был на правильном пути.

3 голосов
/ 08 июня 2009

Просто посмотрите на реальный стек. Протестируйте со следующей реализацией вашего статического блока. Распечатка будет отличаться, независимо от того, «вы» выполнили класс My_ABC или класс был загружен позже:

static // Initialize and load existing data only once at start-up
{
    StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
    for (StackTraceElement el : stackTrace) {
        System.out.println(el);
    }
    // in real life you wouldn't print but use the stackTrace array
    // to identify why the class has been loaded and do your initialisation
}
2 голосов
/ 08 июня 2009

ОЧЕНЬ Грязное решение будет вызывать и перехватывать исключение в статическом блоке инициализации. При отлове исключения заставьте его напечатать трассировку стека в ByteArrayOutputStream, преобразовать массив байтов в String и проанализировать трассировку, чтобы узнать, был ли вызван статический инициализатор из ожидаемого вами основного метода.

Однако, это звучит как черная магия, и ее следует избегать в пользу лучших дизайнов. , ,

2 голосов
/ 08 июня 2009

Технически ваш статический инициализатор не может быть вызван из своего собственного основного метода, потому что он всегда будет запускаться до метода main:

// output: 'foobar'
public class Foobar {
    static { System.out.print("foo"); }
    public static void main(String[] args) { System.out.print("bar"); }
}

Итак, вы пытаетесь проверить невозможное; -)

1 голос
/ 08 июня 2009

Использование статических инициализаторов для загрузки данных в лучшем случае рискованно. Возможно, альтернативный подход состоит в том, чтобы сделать этот класс единичным экземпляром (либо с помощью Singleton Pattern , либо просто путем обеспечения в вашем коде, что он создается только один раз). Затем вы можете вызвать конструктор или метод загрузки с флагом, чтобы указать, как установить переменные.

FWIW, я думаю, что даже загрузка данных с использованием статического метода (которому, опять же, вы можете передать флаг), хотя и не является хорошим решением, была бы предпочтительнее использования статического инициализатора.

0 голосов
/ 08 июня 2009

Я не знаю, возможно ли абсолютно узнать, загружается ли класс, потому что вызывается его собственный main(), или потому что другой класс сделал ссылку на него - или просто решил загрузить его. Вы можете исследовать трассировку стека, но это приведет к очень хрупкому коду. Код, который может работать на одной JVM, но не на другой. Или код, который работает в настольном приложении, но не в приложении Applet или Web Start. Не очень хорошая идея.

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

Вот милый трюк для создания синглтона: используйте однозначное перечисление. JVM гарантирует, что не может быть создано более одного экземпляра. В конструкторе enum (не в статическом блоке инициализатора) сделайте любой доступ к базе данных или файловой системе, который вам необходим, выбрасывая соответствующие RuntimeException в случае необходимости. Enum будет создан при загрузке класса, поэтому вам не нужно использовать статический блок инициализации.

...