字段和代码中的可选类型,是否安全?

时间:2016-12-14 05:15:39

标签: java jvm classloader

我有一个这样的课程:

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.0_111),但这真的很安全吗?

注意:知道,我可以在分离的类中包含可选功能,但在某些情况下这可能会太复杂。

修改

来自Oracle的NoClassDefFoundError文档:

  

如果Java虚拟机或ClassLoader实例尝试加载类的定义(作为普通方法调用的一部分或作为使用new表达式创建新实例的一部分),则抛出)并没有找到班级的定义。

     

在编译当前正在执行的类时存在搜索的类定义,但无法再找到该定义。

可悲的是,它没有明确说明声明或加载但未执行的代码。

2 个答案:

答案 0 :(得分:1)

不,这不行。加载类时,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) {
      }
    }
}

答案 1 :(得分:1)

我相信您的主要问题已在评论

中说明
  

字段声明是否安全且可能不存在类型?

并且安全地你似乎意味着"不会导致NoClassDefFoundError"。

在回答问题本身之前,我想说你最好不这样做。在代码中使用不存在的类型会使其不必要地复杂化 - 这不是安全的。可选的依赖项最好由一种将业务逻辑与依赖项分开的网关来处理。 Roberto Attias建议的附加界面就是一个例子。您还可以在实现中引入自己的接口:一个委托对可选依赖项的所有调用,另一个不执行任何操作。

从技术上讲,答案是

JVM倾向于尽可能懒散地做事 - 特别是类加载。加载类时,JVM只会加载其超类和超接口,而不会加载代码中使用的其他类型。

此外,即使读取和写入具有不存在类型的字段本身也会引发类加载(和NoClassDefFoundError)。这是可能的,因为

  • 如果您正在阅读某个字段,那么可以推迟课程加载,直到您实际使用它为止
  • 如果您正在编写非空值,那么您已经加载了类型来创建实例
  • 如果您正在向字段写入空值,那么类型并不重要(因为null可以分配给任何值)
BTW,同样适用于方法的参数和返回值。