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<?> 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 = ;
try {
while((len = is.read()) != -) {
bos.write(len);
}
} catch (IOException e) {
e.printStackTrace();
}
byte[] data = bos.toByteArray();
is.close();
bos.close();
//数据通过defineClass()生成了Class对象
return defineClass(name, data,,data.length );
} catch (Exception e) {
e.printStackTrace();
}
return super.findClass(name);
} private String getFileName(String name) {
int lastIndexOf = name.lastIndexOf('.');
if (lastIndexOf == -) {
return name + ".class";
}else {
return name.substring(lastIndexOf + ) + ".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类的加载就可以横着走了。
Java运行时环境---ClassLoader类加载机制的更多相关文章
- Java运行时环境---内存划分
背景:听说Java运行时环境的内存划分是挺进BAT的必经之路. 内存划分: Java程序内存的划分是交由JVM执行的,而不像C语言那样需要程序员自己买单(C语言需要程序员为每一个new操作去配对del ...
- kubernetes实战-交付dubbo服务到k8s集群(三)安装配置maven和java运行时环境的底包镜像
maven 官方地址: 官方地址 下载maven,shdd7-200 # cd /opt/src # wget https://archive.apache.org/dist/maven/maven- ...
- java 运行时环境和编译器环境
必须要保证运行环境高于编译环境 1.编译器的环境设置 单击项目右键-> Properties -> Java Compiler -> 5或6 如果编译器的环境高于运行时环境会报错. ...
- Windows7下Java运行时环境搭建
第一步:下载JDK 地址:http://www.oracle.com/technetwork/java/javase/downloads/index.html,(由于Sun于2009年被oracle收 ...
- java 常用类库:操作系统System类,运行时环境Runtime
System类: System 类代表Java程序的运行平台,程序不能创建System类的对象, System类提供了一些类变量和类方法,允许直接通过 System 类来调用这些类变量和类方法. Sy ...
- ClassLoader类加载机制&&JVM内存管理
一.ClassLoader类加载机制 在java中类加载是遵循委派双亲加载的:通过调用loadClass方法逐级往上传递委派加载请求,当找不到父ClassLoader时调用其findClass方法尝试 ...
- java运行时内存模式学习
学习java运行时内存模式: 各区介绍: 方法区(线程共享):用于存放被虚拟机加载的类的元数据:静态变量,常量,以及编译和的代码(字节码),也称为永久代(所有该类的实例被回收,或者此类classLoa ...
- 运行时环境(The Runtime Environment)
App Engine应用响应网络请求.当一个客户端(典型的是用户的Web浏览器)使用HTTP请求(比如获取在URL上的网页)连接上应用的时候,网络请求就开始了.当App Engine接收到请求时,它会 ...
- Jvm基础(1)-Java运行时数据区
最近在看<深入理解Java虚拟机>,里面讲到了Java运行时数据区,这是Jvm基本知识,把读书笔记记录在此.这些知识属于常识,都能查到的,如果我有理解不对的地方,还请指出. 首先把图贴上来 ...
随机推荐
- 大白话5分钟带你走进人工智能-第三节最大似然推导mse损失函数(深度解析最小二乘来源)(1)
第三节最大似然推导mse损失函数(深度解析最小二乘来源) 在第二节中,我们介绍了高斯分布的 ...
- TCP报文解析
概述 在<网络基础总结(一)>总结了TCP建立连接和断开连接的流程,然而TCP协议远比我所了解的复杂得多,我所知的可以说就冰山一角,所总结的也只是纸上谈兵,仅仅只能对TCP有个肤浅的认识, ...
- CSharpGL(53)漫反射辐照度
CSharpGL(53)漫反射辐照度 本系列将通过翻译(https://learnopengl.com)这个网站上关于PBR的内容来学习PBR(Physically Based Rendering). ...
- 兼容性:Adapter(适配器模式)【PHP】
Adapter(适配器模式) ---- 加个“适配器”以便于复用 将一个类的接口转换成客户希望的另一个接口.Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作. 应用场景 如果 ...
- 将一个html文件引入另一个html文件的div中
width="" height=""属性可根据要求自己设定
- AI2(App Inventor 2)离线版服务器单机版
注意:每次退出前导出自己的项目到本地做备份. 单机版特点: 1.同步官方最新版本,没有对java源代码进行修改,仅修改war\login.jsp及\war\WEB-INF\appengine-web. ...
- Windows Server 2016-OU组织单位日常操作
技术无所谓贵贱,既然曾经做过就总该是要留下点什么,毕竟做技术这些年给我们留下太多太多的成长经历,总有人问这些已经很皮毛了为什么还要写,其实没那么多花哨理由,就是想着做或者不做这一块总是要对过往做个简单 ...
- pyspider 文档介绍
一 代码区结构 def on_start(self)是脚本的入口点.单击run仪表板上的按钮时将调用它. self.crawl(url, callback=self.index_page)*是这里最重 ...
- CSS3D 转换调试
css3d 测试工具 效果如图: 代码如下: <!DOCTYPE html> <html lang="zh-CN"> <head> <me ...
- 【Python实践-5】使用迭代查找一个list中最小和最大值
# -*- coding: utf-8 -*- #使用迭代查找一个list中最小和最大值,并返回一个tuple #遍历list,找到最小值 def findMinAndMax(L): if L==[] ...