如何检测Java类是否由其自己的main()或其他类调用?

时间:2009-06-08 17:14:04

标签: java static initialization

我有一个看起来像这样的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)的类,而我正在运行32位版本的Java,它只能使用1.5G Ram max;所以当我自己测试这个类时,我需要加载尽可能多的数据来测试所有可能的情况,但是当从多个其他类调用它时,它不能这样做(会导致堆空间错误),所以它应该只加载最小。需要的数据。然而,由于所有数据只应在启动时加载一次,因此它应该在静态块中;否则我需要实现额外的逻辑来检测它是第一次加载(需要加载数据),还是第二次,第三次加载(不应该反复加载数据)。如果我实现额外的逻辑来执行此操作并将数据加载代码移出静态块,则会导致不必要的复杂性,因为如果我转移到64位版本的java(希望很快),那额外的复杂性将是额外的负担(即使从其他类调用,我也有足够的空间来加载所有数据。因此,临时快速修复是在静态块中检测它并相应地处理差异,当我有足够的空间时,只需将它们注释掉而无需更改编程逻辑结构。

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

6 个答案:

答案 0 :(得分:3)

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

答案 1 :(得分:3)

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

但是既然你asked something concrete在这里(总结来自其他人)。

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。 我不知道为什么约瑟夫删除了他的回答它是在正确的轨道。

答案 2 :(得分:2)

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

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

所以你试图测试不可能的事情; - )

答案 3 :(得分:2)

一个非常糟糕的解决方案是在静态初始化块中抛出并捕获异常。捕获异常时,使其将堆栈跟踪打印到ByteArrayOutputStream,将字节数组转换为String并解析跟踪以查看是否从您期望的main方法调用静态初始化程序。

然而,这样做听起来像是黑魔法,必须避免使用更好的设计。 。 。

答案 4 :(得分:1)

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

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

答案 5 :(得分:0)

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

最好是重新考虑你的设计。如果一个类需要知道谁加载了它,那么这个类就做了太多事情。分配到配置类(或其他任何适当的)的因素,因此无论谁加载类都可以提供正确的信息。

这是一个创建单例的可爱技巧:使用单值枚举。 JVM保证不能创建多个实例。在枚举的构造函数(不是静态初始化程序块)中,执行您需要的任何数据库或文件系统访问,并在发生故障时适当地抛出适当的RuntimeException。枚举将在加载类时实例化,因此您不需要使用静态初始化块。