Java 如何保证类加载

Java 如何保证类加载,java,Java,我有一个类,它的初始化需要相当长的时间;它调用一个服务器,该服务器需要几分钟才能准备就绪 该类上的方法在相当长的一段时间内不会被调用,它们总是从启动时自动加载的类中调用。我的设置如下: class SlowToStartUp { public static void init() { // do nothing } static { initiateConnectionToServer() } public static V invokeServer()

我有一个类,它的初始化需要相当长的时间;它调用一个服务器,该服务器需要几分钟才能准备就绪

该类上的方法在相当长的一段时间内不会被调用,它们总是从启动时自动加载的类中调用。我的设置如下:

class SlowToStartUp {
  public static void init() {
     // do nothing
  }

  static {
    initiateConnectionToServer()
  }

  public static V invokeServer() {
     waitForServerToConnect();
     return valueFromServer();
  }
}


class AlwaysLoaded {
  static {
     SlowToStartUp.init();
  }

  public void someMethod() {
     V v = SlowToStartUp.invokeServer();
  }
我觉得这在结构上是正确的。如果根本没有
init()
函数,
InitiateConnectionServer()
将不会被调用,直到
someMethod()
第一次需要该类,然后将出现不必要的(在我的系统中是不可接受的)延迟

如果我将
initiateConnectionServer()
调用放入
init()
,接口将更加脆弱(因为调用可能会被遗忘)

但现在我想知道我是否比自己聪明。编译器可以看到
init()
为空。它能不能不优化通话?它现在不这样做,但它能保证吗

我尝试将
init()
标记为
volatile
,但这是不允许的


为了安全起见,我正在考虑将实际初始化放入
init()
,确保它是幂等的,并从静态块调用它,但我想我会先征求建议。

我对自己的问题有一个模糊的答案。中指定了触发类初始化的事件

类或接口类型T将在第一次出现以下任一情况之前立即初始化:

  • T是一个类,并且创建了T的一个实例
  • T是一个类,调用由T声明的静态方法
  • 分配了一个由T声明的静态字段
  • 使用T声明的静态字段,该字段不是常量变量(§4.12.4)
  • T是一个顶级类,执行嵌套在T中的assert语句(§14.10)
在我看来,优化掉空静态函数的编译器将违反我用粗体标记的规定


欢迎评论。

另一种方法是重构为单例类,而不是一堆静态方法。然后在启动时创建singleton,您的初始化代码将立即运行

public class SlowPokeSingleton {
  private SlowPokeSingleton() { /* execute init code */ }

  // created at startup
  private final static SlowPokeSingleton instance = new SlowPokeSingleton();

  public static SlowPokeSingleton instance() { return instance; }
}

您需要调用
instance()
,以确保实际创建了实例。为了安全起见,您可以将其添加到服务器启动中。

我同意Giovanni Botta的观点:


您可以使用单例模式而不是静态方法和获取 应用程序一启动,单例实例就开始运行。那会 强制JVM创建实例,从而运行初始化 代码–乔瓦尼·博塔

具体而言:

1) 将初始化的“耗时”部分放入私有的静态“init()”方法中

2) 将类的构造函数设置为“私有”

3) 提供一个公共静态“getInstance()”方法来获取对(已初始化)singleton实例的引用,而不是构造函数


4) 如果您愿意,您的其他方法可以是非静态的。

您可以不在类构造函数中进行初始化,然后简单地延迟类的实例化,直到需要它为止吗?这将避免必须记住调用静态init()类,但如果需要类的快速响应,则会出现问题。节目于周五午夜开始。周一早上有人用它。我不希望她在建立连接时必须等待两分钟。在程序首次启动时实例化对象是否可行?如果您可以保证在一段时间内没有人需要它,那么您可以一直等到主类加载完成后再调用构造函数。您可以使用单例模式而不是静态方法,并在应用程序启动后立即获取单例实例。这将迫使JVM创建实例,从而运行初始化代码。@GiovanniBotta——你知道,我喜欢这样。也许我会同意。我100%确定我需要调用instance(),以确保实例已创建。我已经在这上面混了好几天了。静态初始值设定项和静态方法不一样。静态初始值设定项在初始化类本身的相同条件下触发,但如果其中没有任何内容,则没有理由保留它。同样,如果您的static init()方法为空且从未调用过,也没有理由保留它,但您确定编译器会删除它吗?我所指的静态函数是
init()
。我认为优化它会违反明确的规则。我确信我现在使用的编译器不会删除它,但我不想依赖(潜在的)临时行为。我不认为任何编译器会优化掉公共静态方法,即使是空的,但我不能100%肯定地告诉你。然而,对于您的特殊情况,我认为这一点是没有意义的:在构造函数中初始化您的类,并在程序启动时创建一个新实例,可以是正常的,也可以是单个实例。你有理由不这样做,还是你对规范更感兴趣?@Brian,“不考虑它”算不算是不这样做的理由?当Giovanni“Johnny Thump”Botta建议我使用singleton时,我就去把现实生活中的代码改成了这样,尽管一位内部评论员建议我再次改为使用Spring singleton,这是我们的web服务器支持的模式。现在我主要对规范感兴趣。