1,我写了个自定义的类加载器MyClassLoader,然后写了个类MyClass,我想用我自己的加载器去加载MyClass,但是调用myLoader.loadClass(MyClass.class)的时候,由于MyClassLoader的父类型加载器(即系统加载器(AppClassLoader))能够加载MyClass,所以用的是AppClassLoader。我想要用MyClassLoader来加载该怎么办?我目前想到了一个方法:把MyClass.class文件重命名为MyClass.myclass,问题解决。但是非得该字节码的后缀才能使用自己的类加载器?2,我想看看哪几个类被加载了,该怎么看呢?在类的static块中打印一下显然不行,所以不知道该如何实现?3,看到过这样的说法:JVM由类加载器和执行引擎组成 这样准确吗?

解决方案 »

  1.   

    在第一个问题中 我说的是MyClass.class文件是处于classpath下的
      

  2.   

    处在classpath中不可能做到你想要的那种效果吧。。JVM安排的父类依赖就是为了安全考虑,父类能加载就让父类加载
    你如果说换一个地方来倒是可以指定自定义类加载的path
      

  3.   


    我也是这么想的 所以我就把class后缀改了,让系统加载器不认识 才可以使用自己的类加载器那第2,3个问题呢?
    怎么看哪几个类被加载了?怎么判断一个类有没有被加载?
      

  4.   

    自定义类加载器?都可以自己定义结构了 应该能做到吧
    比如定义一个List存放名字,每次Load一个class就往list里面放,
    代码明天再写试试 系统类加载目前还不了解怎么搞 回头看看
      

  5.   

    噢 你说在被加载的类中啊 static应该是可以在被加载时输出的
    加载过程就是 加载 链接和初始化 会执行static块的 明天再看看 
      

  6.   

    测试类1:package cn.leisore.daily._2011_10_09;public class MyClass { 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
      

  7.   

    关于Q3,sun官方的说法是:
    类加载器->字节码验证器->解释器->运行时->硬件
           |
           \/
         即时编译器->硬件
      

  8.   

    昨天没看源代码 7楼这方法就很好了 
    第一问 将父类加载器指明为ExtClassloader或者null就不可能去找classpath了,这两个是扩展类加载器和根类加载器,7楼的第一问还是使用的AppClassloader的子类
    第二问昨天说的自定义list 看到7楼后来去看了下源代码 确实ClassLoader已经定义好了这个属性 就没必要自己去定义了 但是又没有get方法或者可以继承的东西,反射获取不错,当然你也可以自己定义list 然后重写addClass方法,
    还有昨天说的个问题就是static静态块在加载类的时候某些情况是不会执行的,这个我忘记有哪些情况了 待会查下再贴出来
      

  9.   

    2.加载之后迟早会去运行的,如果是这个逻辑,可以通过static块,但不够精确,如果可以从加载机制处入手的话3.加载,执行(执行中包括各种子步骤)。笼统地不就是可以这样分了嘛
      

  10.   

    1,为了保证类加载器都正确实现代理模式,在开发自己的类加载器时,最好不要覆写 loadClass() 方法,而是覆写 findClass() 方法。
    2,使用命令java -verbose:class XXX类
    3,见8楼。
      

  11.   


    static块在加载的时候肯定不会执行(这一点可以把7楼代码的loadClass.newInstance()注释掉即可看出来) 不过不知道是在连接还是初始化的时候才会执行
      

  12.   


    非常感谢你的代码 getLoadedClass方法写的非常棒!!但是我有几个疑问:都说写自定义加载器的时候重写findClass,不知道为什么要这样?重写findClass和loadClass有什么不同?你为什么要重写loadClass?还有就是我这边3个类都是AppClassLoader加载的 跟你的结果不一样
      

  13.   

    看源代码就知道,loadClass会在找不到类的情况调用findClass,默认findClass是抛出异常的 protected synchronized Class<?> loadClass(String name, boolean resolve)
    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);
        }
      

  14.   

    “还有就是我这边3个类都是AppClassLoader加载的 跟你的结果不一样”
    见9楼,需要指定父加载器为null(根类加载器)或者ExtClassLoader(扩展类加载器)
      

  15.   

    1.你需要重写loadClass方法,比如像下面这样
    @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.我觉得可以这么说
      

  16.   


    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的