Java的设计初衷是主要面向嵌入式领域,对于自己定义的一些类,考虑使用依需求载入原则。即在程序使用到时才载入类,节省内存消耗,这时就可以通过类载入器来动态载入。

假设你平时仅仅是做web开发,那应该非常少会跟类载入器打交道,但假设你想深入学习tomcatserver的架构,它是不可缺少的。所谓类载入器。就是用于载入Java类到Java虚拟机中,它负责读取Java字节码。并转换成java.lang.Class类的一个实例。使字节代码.class文件得以执行。

一般类载入器负责依据一个指定的类找到相应的字节代码,然后依据这些代码定义成一个Java类。另外还负责载入资源。包含图像文件和配置文件。

类载入器在实际使用中给我们带来的优点是。它能够使Java类动态地载入到JVM并执行。就可以在程序执行时再载入类,提供了非常灵活的动态载入方式。

比如我们熟悉的Applet,从远程server下载字节码到client动态载入到JVM便能够执行。

在Java的庞大体系中,能够将系统分为三种类载入器,各自是:

①   启动类载入器(Bootstrap ClassLoader):载入对象是Java核心库。把一些关键的Java类载入进JVM,这个载入器使用原生代码(C/C++)实现的。并非继承java.lang.ClassLoader,它是全部其它类载入器的终于父载入器,负责载入<JAVA_HOME>/jre/lib文件夹下且被JVM指定的类库。事实上它属于JVM总体的一部分,JVM一启动就将这些指定的类载入到内存中,避免以后过多的I/O操作。提高系统的执行效率。

启动类载入器无法被Java程序直接使用。

②   扩展类载入器(Extension ClassLoader):载入的对象为Java的扩展库。即载入<JAVA_HOME>/jre/lib/ext文件夹里面的类。

这个类由上面的Bootstrap ClassLoader载入,但因为Bootstrap ClassLoader并不是用Java实现,已经脱离了Java体系,所以假设尝试调用扩展类载入器的getParent()方法获取父载入器会得到null,但它的父类载入器是Bootstrap ClassLoader。

Java中能够直接使用扩展类载入器。

③   应用程序类载入器(Application  ClassLoader):亦叫系统类载入器(System ClassLoader),它负责载入用户类路径(CLASSPATH)指定的类库。假设程序没有自定义类载入器。就默认使用应用程序类载入器。它也由Bootstrap ClassLoader载入。但它的父载入类被设置成了Extension ClassLoader。

假设要使用这个载入器,可通过ClassLoader.getSystemClassLoader()获取。

假如有一天你心血来潮也想自己写一个类载入器,那么你仅仅须要继承java.lang.ClassLoader类就可以。于是能够用以下的图2-4-1来清晰表示出各种类载入器的关系,Bootstrap ClassLoader是最根本的类载入器,其不存在父类载入器,Extension ClassLoader由Bootstrap ClassLoader载入。所以它的父类载入器是Bootstrap ClassLoader。Application ClassLoader也是由Bootstrap ClassLoader载入,但它的父载入器被指向了Extension
ClassLoader,而其它全部用户自己定义的类载入器都由Application ClassLoader载入。

由此能够看出越重要的类载入器就越早被JVM载入,这是考虑到安全性。由于先载入的类载入器会充当下一个类载入器的父载入器,在双亲委派模型机制下,就能确保安全性。

那么什么是双亲委派模型?比方爷爷、爸爸、你三代单传,你们家族都遗传懒惰基因,每当遇到事情都互相推脱,如今须要一个人去买一包盐,不然今晚的菜就有色无味了。而你第一个被托付去超市买,但因为你的懒惰。你向你爸爸撒了一顿娇,你爸爸受不了你仅仅能答应帮你去,在懒惰的驱使下,你爸爸最后找了一个借口说要赶工作。让你爷爷出去走动走动顺便买一包盐,就这样你爷爷仅仅能乖乖去超市买盐。

双亲委派模型就类似这种机制。类载入器载入类时首先托付给父类载入器载入。除非父类载入器不能载入才自己载入。

