如何以及当Bootstrap jar找到../ lib/rt.jar和../lib/ext.**jar时被加载?
我有两个类如下如何以及当Bootstrap jar找到../ lib/rt.jar和../lib/ext.**jar时被加载?
package foo;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
public class CustomClassLoader extends ClassLoader {
public CustomClassLoader(ClassLoader parent){
super(parent);
}
public Class<?> loadClass(String name) throws ClassNotFoundException {
System.out.println(" >>> loading " + name);
if (name.startsWith("foo")) {
return getClass(name);
}
return super.loadClass(name);
}
public Class getClass(String name){
try {
byte[] data= getClassByteData(name);
return this.defineClass(name, data, 0, data.length);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public byte[] getClassByteData(String name) throws IOException {
String file = name.replace(".", File.separator)+".class";
InputStream in = this.getClass().getResourceAsStream("/"+file);
int length = in.available();
byte[] datas = new byte[length];
in.read(datas, 0, length);
return datas;
}
}
package foo;
public class Test {
public static void main(String[] args) {
System.out.println(" Who is my loader? >>" + Test.class.getClassLoader());
}
}
运行:java -Djava.system.class.loader=foo.CustomClassLoader foo.Test
输出:
>>> loading java.lang.System
>>> loading java.nio.charset.Charset
>>> loading java.lang.String
>>> loading foo.Test
>>> loading java.lang.Object
>>> loading java.lang.StringBuilder
>>> loading java.lang.Class
>>> loading java.io.PrintStream
Who is my loader? >>[email protected]
我的问题是如下:
为什么那些
java.lang.System
,java.nio.charset.Charset
等如前所述ve会被加载CustomClassLoader
?在我的想法中,当我运行java -Djava.system.class.loader foo.Test
时,JVM首先搜索类foo.Test
,加载它,执行主要方法,然后当它检测到System.out.println()
时,它将继续加载类java.lang.System
和java.io.PrintWriter
,因为这些类是由它使用的, ?当我运行它使用位于
java.lang
包中的一些类的类,这些类也将被再次加载,在我的情况下放CustomClassLoader >> >>的ExtClassLoader BoostrapClassLoader加载?当
/lib/rt.jar
和/lib/ext/**.jar
被加载时,在我们运行一个像java foo.Test
这样的类之前,所有这些类都已经加载了bean?
非常感谢您的帮助!
要回答你的问题,让我们依次进行。
Java的ClassLoading机制基于委托模型,其中试图加载类通常首先被委派给父类加载器。如果父类加载器能够成功加载该类,则加载的类将传回代理链,并由最初调用的任何类加载器返回。由于您将自定义类加载器设置为系统类加载器,因此JVM将其实例化为应用程序启动时使用的默认加载器。然后用它来加载它需要的所有类。大多数这些类(即任何不以foo开头的东西。*)委托给父类加载器,这就解释了为什么你会看到关于你的类加载器尝试加载它们的消息。
由于将非foo。*类的加载委托给父类加载器,因此再次加载系统类(如System.out)将由父类加载器处理,该类加载器处理缓存所有加载的类和将返回先前加载的实例。
加载rt.jar和lib/ext/*。jar文件由父类加载器(或其父代)处理,而不是您。因此,您的类加载器不需要关注这些JAR文件。
通常,构建自定义类加载器是为了确保可以从非标准位置(例如存储在数据库中的JAR)加载类。
我假设你运行的实际命令行是java -Djava.system.class.loader=foo.CustomClassLoader foo.Test
。
一般来说,所有的类都可以被不同的类加载器多次加载。事实上,他们甚至被认为是不同的班级。那么foo.Test需要一个java.lang.System,它的类加载器被调用来查找或加载它。
在你的情况下,CustomClassLoader将非foo类的加载委托给super,它不会再次加载同一个类,但返回以前加载的类。
谈论加载罐子误导。来自这些罐子的类别按需加载。为了加载任何程序,JVM需要创建一个线程,所以Thread类和它的依赖关系在你的类之前加载。
您可以使用-verbose:class运行sun的java来查看类是如何加载的。