我有一个看起来像这样的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”方法,效果很好!它解决了我的问题。
答案 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
。枚举将在加载类时实例化,因此您不需要使用静态初始化块。