引用

https://www.jianshu.com/p/353c26c744df
https://blog.csdn.net/javazejian/article/details/73413292

编译

将.java文件编译后成为.class文件,使之可随处运行。

运行

将编译生成的.class文件交给Java虚拟机(JVM)执行。

加载流程(引用)

  1. 加载:类加载过程的一个阶段:通过一个类的完全限定名(包名+类名)查找此类字节码文件,并利用字节码文件创建一个Class对象
  2. 验证:目的在于确保Class文件的字节流中包含信息符合当前虚拟机要求,不会危害虚拟机自身安全。主要包括四种验证,文件格式验证,元数据验证,字节码验证,符号引用验证。
  3. 准备:为类变量(即static修饰的字段变量)分配内存并且设置该类变量的初始值即0(如static int i=5;这里只将i初始化为0,至于5的值将在初始化时赋值),这里不包含用final修饰的static,因为final在编译的时候就会分配了,注意这里不会为实例变量分配初始化,类变量会分配在方法区中,而实例变量是会随着对象一起分配到Java堆中。
  4. 解析:主要将常量池中的符号引用替换为直接引用的过程。符号引用就是一组符号来描述目标,可以是任何字面量,而直接引用就是直接指向目标的指针、相对偏移量或一个间接定位到目标的句柄。有类或接口的解析,字段解析,类方法解析,接口方法解析(这里涉及到字节码变量的引用,如需更详细了解,可参考《深入Java虚拟机》)。
  5. 初始化:类加载最后阶段,若该类具有超类,则对其进行初始化,执行静态初始化器和静态初始化成员变量(如前面只初始化了默认值的static变量将会在这个阶段赋值,成员变量也将被初始化)。

三种类加载器(引用)

  1. 启动类加载器(Bootstrap ClassLoader)
  • 负责将存放在<JAVA_HOME>/lib目录中的,或者被-Xbootclasspath参数所指定的路径中的,并且是虚拟机按照文件名识别的(如rt.jar,名字不符合的类库即使放在lib目录中也不会被加载)类库加载到虚拟机内存中。
  • 启动类加载器无法被Java程序直接引用,用户在编写自定义类加载器时,如果需要把加载请求委派给引导类加载器,那直接使用null代替即可
  • JDK中的常用类大都由启动类加载器加载,如java.lang.String、java.util.List等。需要特别说明的是,启动类Main class也由启动类加载器加载。
  1. 扩展类加载器(Extension ClassLoader)
  • 由sun.misc.Launcher$ExtClassLoader实现。
  • 负责加载<JAVA_HOME>/lib/ext目录中的,或者被java.ext.dirs系统变量所指定的路径中的所有类库。(感觉不是常用类qwq)
  • 开发者可以直接使用扩展类加载器。
  1. 应用程序类加载器(Application ClassLoader)
  • 由sun.misc.Launcher$AppClassLoader实现。由于这个类加载器是ClassLoader.getSystemClassLoader()方法的返回值,所以一般也称它为系统类加载器。
  • 它负责加载用户类路径ClassPath上所指定的类库,开发者可以直接使用这个类加载器。如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。
  • 启动类Main class、其他如工程中编写的类、maven引用的类,都会被放置在类路径下。Main class由启动类加载器加载,其他类由应用程序类加载器加载。

双亲委派模型

双亲不是指父母,更接近祖先的意思。(parents的翻译是双亲,翻译背锅)
双亲委派模型是指,类加载器优先让祖先去加载这个类,不能加载时,才自己加载。

  1. 首先,检查目标类是否已在当前类加载器的命名空间中加载(即,使用二元组<类加载器实例,全限定名>区分不同类)。
  2. 当类加载请求到来时,先从缓存中查找该类对象,如果存在直接返回,如果不存在则交给该类加载去的父加载器去加载,倘若没有父加载则交给顶级启动类加载器去加载。
  3. 最后倘若仍没有找到,则使用findClass()方法自己加载。

使用双亲委派模型最直接的就是建议使用findClass而非loadClass
我觉得不错的 https://www.jianshu.com/p/353c26c744df

为什么需要关心类加载器?

https://blog.csdn.net/SEU_Calvin/article/details/52315125
https://juejin.cn/post/6844903780031397902
https://learnku.com/articles/43520
当 JVM 启动时,BootStrapClassLoader 也会随之启动并加载核心类库。当核心类库加载完成后,BootStrapClassLoader 会创建 ExtClassLoader 和 AppClassLoader 的实例,两个 Java 实现的类加载器将会加载自己负责路径下的类库,这个过程我们可以在 sun.misc.Launcher 中窥见。

为什么使用类加载器

  1. 加密:Java代码可以轻易的被反编译,如果你需要把自己的代码进行加密以防止反编译,可以先将编译后的代码用某种加密算法加密,类加密后就不能再用Java的ClassLoader去加载类了,这时就需要自定义ClassLoader在加载类的时候先解密类,然后再加载。
  2. 从非标准的来源加载代码:如果你的字节码是放在数据库、甚至是在云端,就可以自定义类加载器,从指定的来源加载类。

为什么使用双亲委派模型

  1. 避免自定义类和核心类库冲突:比如我们大量使用的 java.lang.String 类,如果我们自己写的一个 String 类被加载成功,那对于应用系统来说完全是毁灭性的破坏。
  2. 避免了类的重复加载,JAVA虚拟机中的类唯一性,由类加载器和类本身决定

Thread Context ClassLoader 与 ServiceLoader