繁体   English   中英

如何检测Java类是由其自己的main()调用还是从另一个类调用?

[英]How can I detect if a Java class is called by its own main() or from another class?

我有一个看起来像这样的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()调用还是由另一个类调用以有条件地设置变量?


我知道你们中的一些人说:“各种钟声响起!” 好吧,这是因为我遇到了一种情况:我正在设计一个类,需要将大量数据加载到PC的极限(4G Ram)中,并且我正在运行只能使用1.5G的Java 32位版本的Ram max; 因此,当我单独测试该类时,我需要加载尽可能多的数据以测试所有可能的情况,但是当从多个其他类中调用它时,它将无法执行此操作(将导致堆空间不足错误),因此只能加载最小 需要的数据。 但是,由于所有数据在启动时仅应加载一次,因此应将其保存在静态块中。 否则,我需要实现额外的逻辑来检测它是第一次加载(需要加载数据)还是第二次,第三次加载(不应该一次又一次地加载数据)。 而且,如果我实施额外的逻辑来做到这一点并将数据加载代码移出静态块,则会导致不必要的复杂性,因为如果我移至64位版本的Java(希望很快),那么额外的复杂性将是额外的负担(我将有足够的空间来加载所有数据,即使从其他类调用时也是如此。 因此,临时的快速修复方法是在静态块中检测出该异常并相应地处理差异,当我有足够的空间时,只需将它们注释掉,而无需更改编程逻辑结构。

感谢您提供所有答案和建议,我尝试了“ StackTraceElement”方法,效果很好! 它解决了我的问题。

只看实际的堆栈。 使用静态块的以下实现进行测试。 无论您是“执行” 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
}

我认为您绝对应该改变方法。

但是由于您在这里问的是具体的东西 (总结)。

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

ps我不知道为什么约瑟夫删除他的答案是正确的。

从技术上讲,您不能从其自己的main方法调用静态初始化程序,因为它总是 main方法之前运行:

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

因此,您正在尝试测试不可能的事情;-)

一个非常脏的解决方案将在静态初始化块中引发并捕获异常。 捕获异常时,使它将堆栈跟踪打印到ByteArrayOutputStream上,将字节数组转换为String并分析跟踪以查看是否从所需的主方法调用了静态初始化程序。

但是,这样做听起来像是不可思议的事情,必须避免采用更好的设计。

最好使用静态初始化程序加载数据。 也许另一种方法是使它成为单实例类(通过使用Singleton Pattern或仅通过在代码中确保只实例化一次)即可。 然后,您可以调用构造函数或带有标志的load方法来指示如何设置变量。

FWIW,我认为即使使用静态方法(再次可以向其传递标志)加载数据,虽然不是一个很好的解决方案,但也比使用静态初始化器更好。

我不知道是否有可能绝对地知道某个类是否正在被加载,是因为其自身的main()被调用,还是因为另一个类对其进行了引用-或只是决定加载它。 您可以调查堆栈跟踪,但这会导致代码很脆弱。 可能在一个JVM上运行但不在另一个JVM上运行的代码。 或在桌面应用程序中有效但在Applet或Web Start应用程序中无效的代码。 这不是一个好主意。

最好是重新考虑您的设计。 如果一个类需要知道是谁加载的,则该类正在做太多事情。 将部分因素分解到配置类中(或其他合适的方法),以便任何人加载类都可以提供正确的信息。

这是创建单例的一个可爱技巧:使用单值枚举。 JVM保证创建的实例不能超过一个。 在枚举的构造函数(不是静态初始化程序块)中,进行所需的任何数据库或文件系统访问,如果失败,则抛出适当的RuntimeException 枚举将在加载类时实例化,因此您不需要使用静态init块。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM