类加载器的命名空间

 命名空间是类加载器中一个很重要的概念,对于只要学过java的人都知道java万物皆对象,在java中即使是一个“.class”文件,通过类加载器加载到虚拟机内存,那么在内存中会生成一个对应的Class对象

 那么问题又来了,你应该听说过,一个类在内存中只能有一个Class对象,那么真的是这样吗?没有任何前提吗?接下类我们就来详细的分析一下,为什么那么多人说同一个类在内存中有且只有一个Class对象?真的是这样吗?

首先先来介绍一下类加载器,只有了解了类加载器的概念你才能理解我接下来说的。

在java中一共有三种类加载器,如果也可以说是四种,因为还有一种是我们的自定义类加载器,需要我们自己实现,

  1. 根类加载器也叫启动类加载器,我们的rt.jar包就是根类加载器加载的,如果具体的根类加载负责的classPath可以通过System.getProperty("sun.boot.class.path") 查看根类加载器负责加载的路径
  2. 扩展类加载器,通过System.getProperty("java.ext.dirs") 即可查看
  3. 应用加载器,也叫系统类加载器,System.getProperty("java.class.path") 即可查看 
  4. 自定义类加载器

接下来我们来理解一下类加载器的双亲委派机制

当一个类加载器尝试加载某一个类之前会先委派给它的父类加载器,以此类推,如果父类加载器加载不了才会自己加载,如果自己也加载不了,这时候就会抛出异常 

平时我们自己写的类都是由系统类加载器负责加载,所以平时我们写的类信息都保存在系统类加载器的命名空间中

命名空间:

 命名空间是由该类加载器以及其父类加载器所构成的,其中父类加载器加载的类对其子类可见,但是反过来子类加载的类对父类不可见,同一个命名空间中一定不会出现同一个类(全限定名一模一样的类)多个Class对象,换句话说就是在同一命名空间中只能存在一个Class对象,所以当你听别人说在内存中同一类的Class对象只有一个时其实指的是同一命名空间中,当然也不排除他压根就不知道这个概念。

到这里你应该知道,同一命名空间一个类的Class对象只有一个,那么不同的命名空间呢?看来你能想到这个问题以及很厉害了 

接下来我们来看一个异常

可能你一眼就看出来了,这不就是一个类型转换异常吗?是的!没错!但你看看我圈中的两条信息,发现没有?明明是同一类,而java却告诉我不能转换?这是什么鬼?

当然只看这个异常你是看不懂的,接下类我把代码贴出来,并详细的分析一下

这是一个自定义的类加载器,path为成员属性,来指定这个类加载器负责加载的classPath

extName为扩展的名是一个常量指定为“.class”,然后通过defineClass来返回一个Class对象

这是Studnt类

这是测试类

我们先不讨论异常的事,我们先把这段代码给说清楚

实例化了两个自定义类加载器 loader1和loader2,分别将他们的classPath指定到我电脑的G盘下,

然后通过loader1和loader2分别加载Student类,正常来说这是没有任何问题的。而且还会输出true,先来讲讲为什么没有问题,只有理解了为什么不会出现问题,才能理解为什么出问题

按照类加载器的层级关系 自定义类加载器->系统类加载器->扩展类加载器->根类加载器 是按照这种层级关系来的,这是的层级关系并不是通过继承体现的,而是在自类加载器的内部有个成员属性保存了父类加载器

然后我们知道类加载器是有双亲委派机制的,那么loader1加载student类之前一定会去让它的父类,也就是系统类加载器去加载,系统类加载器然后又让它的父类加载,,以此类推,还是由最后系统类加载器加载,因为它的父类都加载不了,

那么loader2去加载的时候也会按照上面那种流程,但是会先判断这个类是否已经加载过了,如果加载过了就直接返回这个类的Class对象,很显然Student已经被系统类加载器加载过了,所以clazz1和clazz2都代表同一个对象,他们肯定是相同的,然后调用方法本来传入的就是Student对象 ,向下转型也是没问题的,那么我刚刚那个异常是怎么导致的呢?

细心的读者应该会发现,在那个异常信息的上面还打印了一个false。

什么?false?他们不是同一个Class对象吗?接下来看我动了哪些手脚

通过System.property("java.class.path")获取到系统类加载器的classPath当然使用Idea的话编译后的代码是存放在out这个目录的,用idea的都知道,我这里只是因为idea上面有其他的项目所以才使用Eclipse写一下,Eclipse会存放在一个bin目录下,如果实在不知道可以通过上面那行代码确定一下

有没有发现什么问题?我把Student的class字节码文件删除了,

然后在我的G盘保存了一份,然后我通过我自己的类加载器来加载这个Student肯定是能加载的,但是这样的话系统类加载是无法加载的,因为当前字节码文件没有在系统类加载器的classPath中,所以只能由我们的自定义类加载器加载,然后我们通过loader1和loader2各自加载一个。但是此时,loader1和loader2是没有任何关系的,他们加载前只会去找它的父类,父类加载不了只能自己加载,而loader1和loader2没有任何关系,他们加载的类只能它们的子类可见,故他们各自加载了一个Student类,这就加载了两个Class对象,而且他们存放在不同的命名空间中,不同的命名空间中的对象是互不可见的,到这里你应该明白为什么是false了,但是类型转换异常又是怎么出来的呢?其实这个也很好理解,连Class对象都不同,那又怎么转换呢?而且他们还互不可见

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值