如何检查一个类是否已经初始化?

11

你可能会问,为什么我想这么做 - 因为我正在使用一个类(来自外部库),它会在其静态初始化器中执行某些操作,而我需要知道它是否已经完成。

我查看了 ClassLoader,但没有找到任何看起来有用的东西。你有什么想法吗?


1
你的意思是“一个类是否被初始化”,这是有很大区别的。 - irreputable
感谢您指出了那个问题,我已经修改了标题。 - mik01aj
1
哪个库?也许有一个副作用你可以检查一下(例如,JDBC驱动程序在DriverManager中注册)。 - Thomas Mueller
这是我所在公司内部使用的一个库。谢谢你的提示,也许我会找到一些有用的东西。 - mik01aj
7个回答

15
您可以使用ClassLoader.findLoadedClass()方法。如果返回null,则表示该类未加载。这样,如果未加载类,则不会加载它。
警告:此代码在系统ClassLoader中无法正常工作,findLoadedClass()是受保护的,您需要用自己的ClassLoader来覆盖它。
请查看下面同一主题的链接,以检查系统ClassLoader是否已加载类。
if(ClassLoader.getSystemClassLoader().findLoadedClass("java.lang.String") != null){
    System.out.println("Yepee, String is loaded !");
}

@irreputable提出了非常好的观点:

“loaded”并不意味着“initialized”。初始化仅发生在由JLS3 $12.4.1定义的精确时刻。

我引用一下:

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

  • T是一个类并且创建了一个T实例。
  • T是一个类并声明了一个静态方法。
  • T声明了一个静态字段。
  • T使用了一个静态字段,该字段不是常量变量(§4.12.4)
  • T是一个顶级类,并且在其中嵌套了一个assert语句(§14.10)

Class类和java.lang.reflect包中调用某些反射方法也会导致类或接口初始化。 在任何其他情况下,都不会初始化类或接口。


资源:

同一主题:


这不会导致在方法调用时加载String吗?因为它必须评估字符串字面值“java.lang.String”? - Nate W.
嗯,这段代码实际上根本不起作用,我们无法在默认类加载器上调用findLoadedClass()。因此,将其视为一个简单的示例。 - Colin Hebert
当然,这只是一个人为简单的例子,但是无论如何,你的if语句不都会始终评估为true吗? - Nate W.
如果它能工作,那么它将始终为“true”(或因无法理解字面字符串而在ClassNotFoundException中崩溃:P)。 - Colin Hebert
1
“loaded”并不意味着“初始化”。初始化仅在由JLS3 $12.4.1定义的精确时刻发生。 - irreputable
既然你已经有了最高票答案,而且你的原始答案实际上并没有回答有关类初始化的问题(但是你的编辑部分有所涉及),为什么不删除你最初不正确的回答呢? - Luke Hutchison

4

为什么不直接引用类(通过创建引用、创建实例或访问静态成员)?这将会启动类型初始化器,如果它还没有被触发,如果已经被触发,那你仍然可以继续。


因为这个库使用 System.getProperty 读取参数,我需要知道是否仍然可以更改它们。 - mik01aj

3

我知道现在已经很晚了,但我认为这个答案可能有用。如果你不太害怕(并且被允许)使用sun.misc.Unsafe类,那么有一个方法可以精确地做到这一点:该方法是

sun.misc.Unsafe.shouldBeInitialized(Class)

如果提供的Class参数被加载但未初始化,则仅在这种情况下返回true


2
您可以使用-verbose标志让JVM在加载类时打印出它们,这可能对您有所帮助。
java -verbose Dummy|head
[Opened C:\Program Files\Java\jre6\lib\rt.jar]
[Loaded java.lang.Object from C:\Program Files\Java\jre6\lib\rt.jar]
[Loaded java.io.Serializable from C:\Program Files\Java\jre6\lib\rt.jar]
[Loaded java.lang.Comparable from C:\Program Files\Java\jre6\lib\rt.jar]
[Loaded java.lang.CharSequence from C:\Program Files\Java\jre6\lib\rt.jar]
[Loaded java.lang.String from C:\Program Files\Java\jre6\lib\rt.jar]

(顺便说一下,我刚刚在一个Hello World程序上尝试了一下,它加载了309个类!哇)

2
你可以尝试这样做:
Class c = new ClassLoader() { Class c = findLoadedClass(className); }.c;

0

如果还不算太晚的话.. 这个也应该可以正常工作

 Class.forName().newInstance();

newInstance() 方法创建一个由该 Class 对象表示的类的新实例。该类被实例化,就像使用空参数列表的 new 表达式一样。如果尚未初始化该类,则会进行初始化。


注意:这仅在可用无参数构造函数的情况下才有效。这并非总是如此(如果您定义了一个或多个带参数的构造函数,但未明确定义一个无参数构造函数)。 - Per Lundberg

0
Class.forName("com.abc.Xyz", true, this.getClass().getClassLoader())

它将会阻塞直到该类已被初始化(由其自身或其他线程)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接