我有两个类列出如下
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? >>foo.CustomClassLoader@de6ced
我的问题如下:
为什么java.lang.System
会加载上述java.nio.charset.Charset
,CustomClassLoader
等?在我的想法中,我认为当我运行java -Djava.system.class.loader foo.Test
时,JVM首先搜索类foo.Test
,加载它,执行main方法,然后当它检测到System.out.println()
时,它将继续加载Class java.lang.System
和java.io.PrintWriter
,因为它使用了这些类,对吗?
当我运行一个使用位于java.lang
包中的类的类时,这些类也会再次加载,在我的情况下委托CustomClassLoader&gt;&gt; ExtClassLoader&gt;&gt; BoostrapClassLoader来加载?
加载/lib/rt.jar
和/lib/ext/**.jar
时,在我们运行像java foo.Test
这样的类之前,所有这些类都已经加载了bean?
提前感谢您的帮助!
答案 0 :(得分:3)
要回答你的问题,让我们轮流看看。
Java的ClassLoading机制基于委托模型,其中加载类的尝试通常首先委托给父类加载器。如果父类加载器能够成功加载该类,则加载的类将沿委托链向下传递,并返回最初调用的任何类加载器。由于您将自定义类加载器设置为系统类加载器,因此JVM将其实例化为应用程序启动时使用的默认加载器。然后它用于加载它需要的所有类。大多数这些类(即任何不以foo。*开头的类)都被委托给父类加载器,这解释了为什么你看到关于你的类加载器的消息试图加载它们。
因为非foo。*类的加载被委托给父类加载器,所以加载系统类(如System.out)将再次由父类加载器处理,该加载器处理缓存所有加载的类并将返回先前加载的实例。
加载rt.jar和lib / ext / * .jar文件由父类加载器(或其父级)处理,而不是您。因此,您的类加载器不需要关注这些JAR文件。
通常,构建自定义类加载器以确保可以从非标准位置加载类,例如存储在数据库中的JAR。
答案 1 :(得分:2)
我假设你运行的实际命令行是java -Djava.system.class.loader=foo.CustomClassLoader foo.Test
。
通常,所有类都可以由不同的类加载器多次加载。事实上,他们甚至被认为是不同的阶级。那么foo.Test需要一个java.lang.System,调用它的类加载器来查找或加载它。
在您的情况下,CustomClassLoader将非foo类加载委托给super,它不会第二次加载相同的类,但返回先前加载的类。
谈论装载误导的罐子。这些罐中的类可以按需单独加载。要加载JVM需要创建线程的任何程序,所以甚至在你的类之前加载Thread类及其依赖项。
你可以使用-verbose:class运行sun的java,看看如何加载类。