热门标签 | HotTags
当前位置:  开发笔记 > 前端 > 正文

Java运行时环境之ClassLoader类加载机制详解

这篇文章主要给大家介绍了关于Java运行时环境之ClassLoader类加载机制的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

背景:听说ClassLoader类加载机制是进入BAT的必经之路。

ClassLoader总述:

普通的Java开发其实用到ClassLoader的地方并不多,但是理解透彻ClassLoader类的加载机制,无论是对我们编写更高效的代码还是进BAT都大有裨益;而从“黄埔军校”出来的我对ClassLoader的理解都是借鉴了很多书籍和博客,站在了各大博主的肩膀上,感谢你们!上菜,Classloader最主要的作用就是将Java字节码文件(后缀为.class)加载到JVM中,JVM在启动时不会一次性加载所有的class文件,而是根据需要动态加载class文件,毕竟一次性加载太多jar包的class文件JVM吃不消;下面主要研究Bootstrap ClassLoader、Extention ClassLoader和AppClassLoader这三种类加载器。

谈到ClassLoader就想到我们安装JDK的时候都会在控制台输入java、javac验证是否安装成功,而这个javac就是Java ClassLoader,测试是否能把Java源文件正确编译成Java字节码文件,下面的截图就是个javac的小例子,javac之后加载器把Java源文件编译成TestClassLoader.class字节码文件。

由于下面要讲到ClassLoader的加载路径,这里顺便把Java的环境变量也复习一遍。

JAVA_HOME:

指的是安装JDK的位置,如:JAVA_HOME="/Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home"

PATH:

配置PATH(程序的路径)的作用将就是能够在命令行窗口直接键入程序的名字了,而不再需要键入它的全路径,比如上面代码中我用的到javac和java两个命令。如:PATH=".$PATH:$JAVA_HOME/bin" ;就是在JAVA_HOME路径上添加了JDK下的bin目录即可。

CLASSPATH:

CLASSPATH就是指向jar包的路径,如:PATH=".$PATH:$JAVA_HOME/bin" ; "."表示当前目录。

ClassLoader类加载流程:

三个Class Loader的执行顺序是:Bootstrap CLassloder -> Extention ClassLoader -> AppClassLoader;

1、Bootstrap CLassloder是最顶层的加载类,主要是加载核心类库,也就是%JRE_HOME%\lib下的rt.jar、resources.jar、charsets.jar和class等资源;并且,可以通过启动JVM时指定-Xbootclasspath和路径来改变Bootstrap ClassLoader的加载目录,下面有个小荔子。

2、Extention ClassLoader是扩展的类加载器,其加载的是目录%JRE_HOME%\lib\ext目录下的jar包和class文件;它同样也可以加载-D java.ext.dirs选项指定的目录。

3、Appclass Loader是用于加载当前应用的classpath的所有类,其也称为SystemAppClass。

另外有兴趣的还可以看下Launcher类的源码,源码中规定了三个加载器的环境属性分别为B:sun.boot.class.path、E:java.ext.dirs和A:java.class.path;下面通过代码来简单测试写,如图:


打印输出结果:

BootstrapClassLoader:
        /Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/jre/lib/resources.jar:
            /Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/jre/lib/rt.jar:
                /Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/jre/lib/sunrsasign.jar:
                    /Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/jre/lib/jsse.jar:
                        /Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/jre/lib/jce.jar:
                            /Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/jre/lib/charsets.jar:
                                /Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/jre/lib/jfr.jar:
                                    /Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/jre/classes

ExtClassLoader:
        /Users/apple/Library/Java/Extensions:
            /Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/jre/lib/ext:
                /Library/Java/Extensions:/Network/Library/Java/Extensions:
                    /System/Library/Java/Extensions:/usr/lib/java

AppClassLoader:
        /TJT/Eclipse/workspace/tjt/bin:
            /Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/jre/lib/rt.jar

为了更好的理解三者之间加载的关系,我们来测试一个类的加载器和它的父类加载以及一些不是我们创建的类如String、Double、int等基础类:

从上图中可用看出,自己编写的类Test2.class文件是由AppClassLoader加载的,并且AppClassLoader有父加载器ExtClassLoader,但ExtClassLoader的父加载器为null;Double.class这个Java基础类的加载器为null,其父加载也为空且程序还会报空指针异常错误;其实呢,Double.class是有Bootstrap CLassLoader加载的,也并不是每个加载器都有父加载器;总的来说就是JVM启动时通过Bootstrap类加载器加载rt.jar等核心jar包中的class文件,诸如一些int.class,String.class都是由它加载;JVM初始化sun.misc.Launcher并创建Extension ClassLoader和AppClassLoader实例,且将ExtClassLoader设置为AppClassLoader的父加载器;而Bootstrap虽然没有父加载器,但是它却可以作为一个ClassLoader的父加载器;另外,一个ClassLoader创建时如果没有指定parent,那么它的parent默认就是AppClassLoader;

双亲委托:

