前言:
此刻我们对“css多个class的执行顺序”大体比较关注,看官们都需要分析一些“css多个class的执行顺序”的相关内容。那么小编也在网络上搜集了一些关于“css多个class的执行顺序””的相关知识,希望朋友们能喜欢,大家快快来了解一下吧!导读:本文整理了基于 JDK8 的 ClassLoader 核心知识点,包括 JVM 中 ClassLoader 种类、ClassLoader 执行顺序、父加载器概念、双亲委派机制、自定义类加载器。本文中只给出了部分较为重要代码,如果对 ClassLoader 源码想进一步深入可以参考文末的参考文章列表。
JDK 和 JRE 的作用JDK 提供了 java 的编程环境,它包含编译调试的环境功能,包含 JRE(JDK 目录中的 JRE 为专用 JRE,而安装后与 JDK 同目录的 JRE 为公用 JRE)。开发时一般运行的是 JDK 专用JRE,而运行外部程序时一般运行的是公用 JRE,实现了分工不同的 jre 负责各自范围的内容。JRE 提供了 JAVA 程序运行的必要环境平台JAVAHOME、PATH、CLASSPATHJAVAHOME: JDK安装的位置路径,如:D:\Program Files\Java\jdk1.8.0_241PATH: 配置后运行 bin 中的命令不需要补全全路径,如可以在任意的位置运行 java 和 javac 命令, %JAVA_HOME%\bin;CLASSPATH:指向jar包路径 %JAVA_HOME%\lib;类加载器的种类
在JVM中有三类ClassLoader构成:
Bootstrap ClassLoader 启动类(或根类)加载器Extention ClassLoader 扩展的类加载器Appclass Loader 应用类加载器
Bootstrap ClassLoader
Bootstrap ClassLoader 最顶层的类加载器,主要加载核心类库 %JRE_HOME%\lib 下的 rt.jar、resources.jar、charsets.jar 和 class文件等。
//执行System.out.println(System.getProperty("sun.boot.class.path"));//输出结果D:\Program Files\Java\jdk1.8.0_241\jre\lib\resources.jar;D:\Program Files\Java\jdk1.8.0_241\jre\lib\rt.jar;D:\Program Files\Java\jdk1.8.0_241\jre\lib\sunrsasign.jar;D:\Program Files\Java\jdk1.8.0_241\jre\lib\jsse.jar;D:\Program Files\Java\jdk1.8.0_241\jre\lib\jce.jar;D:\Program Files\Java\jdk1.8.0_241\jre\lib\charsets.jar;D:\Program Files\Java\jdk1.8.0_241\jre\lib\jfr.jar;D:\Program Files\Java\jdk1.8.0_241\jre\classes
Extention ClassLoader
Extention ClassLoader 扩展的类加载器,主要加载目录 %JRE_HOME%\lib\ext 目录下的jar包和class文件。
//执行System.out.println(System.getProperty("java.ext.dirs"));//输出D:\Program Files\Java\jdk1.8.0_241\jre\lib\ext;C:\Windows\Sun\Java\lib\ext
Appclass Loader
Appclass Loader也称为SystemAppClass 加载当前应用的classpath的所有类。
类加载器的执行顺序
除启动类加载器(Bootstrap ClassLoader)外,扩展类加载器和应用类加载器都是通过类sun.misc.Launcher进行初始化,而Launcher类则由启动类加载器进行加载。Launcher相关代码如下:
public Launcher() { Launcher.ExtClassLoader var1; try { //初始化扩展类加载器,构造函数没有入参,无法获取启动类加载器 var1 = Launcher.ExtClassLoader.getExtClassLoader(); } catch (IOException var10) { throw new InternalError("Could not create extension class loader", var10); } try { //初始化应用类加载器,入参为扩展类加载器 this.loader = Launcher.AppClassLoader.getAppClassLoader(var1); } catch (IOException var9) { throw new InternalError("Could not create application class loader", var9); } // 设置上下文类加载器 Thread.currentThread().setContextClassLoader(this.loader); //...}
加载顺序:Bootstrap CLassloder > Extention ClassLoader > AppClassLoader
父加载器概念
AppClassLoader 和 ExtClassLoader 都继承了 URLClassLoader。每个类加载器都有一个父加载器(注意:父类和父加载器是两个不同的概念),可通过 getParent() 获取父类加载器。
System.out.println("ClassLoader is:"+cl.toString());System.out.println("ClassLoader\'s parent is:"+cl.getParent().toString());System.out.println("ClassLoader\'s grand father is:"+cl.getParent().getParent().toString());AppClassLoader 的父加载器是ExtClassLoaderExtClassLoader的父加载器是Bootstrap ClassLoader(上面代码输出 ExtClassLoader 为null 是因为 Bootstrap ClassLoader 本身不是一个Java 类所致)Bootstrap ClassLoader是由C/C++编写的,它本身是虚拟机的一部分,所以它并不是一个JAVA类,也就是无法在java代码中获取它的引用,JVM启动时通过Bootstrap类加载器加载rt.jar等核心jar包中的class文件,之前的int.class,String.class都是由它加载。然后呢,我们前面已经分析了,JVM初始化sun.misc.Launcher并创建Extension ClassLoader和AppClassLoader实例。并将ExtClassLoader设置为AppClassLoader的父加载器。Bootstrap没有父加载器,但是它却可以作用一个ClassLoader的父加载器。双亲委派
双亲委派模型:当一个类加载器接收到类加载请求时,会先请求其父类加载器加载,依次递归,当父类加载器无法找到该类时(根据类的全限定名称),子类加载器才会尝试去加载。
为什么使用双亲委派模型?
双亲委派模型是为了保证Java核心库的类型安全。所有Java应用都至少需要引用java.lang.Object类,在运行时这个类需要被加载到Java虚拟机中。如果该加载过程由自定义类加载器来完成,可能就会存在多个版本的java.lang.Object类,而且这些类之间是不兼容的。
通过双亲委派模型,对于Java核心库的类的加载工作由启动类加载器来统一完成,保证了Java应用所使用的都是同一个版本的Java核心库的类,是互相兼容的。
自定义类加载器
不管是Bootstrap ClassLoader还是ExtClassLoader等,这些类加载器都只是加载指定的目录下的jar包或者资源。如果我们需要动态加载比如从指定目录中加载一个class文件,这时候通过自定义类加载器可以实现。
自定义类加载器只需要继承java.lang.ClassLoader类,然后重写findClass(String name)方法即可,在方法中指明如何获取类的字节码流。如果要破坏双亲委派规范的话,还需重写loadClass方法(双亲委派的具体逻辑实现)。但不建议这么做。
public class ClassLoaderTest extends ClassLoader { private String classPath; public ClassLoaderTest(String classPath) { this.classPath = classPath; } /** * 编写findClass方法的逻辑 * * @param name * @return * @throws ClassNotFoundException */ @Override protected Class<?> findClass(String name) throws ClassNotFoundException { // 获取类的class文件字节数组 byte[] classData = getClassData(name); if (classData == null) { throw new ClassNotFoundException(); } else { // 生成class对象 return defineClass(name, classData, 0, classData.length); } } /** * 编写获取class文件并转换为字节码流的逻辑 * * @param className * @return */ private byte[] getClassData(String className) { // 读取类文件的字节 String path = classNameToPath(className); try { InputStream is = new FileInputStream(path); ByteArrayOutputStream stream = new ByteArrayOutputStream(); byte[] buffer = new byte[2048]; int num = 0; // 读取类文件的字节码 while ((num = is.read(buffer)) != -1) { stream.write(buffer, 0, num); } return stream.toByteArray(); } catch (IOException e) { e.printStackTrace(); } return null; } /** * 类文件的完全路径 * * @param className * @return */ private String classNameToPath(String className) { return classPath + File.separatorChar + className.replace('.', File.separatorChar) + ".class"; } public static void main(String[] args) { String classPath = "/Users/zzs/my/article/projects/java-stream/src/main/java/"; ClassLoaderTest loader = new ClassLoaderTest(classPath); try { //加载指定的class文件 Class<?> object1 = loader.loadClass("com.secbro2.classload.SubClass"); System.out.println(object1.newInstance().toString()); } catch (Exception e) { e.printStackTrace(); } }}最后
以上就是对于 JDK8 的 ClassLoader 核心知识点的整理,希望对各位有所帮助。
感谢您的阅读,如果喜欢本文欢迎关注和转发,本头条号将坚持持续分享IT技术知识。对于文章内容有其他想法或意见建议等,欢迎提出共同讨论共同进步。
参考文章:
标签: #css多个class的执行顺序