Java类加载器上有1000万篇文章和文档, / *为什么*编写自己的文章...但它们似乎都是假设有些东西我找不到简单的答案!
我理解类加载器的工作:读取字节码并从中构造一个对象。不同的类加载器以不同的方式执行此操作等。
但是从来没有必须在我自己的代码中对类加载器API进行编码,而且从不必编写自己的代码API,在ClassLoader
自己的代码时,我很难理解实际上是火了。
例如:
public static void main(String[] args) {
Fizz fizz = new Fizz();
fuzz.buzz();
}
在这里,我们有一个Fizz
对象。在可以实例化Fizz
之前,我们需要一个类加载器来启动并将Fizz.class
加载到其缓存中。 这种情况何时何地发生?!?!它在我的代码中没有明确表示所以它必须隐含在JRE的某个地方......?
与该问题相关,如果我编写自己的类加载器,比如WidgetClassLoader
,并希望将其配置为加载我的所有应用程序的类,或者可能只是我的Fizz.class
,我该如何“绑定“这个WidgetClassLoader
进入我的应用程序,以便它知道要使用哪个类加载器?我的代码是否需要显式调用此类加载器,还是像第一个示例一样隐式?提前谢谢!
答案 0 :(得分:7)
你的问题并不像你现在想象的那么简单。
你的Fizz例子: Fizz什么时候装?这在JLS (Chapter 5.4: Linking)中定义。它没有定义何时加载Fizz,但它保证了可见行为。对于'when'部分,如果找不到Fizz,则会从访问Fizz的第一个语句(Fizz fizz = new Fizz())中抛出异常。我很确定在这种情况下它将是新的Fizz(),因为表达式的右侧首先被评估。如果你这样写的话:
Fizz fizz = null;
fizz = new Fizz();
在这种情况下,Fizz fizz = null会抛出异常,因为它是第一次访问Fizz类。
谁加载Fizz?当必须加载类时,使用“属于”需要该类的代码的类加载器来获取该类。在Fizz示例中,这将是使用main方法加载类的类加载器。当然,如果类加载器无法自行加载Fizz,则可以选择委托给它的父类加载器。
如何让JVM使用我的 ClassLoader?有两种方式,明确或隐含。明确地说:您可以通过调用其方法通过您自己的类加载器加载类。 Implcitly:当你从已经从类加载器加载的类中执行代码(意思是方法或初始化器)并且需要在进程中解析类引用时,你的类加载器将被自动使用,因为它是加载代码的类加载器第一名。
答案 1 :(得分:3)
Java有一个默认的类加载器。这将在默认类路径中查找类声明。如果您编写自己的类加载器,则可以(并且应该)设置父类加载器。如果您没有其他的,这将是默认值。如果你不这样做,你的类加载器将无法找到java API类。如果java查找一个类,它不会开始查找自定义类加载器,而是使用父类加载器。如果这个有父母,那么就从那里开始,依此类推。只有在找不到类时,才会使用下一个子类加载器再次尝试它。只要有孩子,这种情况就会持续下去。如果链中的任何加载器都找不到该类,则抛出ClassNotFoundException
。
当然,如果您首先将其设置为默认类加载器(通过调用Thread.currentThread().setContextClassLoader()
)或手动加载类(通过调用loadClass()
),则java仅使用您的类加载器。
我不确定何时调用类加载器。我认为它是在启动程序(在所有声明为import
的类上)或首次使用类(变量声明或构造函数调用)时调用的。
答案 2 :(得分:0)
该课程的实际创建发生在defineClass
。该类是使用来自多个源中的任何一个的字节数组创建的。
到达defineClass
(protected
)的正常路径是findClass
(当然,也是protected
)。所以通常的切入点是loadClass
- > findClass
- > defineClass
。但是特殊情况还有其他途径。
(整个过程非常复杂,代表了添加图层的历史,因为保护变得更加复杂,访问模式也变得更加多样化。)
答案 3 :(得分:0)
如果您对类加载器以及它们何时以及如何工作感兴趣,您还可以查看the OSGi specification - 在我看来,这对您来说将是一个非常有趣的读物。 OSGi是一个Java框架,它提供模块化,清晰的代码分离和生命周期管理,目前非常流行(例如Eclipse本身基于一个)。
OSGi大量使用类加载器,并且有很好的解释,何时以及如何在规范内部发生类加载。基本上他们为每个bundle都有一个单独的bundle类加载器(这就是调用模块的方式),这些类加载器负责处理依赖关系并从另一个bundle中获取正确的类。