当一个类加载器查找class和resource时,是通过“委托模式”进行的,它首先会判断这个class是不是已经加载成功,如果没有加载的话它并不是自己进行查找,而是先通过父加载器,然后递归下去,直到Bootstrap ClassLoader,如果Bootstrap classloader找到了,直接返回,如果没有找到,则一级一级返回,最后是由自身去查找这些对象;这种机制就叫做双亲委托。


从上图可用看出ClassLoader的加载序列,委托是从下往上,查找过程则是从上向下的,以下有几个注意事项:

1、一个AppClassLoader查找资源时,首先会查看缓存是否有,若有则从缓存中获取,否则委托给父加载器;

2.、重复第一步的递归操作,查询类是否已被加载;

3.、如果ExtClassLoader也没有加载过,则由Bootstrap ClassLoader加载,它首先也会查找缓存,如果没有找到的话,就去找自己的规定的路径下,也就是sun.mic.boot.class下面的路径,找到就返回,找不到就让子加载器自己去找。

4.、Bootstrap ClassLoader如果没有查找成功,则ExtClassLoader自己在java.ext.dirs路径中去查找,查找成功就返回,查找不成功则再向下让子加载器找。

5.、若是ExtClassLoader查找不成功,则由ppClassLoader在java.class.path路径下自己查找查找,找到就返回,如果没有找到就让子类找,如果没有子类则会抛出各种异常。

自定义CLassLoader:

在ClassLoader中有四个很重要实用的方法loadClass()、findLoadedClass()、findClass()、defineClass(),可以用来创建属于自己的类的加载方式;比如我们需要动态加载一些东西,或者从C盘某个特定的文件夹加载一个class文件,又或者从网络上下载class主内容然后再进行加载等。分三步搞定:

1、编写一个类继承ClassLoader抽象类;

2、重写findClass()方法;

3、在findClass()方法中调用defineClass()方法即可实现自定义ClassLoader;

需求:自定义一个classloader其默认加载路径为"/TJT/Code"下的jar包和资源。首先创建一个Test.java,然后javac编译并把生成的Test.class文件放到"/TJT/Code"路径下,然后再编写一个DiskClassLoader继承ClassLoader,最后通过FindClassLoader的测试类,调用再Test.class里面的一个find()方法。

package www.baidu;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

public class DiskClassLoader extends ClassLoader{
//自定义classLoader能将class二进制内容转换成Class对象
 private String myPath;
 
 public DiskClassLoader(String path) {
  myPath = path;
 }
 
 //findClass()方法中定义了查找class的方法
 @Override
 protected Class<&#63;> findClass(String name) throws ClassNotFoundException{
  String fileName = getFileName(name);
  File file = new File(myPath,fileName);
  try {
   FileInputStream is = new FileInputStream(file);
   ByteArrayOutputStream bos = new ByteArrayOutputStream();
   int len = 0;
   try {
    while((len = is.read()) != -1) {
     bos.write(len);
    }
   } catch (IOException e) {
    e.printStackTrace();
   }
   byte[] data = bos.toByteArray();
   is.close();
   bos.close();
   //数据通过defineClass()生成了Class对象
   return defineClass(name, data,0,data.length );
  } catch (Exception e) {
   e.printStackTrace();
  }
  return super.findClass(name);
 }
 
 private String getFileName(String name) {
  int lastIndexOf = name.lastIndexOf('.');
  if (lastIndexOf == -1) {
   return name + ".class";
  }else {
   return name.substring(lastIndexOf + 1) + ".class";
  }
 }
}

测试结果如下:找到了自定义的加载路径并且调用了类中的find()方法

package www.baidu;
import java.lang.reflect.Method;

public class FindClassLoader {
 public static void main(String[] args) throws ClassNotFoundException {
  //创建自定义classloader对象
  DiskClassLoader diskL = new DiskClassLoader("/TJT/Code");
  System.out.println("classloader is: "+diskL);
  try {
    //加载class文件
   Class clazz = diskL.loadClass("www.baidu.Test");
   if (clazz != null) {
    Object object = clazz.newInstance();
    Method declaredMethod = clazz.getDeclaredMethod("find", null);
    //通过反射调用Test类的find()方法
    declaredMethod.invoke(object, null);
   }
  } catch (Exception e) {
   e.printStackTrace();
  } 
 }
}

总结:

除此之外,ClassLoader还可以进行程序加密(比如你写了比较骚的jar包),这样我们就可以在程序中加载特定了类,并且这个类只能被我们自定义的加载器进行加载,提高了程序的安全性,但是用的不多;反正我们在项目上是不允许用ClassLoader加密,宁愿裸奔,了解一下。另外就是tomcat的类加载机制也是遵循双亲委派机制的,并且大部分的加载机制和JVM类加载机制一样,理解了Bootstrap ClassLoader、Extention ClassLoader和AppClassLoader这三种加载器后再看tomcat类的加载就可以横着走了。

好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对的支持。


