不同classloader装载的类不能互相访问?

dagouaofei 2009-03-23 11:23:24
看到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类拥有不同的“命名空间”吗(因为书上说不同的类装载器装载的类有不同的命名空间)?
我肯定是没有理解这个概念,请大家指教。
...全文
978 15 打赏 收藏 转发到动态 举报
写回复
用AI写文章
15 条回复
切换为时间正序
请发表友善的回复…
发表回复
dagouaofei 2009-03-26
  • 打赏
  • 举报
回复
一,有两个术语,一个叫“定义类加载器”,一个叫“初始类加载器”。
比如有如下的类加载器结构:
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实例也是同一个。
IT农夫 2009-03-24
  • 打赏
  • 举报
回复
它所说的“不能互相访问”是指平级的自定义的classloader加载的两个类不能相互访问。
如下图中:
bootstrap
ExtClassloader
AppClassloader
-自定义clsloadr1
-自定义clsloadr2
自定义clsloadr1和自定义clsloadr2是平级的,被他们load的class是不能相互访问的。
同时,被处于上层的classloader加载的class不能访问被处于下层的classloader加载的class。
iyellu 2009-03-24
  • 打赏
  • 举报
回复
不同分支加载同一个class文件会被认为是不同的class,不可直接互相访问
比方说class A 分别被两个分支上的classLoader 加载,分别生成a1,a2两个实例,
如果两个实例都持有对方的引用, 在a1 中调 a2.f();或在a2中调a1.f();都会提示class not found

但是通过接口访问没问题,比方说A implements IA ,不同classloader 加载的类可以通过IA的引用互访
skycc999 2009-03-24
  • 打赏
  • 举报
回复
楼上正解!
bzwm 2009-03-24
  • 打赏
  • 举报
回复
不同分支(没有什么关系)的类装载器具有隔离性,
也就是它们装载同一个类,会有不同的Class实例。
即使是静态成员,也会有自己单独的内存空间。
每个类装载器都有自己的命名空间,
不同命名空间的两个类是不可见的,
但如果持有类所对应的Class对象的引用,
还是可以访问另一命名空间的类。(我一直这么理解的)

悦峰 2009-03-24
  • 打赏
  • 举报
回复
确实“命名空间”写得很容易产生歧义,可能是翻译的问题吧。这和java class的namespace不是同一个概念。1楼说得基本已经回答了你的问题了。我补充一下为什么要有这种“不能互相访问”的限制。因为当你项目做大了以后,很有可能出现包冲突。例如我们写一个webservice程序需要依赖axis2,那axis2本身依赖很多包,例如xalan2.7.0。但是你自己程序的其他部分会用到xalan1.x,这样就会引起类冲突。因为两个包中有名字(包括namespace)一样的类,jvm只会载入在classpath中放得较前的一个jar中的类,而另一个永远不会被加载,这样需要用到另一个类的地方就会出错了。解决方法就是为axis2的jar包写一个与xalan1.x没有继承关系的classLoader来加载,这样虽然类名一样,但他们互不可见也互不干涉。Eclipse就有自己的一套classLoader,要不然加载那么多插件早就冲突得不行了。
dagouaofei 2009-03-24
  • 打赏
  • 举报
回复
就是《深入JAVA虚拟机》(第二版)
wclszh 2009-03-24
  • 打赏
  • 举报
回复
学习,顶起,
ZiSheng 2009-03-24
  • 打赏
  • 举报
回复
[Quote=引用 6 楼 dagouaofei 的回复:]
绝对不是误导!是 <java虚拟机>中说到的,关于classloader对安全性的支持。
我就是不太理解什么叫不能互相访问
[/Quote]
是深入jvm还是哪本?
dagouaofei 2009-03-24
  • 打赏
  • 举报
回复
绝对不是误导!是<java虚拟机>中说到的,关于classloader对安全性的支持。
我就是不太理解什么叫不能互相访问
guoxyj 2009-03-24
  • 打赏
  • 举报
回复
up
ZiSheng 2009-03-24
  • 打赏
  • 举报
回复
没有听书过,互相访问?,类的命名空间?什么书啊?
纯粹误导
sjkof 2009-03-24
  • 打赏
  • 举报
回复
up
kingssman 2009-03-24
  • 打赏
  • 举报
回复
路过学习,基本功比较差,呵呵
啤酒沫 2009-03-23
  • 打赏
  • 举报
回复
ClassLoader是有父子关系的。并遵循双亲委派原则,也就是说当你请求一个子CL装载类时,其回首先请求其父CL装载,如父CL装载失败才试图自己装载。
你提到的不同的classloader我理解应该是没有直接父子关系(或直系祖先关系)的两个CL之间装载的类不能相互访问。
例如,有如下CL关系:
CL1
|-CL2
|-CL3

