1,我写了个自定义的类加载器MyClassLoader,然后写了个类MyClass,我想用我自己的加载器去加载MyClass,但是调用myLoader.loadClass(MyClass.class)的时候,由于MyClassLoader的父类型加载器(即系统加载器(AppClassLoader))能够加载MyClass,所以用的是AppClassLoader。我想要用MyClassLoader来加载该怎么办?我目前想到了一个方法:把MyClass.class文件重命名为MyClass.myclass,问题解决。但是非得该字节码的后缀才能使用自己的类加载器?2,我想看看哪几个类被加载了,该怎么看呢?在类的static块中打印一下显然不行,所以不知道该如何实现?3,看到过这样的说法:JVM由类加载器和执行引擎组成 这样准确吗?
你如果说换一个地方来倒是可以指定自定义类加载的path
我也是这么想的 所以我就把class后缀改了,让系统加载器不认识 才可以使用自己的类加载器那第2,3个问题呢?
怎么看哪几个类被加载了?怎么判断一个类有没有被加载?
比如定义一个List存放名字,每次Load一个class就往list里面放,
代码明天再写试试 系统类加载目前还不了解怎么搞 回头看看
加载过程就是 加载 链接和初始化 会执行static块的 明天再看看
System.out.println("ClassLoader:"
+ MyClass.class.getClassLoader().getClass().getName());
}
}
测试类2:package cn.leisore.daily._2011_10_09;import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
import java.util.Vector;public class TestMyClassLoader { public static void main(String[] args) throws Exception,
IllegalAccessException { // Q1
URL[] url = new URL[] { new File("classes/").toURI().toURL() };
ClassLoader classLoader = TestMyClassLoader.class.getClassLoader();
MyClassLoader myClassLoader = new MyClassLoader(url, classLoader);
Class<?> loadClass = myClassLoader
.loadClass("cn.leisore.daily._2011_10_09.MyClass");
loadClass.newInstance(); // Q2
System.out.println( "BootstrapClassLoader loaded class: i don't know");
System.out.println( "ExtClassLoader loaded class:" + Arrays.asList(getLoadedClass(classLoader.getParent())));
System.out.println( "SystemClassLoader loaded class:" + Arrays.asList(getLoadedClass(classLoader)));
System.out.println( "MyClassLoader loaded class:" + Arrays.asList(getLoadedClass(myClassLoader)));
// Q3 基本是对的
} static String[] getLoadedClass(ClassLoader cl) throws Exception {
Class<?> clz0 = ClassLoader.class;
Field field = null;
while (clz0 != null) {
field = clz0.getDeclaredField("classes");
if (field != null) {
break;
}
clz0 = clz0.getSuperclass();
}
field.setAccessible(true);
Vector vector = (Vector) field.get(cl);
int size = vector.size();
String[] clz = new String[size];
for (int i = 0; i < size; i++) {
clz[i] = ((Class<?>) (vector.get(i))).getName();
}
return clz;
}
}class MyClassLoader extends URLClassLoader { public MyClassLoader(URL[] urls, ClassLoader parent) {
super(urls, parent);
} public void addURL(URL url) {
super.addURL(url);
} @Override
public Class<?> loadClass(String name) throws ClassNotFoundException { if (name.startsWith("cn.leisore.")) {
try {
return super.findClass(name);
} catch (ClassNotFoundException e) {
// nothing
}
} return super.loadClass(name);
}
}
输出:
ClassLoader:cn.leisore.daily._2011_10_09.MyClassLoader
BootstrapClassLoader loaded class: i don't know
ExtClassLoader loaded class:[]
SystemClassLoader loaded class:[cn.leisore.daily._2011_10_09.TestMyClassLoader, cn.leisore.daily._2011_10_09.MyClassLoader]
MyClassLoader loaded class:[cn.leisore.daily._2011_10_09.MyClass]问题回答见main中Q1~3
类加载器->字节码验证器->解释器->运行时->硬件
|
\/
即时编译器->硬件
第一问 将父类加载器指明为ExtClassloader或者null就不可能去找classpath了,这两个是扩展类加载器和根类加载器,7楼的第一问还是使用的AppClassloader的子类
第二问昨天说的自定义list 看到7楼后来去看了下源代码 确实ClassLoader已经定义好了这个属性 就没必要自己去定义了 但是又没有get方法或者可以继承的东西,反射获取不错,当然你也可以自己定义list 然后重写addClass方法,
还有昨天说的个问题就是static静态块在加载类的时候某些情况是不会执行的,这个我忘记有哪些情况了 待会查下再贴出来
2,使用命令java -verbose:class XXX类。
3,见8楼。
static块在加载的时候肯定不会执行(这一点可以把7楼代码的loadClass.newInstance()注释掉即可看出来) 不过不知道是在连接还是初始化的时候才会执行
非常感谢你的代码 getLoadedClass方法写的非常棒!!但是我有几个疑问:都说写自定义加载器的时候重写findClass,不知道为什么要这样?重写findClass和loadClass有什么不同?你为什么要重写loadClass?还有就是我这边3个类都是AppClassLoader加载的 跟你的结果不一样
throws ClassNotFoundException
{
// First, check if the class has already been loaded
Class c = findLoadedClass(name);
if (c == null) {
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClass0(name);
}
} catch (ClassNotFoundException e) {
// If still not found, then invoke findClass in order
// to find the class.
c = findClass(name);
}
}
if (resolve) {
resolveClass(c);
}
return c;
} protected Class<?> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
见9楼,需要指定父加载器为null(根类加载器)或者ExtClassLoader(扩展类加载器)
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
// TODO Auto-generated method stub
Class c;
try{
c = findClass(name);
} catch (ClassNotFoundException e) {
c = super.loadClass(name);
}
return c;
}
实际上是在用自定义classloader不能load class的时候,才用parent classloader去加载。当然,你还要重写findClass方法,告诉它怎么loadclass。
2.jvm内部肯定会记录哪个classloader加载了哪些class,但是好像没有找到API。
3.我觉得可以这么说
1. 都说写自定义加载器的时候重写findClass,不知道为什么要这样?
ClassLoader的loadClass方法实现了双亲委派模式的流程:缓存->parent.loadClass->findClass。为了保持双亲委派模式,jdk上的doc是说鼓励override findClass,这样就可以保证双亲委派模式。2. 重写findClass和loadClass有什么不同?
见1的分析,仅仅override findClass那么就可以保证使用双亲委派模式来加载类,如果overrode loadClass那么就可以“破坏”双亲委派模式(有时必须这么做)3. 你为什么要重写loadClass?
首先看看你的问题,我不是很清楚你的MyClassLoader是怎么实现的,应该使用了双亲委派模式,并且parent可能是appclassloder,问题应该出在你的测试类所在路径同时出现在appclassloder的classpath中,所以导致总是appclassloder先加载。我例子中的override loadClass就是想破坏双亲委派模式,让cn.leisore.包中的类使用自己的classloader加载。4. 还有就是我这边3个类都是AppClassLoader加载的 跟你的结果不一样?
问题可能出在:
URL[] url = new URL[] { new File("classes/").toURI().toURL() };
我的class被编译在当前工程的classes目录下,你的很可能编译在bin目录下。如果是这样,那么你应该知道为什么结果不一样了。修改classes为bin试试吧5. 不要在override findClass还是loadClass上纠结,根据实际工作该怎么着就怎么着。还可以看看线程中的contextclassLoader或是tomcat的classloader实现,基本都是override loadClass的