【问题标题】:How to circumvent the size limit of a static initialiser in Java when initialising large amounts of constants初始化大量常量时如何规避Java中静态初始化器的大小限制
【发布时间】:2012-05-31 21:47:20
【问题描述】:

我有一个类包含大量生成的常量,例如:

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 的限制,而我仍然可以生成 static final 常量?

【问题讨论】:

  • 你是怎么遇到这个问题的?此代码是从另一个文件自动生成的吗?
  • @templatetypedef:这是jOOQ 的源代码生成器中的一个实际错误。它从数据库生成主键、唯一键和外键作为常量对象。看来 2000 个键对于 jOOQ 来说太多了:groups.google.com/d/topic/jooq-user/2g96fI1Yrj8/discussion
  • 您可以为此使用“虚拟”继承层吗?有一个带有一些非公共名称的基类,其中包含 1,000 个常量,并有一个静态初始化程序设置它们。然后一个派生类增加了 1000 个,子派生类又增加了 1000 个,等等?除了派生程序集中的其他类之外,只有派生度最高的类才会用于任何目的。
  • @supercat:我想你可以。限制实际上是由类标头中的各种 16 位(双字)标头字段强加的。所以,我猜这可以通过继承来规避......不过,您必须在字节码中验证这一点(例如,使用javap

标签: java compiler-construction compiler-errors static-initialization


【解决方案1】:

一种选择是使用继承——有一系列类Constants1Constants2、...、ConstantsN,它们都定义了常量,然后让每个类都从前一个类继承。最后一个类Constants 可以直接从它们中的最后一个继承。这也可以让您标记所有内容final

出于好奇,您是如何最终得到一个如此大的文件,以至于无法将初始化代码放入 64KB 的限制中?

希望这会有所帮助!

【讨论】:

  • 嗯,是的,这将是一个有效的解决方法,很好的想法。我还可以将所有常量放在接口中,让Constants 实现所有常量...
  • 您也可以使用嵌套类来执行此操作,这样您就可以将所有内容放在一个源文件中。
  • @Loadmaster:那就更好了!这些嵌套类可能是私有的,然后顶级类可以引用嵌套类中的常量以公开它们,这可能会导致顶级类中的初始化程序更小 - 可能允许大约 200k 常量。我认为这条评论值得自己回答
  • @Loadmaster:我已经在separate, newly accepted answer 中记录了您的想法。虽然这里的答案更笼统(即支持更多常量),但我更喜欢你的想法,因为我可以向外界隐藏这些变通方法的实现细节
【解决方案2】:

我终于找到了一个涉及嵌套类的解决方案。这是用户Loadmaster 在对this answer here 的评论中建议的。嵌套类有两个优点:

  • 它们允许通过 private 嵌套类向外界隐藏这些变通方法的实现细节
  • 它们允许保持常量final

但与templatetypedef's解决方案相比,它们也有一个劣势:

  • 我将再次遇到同样的问题,但常量数量要多得多

然而,目前这似乎是最合适的解决方案:

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 ... {}
}

【讨论】:

    【解决方案3】:

    这不行吗? 没有。请参阅 cmets 了解为什么这种答案不能解决问题。

    这将允许您保持静态变量最终,并且更容易自动生成。 然而java将所有的静态初始化块折叠成一个巨大的静态初始化块,因此问题并没有解决。

    public class Constants extends SomeBaseClass {
    
      // init() is defined in some base class...
      public static final XXX KEY1 ;
      static
      {
           KEY1 = init(...);
      }
      public static final XXX KEY2 ;
      static
      {
           KEY2 = init(...);
      }
      public static final XXX KEY3 ;
      static
      {
           KEY3 = init(...);
      }
    
      // ...
    
    }
    

    【讨论】:

    • 已经有一个这样的答案(同时,已删除)。一个字节码类似乎只有一个静态初始化器。我认为编译器将所有初始化语句组合成一个语句
    • 真可惜。如果您使用枚举,会发生同样的事情吗?缺点 XXX 必须是同一个类,您必须重新编写代码,以便 init() 调用枚举构造函数。
    • 我不能使用枚举。真实世界的类型XXX 具有泛型类型参数... :-/ 但我想在幕后,枚举与常规类共享相同的静态初始化器逻辑...
    • 是的。我做了一些审查。枚举也是如此。如果您使用实例而不是静态变量,此策略也将不起作用。我会留下这个答案而不是删除它(即使它是错误的)b / c它可能对其他人有用,知道这种方法不起作用。
    • 我同意留下这个答案。除了 cmets,它也很有价值
    【解决方案4】:

    您可以拥有任意数量的静态初始化块。

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-11
    • 1970-01-01
    • 2012-08-03
    • 2016-10-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多