这样的模型要求除了顶层的启动类载入器外,其它的类载入器都要有自己的父类载入器。假如有一个类要载入进来。一个类载入器并不会立即尝试自己将其载入,而是委派到父类载入器。父类载入器收到后又尝试委派到其父类载入器,以此类推,直到委派到启动类载入器,这样一层一层往上委派。仅仅有当父类载入器反馈自己没法完毕这个载入时,子载入器才会尝试自己载入。通过这个机制。保证了Java应用所使用的都是同一个版本号的Java核心库的类。同一时候这个机制也保证了安全性,设想假设Application ClassLoader想要载入一个有破坏性的java.lang.System类,双亲委派模型会一层层向上委派,终于委派给Bootstrap
ClassLoader,而Bootstrap ClassLoader检查到缓存中已经有了这个类。并不会再载入这个有破坏性的System类。

另外,类载入器还拥有全盘负责机制,即当一个ClassLoader载入一个类时,这个类所依赖的、引用的其它全部类都由这个ClassLoader载入,除非在程序中显性地指定另外一个ClassLoader载入。

图2-4-1 类载入器关系

在Java中。我们用全然匹配类名来标识一个类。即用包名和类名。而在JVM中。一个类由全然匹配类名和一个类载入器的实例ID作为唯一标识。

即是说同一个虚拟机能够有两个包名、类名都相同的类。仅仅要他们由两个不同类载入器载入。

于是当我们在Java中常常说两个类相不相等,必须是针对同一个类载入器载入的前提下才有意义。否则就算是相同的字节代码,由不同类载入器载入,这两个类也不是相等的。这样的特征为我们提供了隔离机制,在tomcatserver中是十分实用的。

了解了JVM的类载入器的各种机制后,看看一个类是如何被类载入器载入进来的。一个类准备载入,类载入器先推断此类是否已经被载入过(载入过的类会被缓存在内存中),假设缓存中存在此类则直接返回这个类。

否则获取父类载入器,假设父类载入器为null,则由Bootstrap ClassLoader载入并返回Class。假设父类载入器不为null,则由父类载入器载入。载入成功就返回Class,载入失败则依据类路径查找class文件,找到就载入此class并返回Class,找不到就抛出ClassNotFoundException异常。

图2-4-2 类载入过程

类载入器属于JVM级别的设计,我们非常多时候基本不会跟他打交道,可是假如你想了解整个JAVA体系是怎样工作的,假如你要设计开发自己的框架或中间件或一个较庞大的java软件。那么你必须熟悉类载入器的相关机制,在现实的设计中,依据实际情况利用类载入器能够提供类库的隔离及共享,保证你的软件不同级别的逻辑切割程序不会互相影响,提供更好的安全性。

喜欢研究java的同学能够交个朋友,以下是本人的微信号:

最新文章

  1. wordpress模板学习之导航目录
  2. 将 ASP.NET Core 1.0 应用作为 docker 镜像发布 (Linux版)
  3. 《C#图解教程》读书笔记之六:接口和转换
  4. RealtekRTL8111内建网卡-黑苹果之路
  5. 状态机学习(三)解析JSON
  6. [Elixir008]Nested Module里的动态函数调用方式
  7. IE8上传文件时文件本地路径变成&quot;C:\fakepath\&quot;的问题
  8. html form一点基础知识,实现文件上传
  9. java客户端连接MongoDB数据库的简单使用
  10. 《Android View 的事件分发和滑动冲突》 —预习资料
  11. CH Round #53 -密室
  12. AS 实机测试 ADB.exe 提示
  13. jenkins问题整理
  14. 时间日期date/cal
  15. JavaScript设计模式 - 状态模式
  16. SQL注入之Sqli-labs系列第八篇(基于布尔盲注的注入)
  17. 在Android开发中替换资源图片不起作用的解决方法
  18. mysql 压缩备份 压缩还原 命令
  19. php在数组中判断某个值是否存在
  20. Mac下安装Fiddler抓包工具(别试了,会报错,没办法使用)

热门文章

  1. docker 私有镜像服务器搭建
  2. 在docker中部署nginx
  3. 基于flask的网页聊天室(二)
  4. Python中比元组更好用的namedtuple
  5. LayUI分页基于ASP.NET MVC
  6. 【三种负载均衡器的优缺点】LVS Nginx HAProxy
  7. 一、SQL基础知识点补充
  8. mongodb 的创建和使用
  9. 【HTML/XML 4】实例分析HTML和XML的不同
  10. codeforces 315B