JVM(十) 命名空间

每个类加载器都有自己的命名空间。

和我们Java中的Package的概念是一样的,和XML中的namespace的概念类似。

同一个命名空间内的类是相互可见的,命名空间由该加载器及所有父加载器所加载的类组成。

比如说loader1上面有父加载器,父加载器和所加载的所有的类在一个命名空间里面。子加载器的命名空间包含所有父加载器的命名空间。因此由子加载器加载的类能看见父加载器的类。例如系统类加载器加载的类能看见根类加载器加载的类。由父亲加载器加载的类不能看见子加载器加载的类。如果两个加载器之间没有直接或间接的父子关系,那么它们各自加载类相互不可见。

在同一个命名空间中,不会出现类的完整名字(包括类的包名)相同的两个类;在不同的命名空间中,有可能会出现类的完整名字(包括类的包名)相同的两个类。

比如有两个加载器A和B加载同一个类,他们如果不是父子关系的话,A已经把类加载到内存当中,B也可以把类加载到内存当中。但如果A和B是父子关系,那么他们只有先加载的才能将类加载到内存。因为加载的条件首先判断是否已经加载了这个类,如果没有加载则进行加载,如果加载就不会再进行加载。


把博客《JVM(十一) 创建用户自定义的类加载器》的main方法稍做一些变化。

public static void main(String[] aregs) throws Exception
	{
		MyClassLoader loader1 = new MyClassLoader("loader1");
		loader1.setPath("d:\\myapp\\serverlib\\");
		MyClassLoader loader2 = new MyClassLoader(loader1,"loader2");
		loader2.setPath("d:\\myapp\\clientlib\\");
		/*MyClassLoader loader3 = new MyClassLoader(null,"loader3");
		loader3.setPath("d:\\myapp\\otherlib\\");
		test(loader2);
		test(loader3);*/
		
		Class clazz = loader1.loadClass("Sample");
		Object object = clazz.newInstance();
		Sample sample = (Sample)object;
		System.out.println(sample.v1);
	}
输出结果(命令:java MyClassLoader):

Sample is loaded by: loader1
Dog is loaded by :loader1
Exception in thread "main" java.lang.NoClassDefFoundError: Sample
        at MyClassLoader.main(MyClassLoader.java:88)
Caused by: java.lang.ClassNotFoundException: Sample
        at java.net.URLClassLoader$1.run(URLClassLoader.java:372)
        at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:360)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
        ... 1 more

分析:

MyClassLoader类由系统类加载器加载,而Sample类由loader1类加载,因此MyClassLoader类看不见Sample类。在MyClassLoader类的main()方法中使用Sample类,会导致NoClassDefFoundError错误。


当两个不同命名空间内的类相互不可见时,可采用Java反射机制来访问对方实例的属性和方法。如果把MyClassLoader类的main()方法替换为如下代码:

	public static void main(String[] aregs) throws Exception
	{
		MyClassLoader loader1 = new MyClassLoader("loader1");
		loader1.setPath("d:\\myapp\\serverlib\\");
		MyClassLoader loader2 = new MyClassLoader(loader1,"loader2");
		loader2.setPath("d:\\myapp\\clientlib\\");
		/*MyClassLoader loader3 = new MyClassLoader(null,"loader3");
		loader3.setPath("d:\\myapp\\otherlib\\");
		test(loader2);
		test(loader3);*/
		
		/*Class clazz = loader1.loadClass("Sample");
		Object object = clazz.newInstance();
		Sample sample = (Sample)object;
		System.out.println(sample.v1);*/
		
		Class clazz = loader1.loadClass("Sample");
		Object object = clazz.newInstance(); //创建一个Sample类的对象
		Field field = clazz.getField("v1");
		int v1 = field.getInt(object);
		System.out.println("v1:"+v1);
	}
输出结果:

Sample is loaded by: loader1
Dog is loaded by :loader1
v1:1


如果把D:\myapp\serverlib目录下的Sample.class和Dog.class删除,再把这两个文件拷贝到D:\myapp\syslib目录下,然后运行main()方法,也能正常运行。此时MyClassLoader类和Sample类都由系统类加载器加载,由于它们位于同一个命名空间内,因此相互可见。




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值