黑马程序员-->Java基础加强-->类加载器

-------android培训java培训、期待与您交流! ----------

一、如何认识类加载器?

类加载器的定义:类加载器(ClassLoader)用来加载 Java 类或者资源到 Java 虚拟机中。

类加载器的作用:一般来说,Java 虚拟机使用 Java 类的方式如下:Java 源程序(.java 文件)在经过 Java 编译器编译之后就被转换成 Java 字节代码(.class 文件)。类加载器负责读取 Java 字节代码,并转换成 java.lang.Class类的一个实例。每个这样的实例用来表示一个 Java 类。通过此实例的 newInstance()方法就可以创建出该类的一个对象。实际的情况可能更加复杂,比如 Java 字节代码可能是通过工具动态生成的,也可能是通过网络下载的。

Java 中的类加载器大致可以分成两类,一类是系统提供的,另外一类则是由 Java 应用开发人员编写的。系统提供的类加载器主要有下面三个:

引导类加载器(Bootstrap):它用来加载 Java 的核心库,是用c语言来编写的,并不是由Java代码编写的,此类加载器是所有l类的祖宗。并不继承自java.lang.ClassLoader。
扩展类加载器(ExtClassLoader):它用来加载 Java 的扩展库(就是额外的jar比如我们自己写的Java类打成jar就可以放在这里)。Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。
系统类加载器(AppClassLoader):它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过ClassLoader.getSystemClassLoader()来获取它。

 

测试一下ClassLoaderTest类的类加载器和System类的类加载器:

运行结果:
sun.misc.Launcher$AppClassLoader
null

 

测试一下类加载器的父子关系:

运行结果:
sun.misc.Launcher$AppClassLoader
sun.misc.Launcher$ExtClassLoader
null

总结:
Java的类加载器有三层(特点):
BootStrap加载核心类库(最先)
ExtClassLoader加载非核心的辅助类库(其次)
AppClassLoader加载每个应用自已的类库(最后)
每个Java程序运行都需要启用上述三个类加载器

基本上所有的类加载器都是 java.lang.ClassLoader 类的一个实例。ClassLoader与加载类的相关方法:
getParent() 返回该类加载器的父类加载器。
loadClass(String name) 加载名称为 name的类,返回的结果是 java.lang.Class类的实例。
findClass(String name) 查找名称为 name的类,返回的结果是 java.lang.Class类的实例。
findLoadedClass(String name) 查找名称为 name的已经被加载过的类,返回的结果是 java.lang.Class类的实例。
defineClass(String name, byte[] b, int off, int len) 把字节数组 b中的内容转换成 Java 类,
返回的结果是 java.lang.Class类的实例。这个方法被声明为final的。

下面介绍另外一种加载类的方法:Class.forName。
Class.forName是一个静态方法,同样可以用来加载类。该方法有两种形式:
String name,boolean initialize, ClassLoader loader)和Class.forName(String className)
第一种形式的参数name表示的是类的全名;initialize表示是否初始化类;loader表示加载时使用的类加载器。
第二种形式则相当于设置了参数 initialize的值为true,loader的值为当前类的类加载器。

 

二、类加载器机制:

a)全盘负责
    当加载一个非核心非辅助的类库时,如果涉及到其他的类,都由该AppClassLoader全盘负责加载

 c)缓存机制
    当加载一个类时,如果事先缓存中有对应的字节码,则直接取来用;如果没有,再临时加载,完成后依然放入到缓存中,以使下次重用

 b)委托机制
    当加载一个类时,首先由父加载器依次加载,如果所有的父加载器都加载不到,最后再由自已加载。
原理分析:
当Java虚拟机要加载一个类时,到底派那个类加载器去加载呢?

1.首先当前线程的类加载器去加载线程中的第一个类。
2.如果类A中引用了类B,Java虚拟机将使用加载A的类加载器来加载类B。
3.直接调用ClassLoader.loaderClass()方法来指定某个类加载器去加载某个类

委托:每个类加载器加载类时,又先委托给其上级类加载器。当所有祖宗类加载器没有加载到类,
回到发起者类加载器,还加载不了,则抛ClassNotFoundException,不是再去找发起者类加载器的儿子。

补充:Java虚拟机是如何判定两个Java类是相同的?
Java 虚拟机不仅要看类的全名是否相同,还要看加载此类的类加载器是否一样。只有两者都相同的情况,
才认为两个类是相同的。即便是同样的字节代码,被不同的类加载器加载之后所得到的类,也是不同的。
 
三、如何自定义自己的类加载器

需要的具备的条件:
1、必须继承ClassLoader
2、覆盖findClass方法
3、返回defindClass转换的Class类的实例

自定义类加载器MyClassLoader代码示例:

 

类加载附件类ClassLoaderAttachment代码示例:

 

类加载测试类ClassLoaderTest代码示例:

运行结果:
hello,java
sun.misc.Launcher$AppClassLoader//说明不是自己的类加载器加载的,是爸爸类加载的,该类还是没加密过的源文件。
所以需要把爸爸类AppClassLoader加载的Class类删除,即工程源目录下的ClassLoaderAttachment.class文件删除。
结果是:
hello,java
cn.itcast.day2.MyClassLoader