CL1的Path中加载了Class1那么在CL2,CL3中使用Class1是获取的是同一个Class实例(这里的Class实例不是Object)
CL2加载了自身path中的一个Class2,同时CL3也自身加载了一个Class2。虽然类名称一样但其实是两个完全不同的Class实例。(通过Class的static变量值不同就可以看出)

你可以按照这个思路写一个Test看看
线程 单例模式下的线程安全http://blog.sina.com.cn/s/blog_75247c770100yxpb.html map集合 集合数据结构及方法的使用 多线程 线程进程 线程状态 线程状态的改变 sql :临时表、游标、存贮过程、触发机制http://www.cnblogs.com/SkySoot/archive/2012/04/09/2439190.html jvm工作原理 JVM中类的装载是由ClassLoader和它的子类来实现的,Java ClassLoader 是一个重要的Java运行时系统组件。它负责在运行时查找和装入类文件的类。 1说说JVM原理?内存泄露与溢出区别,何时产生内存泄露? 编译源代码为本地机器码执行。 内存泄露是一部分内存无法回收。溢出是说内存不够用了。泄露可能在将来会导致溢出 当对象在程序中不会被使用,但却有其他对象持有该对象时会发生溢出。比如出现持有的环的情况。 java并发面试题 .内存存储锁控制 http://blog.csdn.net/geolo/article/details/8670900 4.请说明下java的内存模型及其工作流程。 答:Java把内存划分成两种:一种是栈内存,一种是堆内存。 栈内存:存放对象:函数中基本类型的变量和对象的引用变量、静态类方法 ;特点:栈有一个很重要的特殊性,就是存在栈中的数据可以共享。 堆内存:存放对象:用来存放由new创建的对象和数组;特点:在堆中分配的内存,由Java虚拟机的自动垃圾回收器来管理。 java 内存模型 ( java memory model ):根据Java Language Specification中的说明, jvm系统中存在一个主内存(Main Memory或Java Heap Memory),Java中所有对象成员变量都储存在主存中,对于所有线程都是共享的。每条线程都有自己的工作内存(Working Memory),工作内存中保存的是主存中某些对象成员变量的拷贝,线程对所有对象成员变量的操作都是在工作内存中进行,线程之间无法相互直接访问,变量传递均需要通过主存完成。 (1) 获取对象监视器的锁(lock) (2) 清空工作内存数据, 从主存复制对象成员变量到当前工作内存, 即同步数据 (read and load) (3) 执行代码,改变共享变量值 (use and assign) (4) 将工作内存数据刷回主存 (store and write) (5) 释放对象监视器的锁 (unlock) spring框架原理 10. spring工作机制及为什么要用? 1.spring mvc请所有的请求都提交给DispatcherServlet,它会委托应用系统的其他模块负责负责对请求进行真正的处理工作。 2.DispatcherServlet查询一个或多个HandlerMapping,找到处理请求的Controller. 3.DispatcherServlet请求提交到目标Controller 4.Controller进行业务逻辑处理后,会返回一个ModelAndView 5.Dispathcher查询一个或多个ViewResolver视图解析器,找到ModelAndView对象指定的视图对象 6.视图对象负责渲染返回给客户端。
java核心面试技术点 线程 单例模式下的线程安全http://blog.sina.com.cn/s/blog_75247c770100yxpb.html map集合 集合数据结构及方法的使用 多线程 线程进程 线程状态 线程状态的改变 sql :临时表、游标、存贮过程、触发机制http://www.cnblogs.com/SkySoot/archive/2012/04/09/2439190.html jvm工作原理 JVM中类的装载是由ClassLoader和它的子类来实现的,Java ClassLoader 是一个重要的Java运行时系统组件。它负责在运行时查找和装入类文件的类。 1说说JVM原理?内存泄露与溢出区别,何时产生内存泄露? 编译源代码为本地机器码执行。 内存泄露是一部分内存无法回收。溢出是说内存不够用了。泄露可能在将来会导致溢出 当对象在程序中不会被使用,但却有其他对象持有该对象时会发生溢出。比如出现持有的环的情况。 java并发面试题 .内存存储锁控制 http://blog.csdn.net/geolo/article/details/8670900 4.请说明下java的内存模型及其工作流程。 答:Java把内存划分成两种:一种是栈内存,一种是堆内存。 栈内存:存放对象:函数中基本类型的变量和对象的引用变量、静态类方法 ;特点:栈有一个很重要的特殊性,就是存在栈中的数据可以共享。 堆内存:存放对象:用来存放由new创建的对象和数组;特点:在堆中分配的内存,由Java虚拟机的自动垃圾回收器来管理。 java 内存模型 ( java memory model ):根据Java Language Specification中的说明, jvm系统中存在一个主内存(Main Memory或Java Heap Memory),Java中所有对象成员变量都储存在主存中,对于所有线程都是共享的。每条线程都有自己的工作内存(Working Memory),工作内存中保存的是主存中某些对象成员变量的拷贝,线程对所有对象成员变量的操作都是在工作内存中进行,线程之间无法相互直接访问,变量传递均需要通过主存完成。 (1) 获取对象监视器的锁(lock) (2) 清空工作内存数据, 从主存复制对象成员变量到当前工作内存, 即同步数据 (read and load) (3) 执行代码,改变共享变量值 (use and assign) (4) 将工作内存数据刷回主存 (store and write) (5) 释放对象监视器的锁 (unlock) spring框架原理 10. spring工作机制及为什么要用? 1.spring mvc请所有的请求都提交给DispatcherServlet,它会委托应用系统的其他模块负责负责对请求进行真正的处理工作。 2.DispatcherServlet查询一个或多个HandlerMapping,找到处理请求的Controller. 3.DispatcherServlet请求提交到目标Controller 4.Controller进行业务逻辑处理后,会返回一个ModelAndView 5.Dispathcher查询一个或多个ViewResolver视图解析器,找到ModelAndView对象指定的视图对象 6.视图对象负责渲染返回给客户端。
主要特性Java 语言是简单的:Java 语言的语法与 C 语言和 C++ 语言很接近,使得大多数程序员很容易学习和使用。另一方面,Java 丢弃了 C++ 中很少使用的、很难理解的、令人迷惑的那些特性,如操作符重载、多继承、自动的强制类型转换。特别地,Java 语言不使用指针,而是引用。并提供了自动分配和回收内存空间,使得程序员不必为内存管理而担忧。Java 语言是面向对象的:Java 语言提供类、接口和继承等面向对象的特性,为了简单起见,只支持类之间的单继承,但支持接口之间的多继承,并支持类与接口之间的实现机制(关键字为 implements)。Java 语言全面支持动态绑定,而 C++语言只对虚函数使用动态绑定。总之,Java语言是一个纯的面向对象程序设计语言。Java语言是分布式的:Java 语言支持 Internet 应用的开发,在基本的 Java 应用编程接口中有一个网络应用编程接口(java net),它提供了用于网络应用编程的类库,包括 URL、URLConnection、Socket、ServerSocket 等。Java 的 RMI(远程方法激活)机制也是开发分布式应用的重要手段。Java 语言是健壮的:Java 的强类型机制、异常处理、垃圾的自动收集等是 Java 程序健壮性的重要保证。对指针的丢弃是 Java 的明智选择。Java 的安全检查机制使得 Java 更具健壮性。Java语言是安全的:Java通常被用在网络环境中,为此,Java 提供了一个安全机制以防恶意代码的攻击。除了Java 语言具有的许多安全特性以外,Java 对通过网络下载的类具有一个安全防范机制(类 ClassLoader),如分配不同的名字空间以防替代本地的同名类、字节代码检查,并提供安全管理机制(类 SecurityManager)让 Java 应用设置安全哨兵。Java 语言是体系结构中立的:Java 程序(后缀为 java 的文件)在 Java 平台上被编译为体系结构中立的字节码格式(后缀为 class 的文件),然后可以在实现这个 Java 平台的任何系统中运行。这种途径适合于异构的网络环境和软件的分发。Java 语言是可移植的:这种可移植性来源于体系结构中立性,另外,Java 还严格规定了各个基本数据类型的长度。Java 系统本身也具有很强的可移植性,Java 编译器是用 Java 实现的,Java 的运行环境是用 ANSI C 实现的。Java 语言是解释型的:如前所述,Java 程序在 Java 平台上被编译为字节码格式,然后可以在实现这个 Java 平台的任何系统中运行。在运行时,Java 平台中的 Java 解释器对这些字节码进行解释执行,执行过程中需要的类在联接阶段被载入到运行环境中。Java 是高性能的:与那些解释型的高级脚本语言相比,Java 的确是高性能的。事实上,Java 的运行速度随着 JIT(Just-In-Time)编译器技术的发展越来越接近于 C++。Java 语言是多线程的:在 Java 语言中,线程是一种特殊的对象,它必须由 Thread 类或其子(孙)类来创建。通常有两种方法来创建线程:其一,使用型构为 Thread(Runnable) 的构造子类将一个实现了 Runnable 接口的对象包装成一个线程,其二,从 Thread 类派生出子类并重写 run 方法,使用该子类创建的对象即为线程。值得注意的是 Thread 类已经实现了 Runnable 接口,因此,任何一个线程均有它的 run 方法,而 run 方法中包含了线程所要运行的代码。线程的活动由一组方法来控制。Java 语言支持多个线程的同时执行,并提供多线程之间的同步机制(关键字为 synchronized)。Java 语言是动态的:Java 语言的设计目标之一是适应于动态变化的环境。Java 程序需要的类能够动态地被载入到运行环境,也可以通过网络来载入所需要的类。这也有利于软件的升级。另外,Java 中的类有一个运行时刻的表示,能进行运行时刻的类型检查。

62,615

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