Java 字段和代码中的可选类型,是否安全?
我有一门课是这样的:Java 字段和代码中的可选类型,是否安全?,java,jvm,classloader,Java,Jvm,Classloader,我有一门课是这样的: class Environment { somelib.SomeType someOptionalDep = null; public void doSomething() { boolean found = false; try { Class.forName("somelib.SomeType"); found = true; } catch (Class
class Environment {
somelib.SomeType someOptionalDep = null;
public void doSomething() {
boolean found = false;
try {
Class.forName("somelib.SomeType");
found = true;
} catch (ClassNotFoundException e) {
}
if (found) {
someOptionalDep = new somelib.SomeType();
// ...
}
}
}
somelib
是一个可选包:存在于编译时,但从基本jar中排除。事实上,这段代码没有依赖关系(Debian 8上的Oracle Java HotSpot 1.8.0111),但这真的安全吗
注意:我知道,我可以将可选功能包装在单独的类中,但在某些情况下,这太复杂了
编辑:
从Oracle的NoClassDefFoundError
文档中:
如果Java虚拟机或ClassLoader
实例尝试加载类的定义(作为普通方法调用的一部分,或作为使用new
表达式创建新实例的一部分),并且找不到类的定义,则引发
编译当前执行的类时,已存在搜索的类定义,但无法再找到该定义
遗憾的是,它没有明确说明声明或已加载但未执行的代码。不,这不起作用。加载类时,VM还会查找
SomeType
,并抛出错误。正确的方法是创建一个也包含在base.jar中的接口,让SomeType实现该接口,然后执行以下操作:
class Environment {
SomeInterface someOptionalDep = null;
public void doSomething() {
try {
Class c = Class.forName("somelib.SomeType");
someOptionalDep = c.newInstance();
} catch (ClassNotFoundException, InstantiationException, IllegalAccessException e) {
}
}
}
我相信你的主要问题已经在评论中陈述了
对于可能不存在的类型,字段声明是否安全 “安全”的意思似乎是“不会导致
NoClassDefFoundError
”
在回答这个问题之前,我想说你最好不要那样做。在您的代码中有一个不存在的类型会使它变得不必要的复杂-这是不安全的。可选的依赖关系最好由一种网关来处理,该网关将业务逻辑与依赖关系分开。Roberto Attias建议的附加接口就是一个例子。您还可以将自己的接口引入到实现中:一个将所有调用委托给可选的依赖项,另一个什么也不做
从技术上讲,答案是肯定的 JVM倾向于尽可能懒惰地做事情——尤其是类加载。加载类时,JVM将只加载其超类和超接口,而不加载代码中使用的其他类型 此外,即使读取和写入不存在类型的字段本身也不会引发类加载(以及
NoClassDefFoundError
)。这是可能的,因为
- 如果您正在读取一个字段,那么类加载可以推迟,直到您实际使用它
- 如果您正在编写一个非null值,那么您已经加载了创建实例的类型
- 如果将null写入字段,则类型无关紧要(因为null可以分配给任何值)
顺便说一句,方法的参数和返回值也是如此。这有什么意义?为什么不捕获
NoClassDefFoundError
?字段声明对可能不存在的类型是否安全?我怀疑它能工作,除非环境类没有首先加载。@Roberto Attias:正如@shmosel所说,我在Debian 8上用Oracle Java HotSpot 1.8.0111尝试过这一点,并且成功了。“someOptionalDep
”存储第三方库类的实例,该类通常没有可移植接口(可能是对象someOptionalDep
)。请更正。事实上,这应该保证任何遵守规范的JVM都会这样做:在工作时多考虑一下这一点,它会使代码变得非常脆弱。您能否提供一个关于您使用的库中有多少函数的估计,以及当这些函数不可用时您会做什么?JLS的参考是什么?该链接指向“类和接口的初始化”,与问题无关。“可选依赖项最好由一种将业务逻辑与依赖项分离的网关来处理。”-一般来说,这一点很好,但在我看来,这种特殊情况属于例外情况。我的包是对一些依赖于平台的资源的轻量级抽象,其中无法实现繁忙逻辑。@DávidHorváth从这一点和你的其他评论来看,环境
似乎已经使用了类似于我试图描述的内容,即它安全地封装了对某种类型
的访问,将其提供给业务逻辑,不做更多的事情。如果是的话,没关系。否则,这也没关系:)因为这是你深思熟虑的选择,你是唯一能够做出决定的人。