如何在初始化大量常量时绕过Java中静态初始化器的大小限制
我有一个类包含大量生成的常量,例如:如何在初始化大量常量时绕过Java中静态初始化器的大小限制,java,compiler-construction,compiler-errors,static-initialization,Java,Compiler Construction,Compiler Errors,Static Initialization,我有一个类包含大量生成的常量,例如: public class Constants extends SomeBaseClass { // init() is defined in some base class... public static final XXX KEY1 = init(...); public static final XXX KEY2 = init(...); public static final XXX KEY3 = init(...); //
public class Constants extends SomeBaseClass {
// init() is defined in some base class...
public static final XXX KEY1 = init(...);
public static final XXX KEY2 = init(...);
public static final XXX KEY3 = init(...);
// ...
public static final XXX KEY2000 = init(...);
}
当生成的常量数量非常大时,会导致静态初始化器大于Java方法大小的上限(即大于64kb),从而导致编译器错误。一种解决方案是为可以保证产生小于64kb字节码的块创建几个“块初始化方法”,以便它们适合于一个方法:
public class Constants extends SomeBaseClass {
public static XXX KEY1;
public static XXX KEY2;
public static XXX KEY3;
// ...
public static XXX KEY2000;
static {
initialise0001To1000();
initialise1001To2000();
}
private static void initialise0001To1000() {
KEY1 = init(...);
KEY2 = init(...);
KEY3 = init(...);
// ...
}
private static void initialise1001To2000() {
// ...
KEY2000 = init(...);
}
}
这样做的缺点是,我不能再将常量声明为final
,因为它们现在不再在静态初始化器中直接初始化
我的问题是,我如何才能绕过编译器/JVM的限制,以使我仍然能够生成
静态final
常量?一个选项是使用继承-拥有一系列类Constants1
,Constants2
,…,ConstantsN
,它们都定义了常量,然后让每一个继承上一个。最后一个类常量
可以直接从最后一个继承。这还允许您标记所有内容final
出于好奇,您是如何得到一个如此大的文件,以至于无法将初始化代码放入64KB的限制中的
希望这有帮助 这不管用吗?不可以。请参阅评论,了解这种回答无法解决问题的原因
这将允许您保持静态变量为最终变量,并且更容易自动生成。然而,java将所有静态init块压缩为一个巨大的静态init块,因此问题没有得到解决
您可以拥有任意数量的静态初始化块。我终于找到了一个包含嵌套类的解决方案。这是在用户的评论中提出的。嵌套类有两个优点:
- 它们允许通过
嵌套类对外部世界隐藏这些解决方案实现细节private
- 它们允许保留常量
final
public class Constants {
public static XXX KEY1 = Constants1.KEY1;
public static XXX KEY2 = Constants1.KEY2;
public static XXX KEY3 = Constants1.KEY3;
// ...
public static XXX KEY2000 = Constants2.KEY2000;
// Nested class holding 1000 constants
private static class Constants1 extends SomeBaseClass {
KEY1 = init(...);
KEY2 = init(...);
KEY3 = init(...);
// ...
}
// Nested class holding the next 1000 constants
private static class Constants2 extends SomeBaseClass {
// ...
KEY2000 = init(...);
}
// Keep generating nested classes for more constants...
private static class Constants3 ... {}
}
- 我将再次遇到同样的问题,常数的数目要大得多
public class Constants {
public static XXX KEY1 = Constants1.KEY1;
public static XXX KEY2 = Constants1.KEY2;
public static XXX KEY3 = Constants1.KEY3;
// ...
public static XXX KEY2000 = Constants2.KEY2000;
// Nested class holding 1000 constants
private static class Constants1 extends SomeBaseClass {
KEY1 = init(...);
KEY2 = init(...);
KEY3 = init(...);
// ...
}
// Nested class holding the next 1000 constants
private static class Constants2 extends SomeBaseClass {
// ...
KEY2000 = init(...);
}
// Keep generating nested classes for more constants...
private static class Constants3 ... {}
}
嗯,是的,这将是一个有效的解决办法,很好的想法。我还可以将所有常量放在接口中,并让
常量
实现所有这些常量……您也可以通过嵌套类来实现这一点,这样您就可以将所有内容都放在单个源文件中。@Loadmaster:那就更好了!这些嵌套类可以是私有的,顶级类可以引用嵌套类中的常量以公开它们,这可能会导致顶级类中的初始化器小得多-可能允许大约200k个常量。我认为这一评论值得一提answer@Loadmaster当前位置我已将你的想法记录在一份报告中。虽然这里的答案更一般(即支持更多常量),但我更喜欢您的想法,因为我可以向外界隐藏这些解决方案实现细节,您最终是如何遇到这个问题的?这段代码是从另一个文件自动生成的吗?@templatetypedef:这是的源代码生成器中的一个实际错误。它从数据库中生成主键、唯一键和外键作为常量对象。对于jOOQ来说,2000个键似乎太多了:您可以使用“虚拟”继承层来处理吗?有一个基类,它有一些非公共的使用名称,其中包含1000个常量,并且有一个静态初始值设定项来设置它们。然后一个派生类再增加1000个,一个子派生类再增加1000个,等等。?除了程序集中其他类的派生之外,只有派生最多的类才能用于任何目的。@supercat:我想你可以。该限制实际上是由类标题中的各种16位(两个字)标题字段施加的。所以,我猜这可以通过继承来避免。。。您必须验证字节码中是否已经有这样的答案(同时删除)。一个字节码类似乎只有一个静态初始化器。编译器将所有初始化语句合并到一个语句中,我认为这是一个遗憾。如果使用枚举,会发生同样的情况吗?缺点XXX必须是同一个类,您必须重新编写代码,以便init()是对枚举构造函数的调用。我不能使用枚举。真实世界的类型XXX
具有泛型类型参数…:-/但我可以想象,在幕后,枚举与常规类共享相同的静态初始化器逻辑……是的。我做了一些复习。枚举也是如此。如果使用实例而不是静态变量,则此策略也不起作用。我将留下这个答案,而不是删除它(即使它是错误的)。b/c知道这种方法不起作用可能对其他人有用。我同意留下这个答案。除了这些评论,它也很有价值,这和答案一样。。。