推荐阅读
  • 这是原文链接:sendingformdata许多情况下,我们使用表单发送数据到服务器。服务器处理数据并返回响应给用户。这看起来很简单,但是 ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • Tomcat/Jetty为何选择扩展线程池而不是使用JDK原生线程池?
    本文探讨了Tomcat和Jetty选择扩展线程池而不是使用JDK原生线程池的原因。通过比较IO密集型任务和CPU密集型任务的特点,解释了为何Tomcat和Jetty需要扩展线程池来提高并发度和任务处理速度。同时,介绍了JDK原生线程池的工作流程。 ... [详细]
  • 如何实现JDK版本的切换功能,解决开发环境冲突问题
    本文介绍了在开发过程中遇到JDK版本冲突的情况,以及如何通过修改环境变量实现JDK版本的切换功能,解决开发环境冲突的问题。通过合理的切换环境,可以更好地进行项目开发。同时,提醒读者注意不仅限于1.7和1.8版本的转换,还要适应不同项目和个人开发习惯的需求。 ... [详细]
  • 开发笔记:spring boot项目打成war包部署到服务器的步骤与注意事项
    本文介绍了将spring boot项目打成war包并部署到服务器的步骤与注意事项。通过本文的学习,读者可以了解到如何将spring boot项目打包成war包,并成功地部署到服务器上。 ... [详细]
  • 模块化区块链生态系统的优势概述及其应用案例
    本文介绍了相较于单体区块链,模块化区块链生态系统的优势,并以Celestia、Dymension和Fuel等模块化区块链项目为例,探讨了它们解决可扩展性和部署问题的方案。模块化区块链架构提高了区块链的可扩展性和吞吐量,并提供了跨链互操作性和主权可扩展性。开发人员可以根据需要选择执行环境,并获得奖学金支持。该文对模块化区块链的应用案例进行了介绍,展示了其在区块链领域的潜力和前景。 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • Centos7.6安装Gitlab教程及注意事项
    本文介绍了在Centos7.6系统下安装Gitlab的详细教程,并提供了一些注意事项。教程包括查看系统版本、安装必要的软件包、配置防火墙等步骤。同时,还强调了使用阿里云服务器时的特殊配置需求,以及建议至少4GB的可用RAM来运行GitLab。 ... [详细]
  • 本文介绍了新款奇骏的两个让人上瘾的功能,分别是智能互联系统和BOSE音响。通过对新款奇骏的配置和功能进行评测,探讨了这两个新增功能的使用体验和优势。此外,还介绍了新款奇骏的其他配置和改进,如增加的座椅和驾驶辅助系统,以及内饰的舒适性提升。对于喜欢音响的消费者来说,BOSE音响的升级也是一个亮点。最后,文章提到了BOSE音响的数字还原能力,以及7座版无法配备BOSE音响的原因。 ... [详细]
  • 本文介绍了adg架构设置在企业数据治理中的应用。随着信息技术的发展,企业IT系统的快速发展使得数据成为企业业务增长的新动力,但同时也带来了数据冗余、数据难发现、效率低下、资源消耗等问题。本文讨论了企业面临的几类尖锐问题,并提出了解决方案,包括确保库表结构与系统测试版本一致、避免数据冗余、快速定位问题等。此外,本文还探讨了adg架构在大版本升级、上云服务和微服务治理方面的应用。通过本文的介绍,读者可以了解到adg架构设置的重要性及其在企业数据治理中的应用。 ... [详细]
  • 禁止程序接收鼠标事件的工具_VNC Viewer for Mac(远程桌面工具)免费版
    VNCViewerforMac是一款运行在Mac平台上的远程桌面工具,vncviewermac版可以帮助您使用Mac的键盘和鼠标来控制远程计算机,操作简 ... [详细]
  • 无线认证设置故障排除方法及注意事项
    本文介绍了解决无线认证设置故障的方法和注意事项,包括检查无线路由器工作状态、关闭手机休眠状态下的网络设置、重启路由器、更改认证类型、恢复出厂设置和手机网络设置等。通过这些方法,可以解决无线认证设置可能出现的问题,确保无线网络正常连接和上网。同时,还提供了一些注意事项,以便用户在进行无线认证设置时能够正确操作。 ... [详细]
  • 如何修改路由器密码?路由器登录密码和无线密码的修改方法
    本文介绍了修改路由器密码的两种方法:一是修改路由器登录口令,需要进入路由器后台进行操作;二是修改无线连接密码,通过进入路由器后台的无线设置和无线安全设置进行修改。详细步骤包括复位处理、登录路由器后台、选择系统工具、填入用户名和用户密码、保存修改等。 ... [详细]
  • 本文介绍了2019年上半年内蒙古计算机软考考试的报名通知和考试时间。考试报名时间为3月1日至3月23日,考试时间为2019年5月25日。考试分为高级、中级和初级三个级别,涵盖了多个专业资格。报名采取网上报名和网上缴费的方式进行,报考人员可登录内蒙古人事考试信息网进行报名。详细内容请点击查看。 ... [详细]
author-avatar
当官的好办事_625
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有