Java检查类是否存在
对于决定使用哪个接口的实现类,检查类的存在性非常有用。本文我们探索 Class.forName()
方法检查类路径下是否存在特定类的一些细节差异。
1. 使用Class.forName()
使用java反射检查类的存在性,对于 Class.forName()
方法,其文档中指出如果类不存在则抛出 *ClassNotFoundException*
异常。
1.1 检测ClassNotFoundException异常
首先我们写个测试期望抛出ClassNotFoundException异常,这样可以知道测试满足期望:
@Test(expected = ClassNotFoundException.class)
public void givenNonExistingClass_whenUsingForName_thenClassNotFound() throws ClassNotFoundException {
Class.forName("class.that.does.not.exist");
}
这样我们能够证明当类不存在是会抛 *ClassNotFoundException*
异常。让我们写个类测试其确实存在:
@Test
public void givenExistingClass_whenUsingForName_thenNoException() throws ClassNotFoundException {
Class.forName("java.lang.String");
}
这个测试证明 Class.forName()
方法在类确实存在时不在抛异常。然而这并不是完美的解决方案。
1.2 类初始化的副作用
需要特别指出的是,没有指定类加载器,Class.forName() 方法对执行类的静态初始化代码块,这回导致异常行为。
为了验证这种行为,下面创建一个类在静态块中国抛出 RuntimeException
异常,这样可以知道初始化块是否被执行:
public static class InitializingClass {
static {
if (true) {
//enable throwing of an exception in a static initialization block
throw new RuntimeException();
}
}
}
通过查阅 forName文档,我们看到如果初始化失败会抛出 ExceptionInInitializerError
异常。下面写个测试进行验证:
@Test(expected = ExceptionInInitializerError.class)
public void givenInitializingClass_whenUsingForName_thenInitializationError() throws ClassNotFoundException {
Class.forName("path.to.InitializingClass");
}
执行初始化块有不可见的副作用。现在知道了如何引起性能问题或潜在错误。下面讨论如何跳过类初始化。
2. 跳过 Class.forName() 中初始化
幸运的是,forName()方法提供了重载方法,其接收一个类加载器,以及是否执行类初始化两个参数。根据文档,下面调用时等价的:
Class.forName("Foo")
Class.forName("Foo", true, this.getClass().getClassLoader())
现在我们修改true为false,测试测试进行验证是否触发了静态块的调用:
@Test
public void givenInitializingClass_whenUsingForNameWithoutInitialization_thenNoException() throws ClassNotFoundException {
Class.forName("path.to.InitializingClass", false, getClass().getClassLoader());
}
3. Java9 实现
对于Java9及以上版本,有第三个重载方法,其接收模块或类名字符串。该重载默认不运行类初始化,另外如果请求的类不存在不再抛异常而是返回null。
4. 总结
本文我们解释了调用Class.forName会引起类初始化的副作用和性能问题,因此尽量使用其重载方法避免这些问题。
本文参考链接:https://blog.csdn.net/neweastsun/article/details/108809901