jar 运行过程和类加载机制有关,而类加载机制又和我们自定义的类加载器有关,现在我们先来了解一下双亲委派模式。
java 中类加载器分为三个:
-
BootstrapClassLoader 负责加载
${JAVA_HOME}/jre/lib
部分 jar 包 -
ExtClassLoader 加载
${JAVA_HOME}/jre/lib/ext
下面的 jar 包 -
AppClassLoader 加载用户自定义 -classpath 或者 Jar 包的 Class-Path 定义的第三方包
类的生命周期为:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using) 和 卸载(Unloading)七个阶段。
当我们执行 java -jar 的时候 jar 文件以二进制流的形式被读取到内存,但不会加载到 jvm 中,类会在一个合适的时机加载到虚拟机中。类加载的时机:
-
遇到 new、getstatic、putstatic 或 invokestatic 这四条字节码指令时,如果类没有进行过初始化,则需要先对其进行初始化。这四条指令的最常见的 Java 代码场景是使用 new 关键字实例化对象的时候,读取或设置一个类的静态字段调用一个类的静态方法的时候。
-
使用 java.lang.reflect 包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化。
-
当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化。
-
当虚拟机启动时,用户需要指定一个要执行的主类(包含 main() 方法的那个类),虚拟机会先初始化这个主类。
当触发类加载的时候,类加载器也不是直接加载这个类。首先交给 AppClassLoader
,它会查看自己有没有加载过这个类,如果有直接拿出来,无须再次加载,如果没有就将加载任务传递给 ExtClassLoader
,而 ExtClassLoader
也会先检查自己有没有加载过,没有又会将任务传递给 BootstrapClassLoader
,最后 BootstrapClassLoader
会检查自己有没有加载过这个类,如果没有就会去自己要寻找的区域去寻找这个类,如果找不到又将任务传递给 ExtClassLoader
,以此类推最后才是 AppClassLoader
加载我们的类。这样做是确保类只会被加载一次。通常我们的类加载器只识别 classpath (这里的 classpath 指项目根路径,也就是 jar 包内的位置)下 .class
文件。jar 中其他的文件包括 jar 包被当做了资源文件,而不会去读取里面的 .class
文件。但实际上我们可以通过自定义类加载器来实现一些特别的操作