总结:一般来说,自己开发的类加载器主要是覆写 findClass(String name)方法。
java.lang.ClassLoader类的方法loadClass()封装了模板方法设计模式的实现。该方法会首先调用findLoadedClass()方法来检查该类是否已经被加载过;如果没有加载过的话,会调用父类加载器的loadClass()方法来尝试加载该类;如果父类加载器无法加载该类的话,就调用findClass()方法来查找该类。因此,为了然后保证类加载器正确实现该模式,在开发自己的类加载器时,最好不要覆写loadClass()方法,而是覆写findClass()方法。然后方法体中通过IO流读写某个类的class文件,最后通过defineclass()方法将二进制数据转换成字节码完成加载。


四、了解类加载器实现过程中的扩展知识

1、将ClassLoaderTest类的class文件,打成jar包,放置在JRE/lib/ext/*.jar,它会由哪个类加载器加载呢?
右击ClassLoaderTest.java-->点击Export。

 

弹出如下窗口,点击Java/JAR file-->点击Next

 

点击Browse

 

存放在jdk目录下的JRE/lib/ext目录下,并且jar包取名为itheima.jar

 

然后再执行ClassLoaderTest类就会出现如下结果:

ClassLoaderTest类现在由ExtClassLoader类加载器加载的,这跟类加载器的委托机制有关。

 

2、class文件进行加密的工具类(结合自定义类加载器演示代码)
执行MyClassLoader类中的main方法,首先先设置传入的参数:

 

点击选中ClassLoaderAttachment在菜单栏打开 ,复制ClassLoaderAttachment类的Class文件的绝对路径,
黏贴到下面这里 第一个为源文件ClassLoaderAttachmen.class的绝对路径 第二个目标文件目录

 

 

在运行前一定要注意运行的Main方法所在的类是否是MyClassLoader!

 

 

执行MyClassLoader类中的main方法后,鼠标移到itcastlib目录按F5刷新,则可以看到编码后的ClassLoaderTest.class文件。

将此class文件,覆盖项目中bin\cn\itcast\day2目录下的ClassLoaderAttachment.class文件,
重新执行ClassLoaderTest类中的main方法,就会报错。这是因为AppClassLoader已经无法正确加载编码后的class文件了,
只有我们自己写一个带有解码功能的类加载器才可以,还有一个前提,必须把爸爸类AppClassLoader中的ClassLoaderAttachment.class文件删除掉。

 

五、有关于类加载器的一个高级问题的实验分析

编写一个能打印出自己的类加载器名称和当前类加载器的父子结构关系链的MyServlet,正常发布后,看到打印结果为WebAppClassloader。
把MyServlet.class文件打成jar包,放到ext目录中,重启tomcat,发现找不到HttpServlet的错误。
把servlet.jar也放到ext目录中,问题解决了,打印的结果是ExtclassLoader 。
代码演示:

 

具体步骤:
新建一个web工程

 

项目名称命名为itcastweb-->点击Finish

 

右击src-->New-->Servlet,创建一个类Servlet

 

命名包名和类名,并且只创建doGet方法

 

点击Finish完成

 

MyEclipse配置tomcat:点击window--preferences

 

在浏览器中输入访问MyServlet的地址,可以看到MyServlet是由WebappClassLoader类加载器加载的。

 

如何想用上级的类加载器加载MyServlet类的话:比如用ExtClassLoader系统类加载器
步骤:(1)先将MyServlet以jar包的方式导出到jre\lib\ext目录下,jar包名称为itheima.jar

 

(2)重启Tomcat服务器,并且通过浏览器访问,可以看到错误信息提示无法加载HttpServlet。
这里需要介绍一下类加载器常见错误
类加载器常见错误一:500

 

java.lang.ClassNotFoundException: javax.servlet.http.HttpServlet
说明在执行程序的时候找不到HttpServlet的类异常
分析:出现此类错误,需要看看出现异常的类是那个,这里是HttpServlet,一般都是这个类不存在或者缺少这个类的jar包引起的异常错误。
解决办法,在该类可能出现的地方查看一下,比如此处HttpServlet,是tomcat的bin目录下的,一般默认tomcat自带的类加载器加载,但是如果在系统加载器中加载该类的子类时候,还必须要加载父类HttpServlet,所以缺少就会报错。要么加上父类,要么删掉extclassLoader或者APPclassloader中存在HttpServle子类的jar包。
java.lang.NoClassDefFoundError: javax/servlet/http/HttpServlet
说明实际上没有这个HttpServlet类 或者存在一个同名错误的类返回defineclass是错误的,在初始化stastic代码块的时候容易出现异常。
分析:一般情况是不存在该类或者是存在同名的类但是错的字节码。
解决办法:重新在该类应该在的位置加入该类或者该类jar包或者目录,有同名的去掉或者覆盖掉,加入正确的该类或者该类jar包或者目录。

这里 就是HttpServlet的子类MyServlet的jar包,名字自己起的,这个最好起同名容易分辨。把在ext目录里的这个jar包去掉就好了。
参考资料:
http://zhidao.baidu.com/question/520922179.html
附加原理图:

 

小结:父级类加载器加载的类无法引用只能被子级类加载器加载的类

 

类加载器常见错误二:404错误

这个可能是配置信息里面跟这个输入的网址不同导致的错误
查看一下web.xml的内容,包括文件名和路径名的对照
这里发现是web.xml中的文件名是Myservlet 这里的网址输入是的最后那个写错成MyServlet
参考资料:
http://blog.sina.com.cn/s/blog_705a5ff00101bg8r.html

 

(3)将HttpServlet所在的jar包拷贝到jre\lib\ext目录下,重新启动tomcat服务器,重新在浏览器中访问MyServlet。

 

可以看到此时加载MyServlet的类加载器是ExtClassLoader类加载器

 

 

-------android培训java培训、期待与您交流! ----------

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值