看到JAVA书上说到如题的话,不是特别理解“不能互相访问”的含义,比如一个简单的类:
public class CLTest {
    public void parse(String s){
System.out.println(s);
    }
    public static void main(String[] args) {
String str = new String("ok");  //String 类是又启动类装载器装载的
CLTest test = new CLTest();    //CLTest类是有系统类装载器装载的
test.parse(str);    //这叫做互相访问吗?
    }
}
答案当然肯定不是,那么什么样的访问才叫做“互相访问”?
还有在上边的例子里,String类和CLTest类拥有不同的“命名空间”吗(因为书上说不同的类装载器装载的类有不同的命名空间)?
我肯定是没有理解这个概念,请大家指教。

解决方案 »

  1.   

    ClassLoader是有父子关系的。并遵循双亲委派原则,也就是说当你请求一个子CL装载类时,其回首先请求其父CL装载,如父CL装载失败才试图自己装载。
    你提到的不同的classloader我理解应该是没有直接父子关系(或直系祖先关系)的两个CL之间装载的类不能相互访问。
    例如,有如下CL关系:
    CL1
     |-CL2
     |-CL3CL1的Path中加载了Class1那么在CL2,CL3中使用Class1是获取的是同一个Class实例(这里的Class实例不是Object)
    CL2加载了自身path中的一个Class2,同时CL3也自身加载了一个Class2。虽然类名称一样但其实是两个完全不同的Class实例。(通过Class的static变量值不同就可以看出)你可以按照这个思路写一个Test看看
      

  2.   

    绝对不是误导!是<java虚拟机>中说到的,关于classloader对安全性的支持。
    我就是不太理解什么叫不能互相访问
      

  3.   

    就是《深入JAVA虚拟机》(第二版)
      

  4.   

    确实“命名空间”写得很容易产生歧义,可能是翻译的问题吧。这和java class的namespace不是同一个概念。1楼说得基本已经回答了你的问题了。我补充一下为什么要有这种“不能互相访问”的限制。因为当你项目做大了以后,很有可能出现包冲突。例如我们写一个webservice程序需要依赖axis2,那axis2本身依赖很多包,例如xalan2.7.0。但是你自己程序的其他部分会用到xalan1.x,这样就会引起类冲突。因为两个包中有名字(包括namespace)一样的类,jvm只会载入在classpath中放得较前的一个jar中的类,而另一个永远不会被加载,这样需要用到另一个类的地方就会出错了。解决方法就是为axis2的jar包写一个与xalan1.x没有继承关系的classLoader来加载,这样虽然类名一样,但他们互不可见也互不干涉。Eclipse就有自己的一套classLoader,要不然加载那么多插件早就冲突得不行了。
      

  5.   

    不同分支(没有什么关系)的类装载器具有隔离性,
    也就是它们装载同一个类,会有不同的Class实例。
    即使是静态成员,也会有自己单独的内存空间。
    每个类装载器都有自己的命名空间,
    不同命名空间的两个类是不可见的,
    但如果持有类所对应的Class对象的引用,
    还是可以访问另一命名空间的类。(我一直这么理解的)
      

  6.   

    不同分支加载同一个class文件会被认为是不同的class,不可直接互相访问
    比方说class A 分别被两个分支上的classLoader 加载,分别生成a1,a2两个实例,
    如果两个实例都持有对方的引用, 在a1 中调 a2.f();或在a2中调a1.f();都会提示class not found但是通过接口访问没问题,比方说A implements IA ,不同classloader 加载的类可以通过IA的引用互访
      

  7.   

    它所说的“不能互相访问”是指平级的自定义的classloader加载的两个类不能相互访问。
    如下图中:
    bootstrap
      ExtClassloader
        AppClassloader
         -自定义clsloadr1
         -自定义clsloadr2
    自定义clsloadr1和自定义clsloadr2是平级的,被他们load的class是不能相互访问的。
    同时,被处于上层的classloader加载的class不能访问被处于下层的classloader加载的class。   
      

  8.   

    一,有两个术语,一个叫“定义类加载器”,一个叫“初始类加载器”。
    比如有如下的类加载器结构:
    bootstrap
      ExtClassloader
        AppClassloader
        -自定义clsloadr1
        -自定义clsloadr2 
    如果用“自定义clsloadr1”加载java.lang.String类,那么根据双亲委派最终bootstrap会加载此类,那么bootstrap类就叫做该类的“定义类加载器”,而包括bootstrap的所有得到该类class实例的类加载器都叫做“初始类加载器”。二,所说的“命名空间”,是指jvm为每个类加载器维护的一个“表”,这个表记录了所有以此类加载器为“初始类加载器”(而不是定义类加载器,所以一个类可以存在于很多的命名空间中)加载的类的列表,所以,题目中的问题就可以解释了:
    CLTest是AppClassloader加载的,String是通过加载CLTest的类加载器也就是AppClassloader进行加载,但最终委派到bootstrap加载的(当然,String类其实早已经被加载过了,这里只是举个例子)。所以,对于String类来说,bootstrap是“定义类加载器”,AppClassloader是“初始类加载器”。根据刚才所说,String类在AppClassloader的命名空间中(同时也在bootstrap,ExtClassloader的命名空间中,因为bootstrap,ExtClassloader也是String的初始类加载器),所以CLTest可以随便访问String类。这样就可以解释“处在不同命名空间的类,不能直接互相访问”这句话了。三,一个类,由不同的类加载器实例加载的话,会在方法区产生两个不同的类,彼此不可见,并且在堆中生成不同Class实例。四,那么由不同类加载器实例(比如-自定义clsloadr1,-自定义clsloadr2)所加载的classpath下和ext下的类,也就是由我们自定义的类加载器委派给AppClassloader和ExtClassloader加载的类,在内存中是同一个类吗?
    所有继承ClassLoader并且没有重写getSystemClassLoader方法的类加载器,通过getSystemClassLoader方法得到的AppClassloader都是同一个AppClassloader实例,类似单例模式。
    在ClassLoader类中getSystemClassLoader方法调用私有的initSystemClassLoader方法获得AppClassloader实例,在initSystemClassLoader中:
    sun.misc.Launcher l = sun.misc.Launcher.getLauncher();

    scl = l.getClassLoader();
    AppClassloader是sun.misc.Launcher类的内部类,Launcher类在new自己的时候生成AppClassloader实例并且放在自己的私有变量loader里:
    loader = AppClassLoader.getAppClassLoader(extclassloader);
    值得一提的是sun.misc.Launcher类使用了一种类似单例模式的方法,即既提供了单例模式的接口getLauncher()又把构造函数设成了public的。但是在ClassLoader中是通过单件模式取得的Launcher 实例的,所以我们写的每个类加载器得到的AppClassloader都是同一个AppClassloader类实例。
    这样的话得到一个结论,就是所有通过正常双亲委派模式的类加载器加载的classpath下的和ext下的所有类在方法区都是同一个类,堆中的Class实例也是同一个。