如上图所示,Java中的类的加载靠的是类加载器实现的,其中通过loadClass进行类加载的时候会经历:加载》》验证》》准备》》解析》》初始化几个步骤。
加载:查找磁盘中的类的字节码文件并通过io操作读入,只有类被真正使用的时候才会被加载验证:验证字节码是否符合规范准备:为静态变量分配内存空间,并且赋上默认的初始值。int类型赋值0、boolean类型赋值false、对象类型赋值null解析:将符号引用(静态方法)转换为直接引用初始化:为静态变量赋上真正的值注意:主类在运行时如果使用到其他类,这些类会被逐步加载,jar包和war包中的类并不是一次性都加载出来的,而是真正使用到这个类的时候才会进行加载
package com.muzili.jvm; public class TestDynamicLoad { public static String initDate="test"; static { System.out.println("运行TestDynamicLoad类的静态方法....."); } public TestDynamicLoad(){ System.out.println("运行TestDynamicLoad类的构造方法....."); } public static class InnerClassA{ static { System.out.println("运行InnerClassA类的静态方法....."); } public InnerClassA(){ System.out.println("运行InnerClassA类的构造方法....."); } } public static void main(String[] args) { TestDynamicLoad testDynamicLoad=new TestDynamicLoad(); InnerClassA innerClassA=new InnerClassA(); } } 结果输出: 运行TestDynamicLoad类的静态方法..... 运行TestDynamicLoad类的构造方法..... 运行InnerClassA类的静态方法..... 运行InnerClassA类的构造方法..... Process finished with exit code 0Java中的类加载器主要有三种:BootstrapClassLoader(引导类加载器)、ExtClassLoader(扩展类加载器)、AppClassLoader(应用程序类加载器)。
BootstrapClassLoader:主要用于加载Java程序运行时必不可少的核心类,如jre/lib/rt.jar、jre/lib/charsets.jar中的类。ExtClassLoader:用于加载jre/lib/ext下的所有jar包中的类AppClassLoader:用于加载自己编写的类 package com.muzili.jvm; import com.sun.crypto.provider.DESKeyFactory; import sun.misc.Launcher; import java.net.URL; public class TestJDKClassLoader { public static void main(String[] args) { /** * 输出以下类的ClassLoader */ System.out.println(String.class.getClassLoader()); System.out.println(DESKeyFactory.class.getClassLoader()); System.out.println(TestJDKClassLoader.class.getClassLoader()); System.out.println(); /** * 获取ClassLoader */ //因为BootStrapClassLoader是通过C++实现,并实例化的故Java程序无法获取 ClassLoader appClassLoader=TestJDKClassLoader.class.getClassLoader(); ClassLoader extClassLoader=appClassLoader.getParent(); ClassLoader bootstrapClassLoader=extClassLoader.getParent(); System.out.println("the BootStrapClassLoader:"+bootstrapClassLoader); System.out.println("the ExtClassLoader:"+extClassLoader); System.out.println("the AppClassLoader:"+appClassLoader); System.out.println(); URL[] bootStrapUrls = Launcher.getBootstrapClassPath().getURLs(); System.out.println("BootStrapLoader加载:"); for (URL url : bootStrapUrls) { System.out.println(url.getPath()); } System.out.println(); System.out.println("ExtClassLoader加载:"); System.out.println(System.getProperty("java.ext.dirs")); System.out.println(); System.out.println("AppClassLoader加载:"); System.out.println(System.getProperty("java.class.path")); } } 结果输出: null sun.misc.Launcher$ExtClassLoader@4b67cf4d sun.misc.Launcher$AppClassLoader@18b4aac2 the BootStrapClassLoader:null the ExtClassLoader:sun.misc.Launcher$ExtClassLoader@4b67cf4d the AppClassLoader:sun.misc.Launcher$AppClassLoader@18b4aac2 BootStrapLoader加载: /E:/Program%20Files/Java/jdk1.8.0_191/jre/lib/resources.jar /E:/Program%20Files/Java/jdk1.8.0_191/jre/lib/rt.jar /E:/Program%20Files/Java/jdk1.8.0_191/jre/lib/sunrsasign.jar /E:/Program%20Files/Java/jdk1.8.0_191/jre/lib/jsse.jar /E:/Program%20Files/Java/jdk1.8.0_191/jre/lib/jce.jar /E:/Program%20Files/Java/jdk1.8.0_191/jre/lib/charsets.jar /E:/Program%20Files/Java/jdk1.8.0_191/jre/lib/jfr.jar /E:/Program%20Files/Java/jdk1.8.0_191/jre/classes ExtClassLoader加载: E:\Program Files\Java\jdk1.8.0_191\jre\lib\ext;C:\WINDOWS\Sun\Java\lib\ext AppClassLoader加载: E:\Program Files\Java\jdk1.8.0_191\jre\lib\charsets.jar;E:\Program Files\Java\jdk1.8.0_191\jre\lib\deploy.jar;E:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\access-bridge-64.jar;E:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\cldrdata.jar;E:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\dnsns.jar;E:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\jaccess.jar;E:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\jfxrt.jar;E:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\localedata.jar;E:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\nashorn.jar;E:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\sunec.jar;E:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\sunjce_provider.jar;E:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\sunmscapi.jar;E:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\sunpkcs11.jar;E:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\zipfs.jar;E:\Program Files\Java\jdk1.8.0_191\jre\lib\javaws.jar;E:\Program Files\Java\jdk1.8.0_191\jre\lib\jce.jar;E:\Program Files\Java\jdk1.8.0_191\jre\lib\jfr.jar;E:\Program Files\Java\jdk1.8.0_191\jre\lib\jfxswt.jar;E:\Program Files\Java\jdk1.8.0_191\jre\lib\jsse.jar;E:\Program Files\Java\jdk1.8.0_191\jre\lib\management-agent.jar;E:\Program Files\Java\jdk1.8.0_191\jre\lib\plugin.jar;E:\Program Files\Java\jdk1.8.0_191\jre\lib\resources.jar;E:\Program Files\Java\jdk1.8.0_191\jre\lib\rt.jar;D:\workspace\tulingxueyuan-demo\out\production\jvm-demo-01;E:\Program Files\JetBrains\IntelliJ IDEA 2019.1.1\lib\idea_rt.jar Process finished with exit code 0类加载过程解析:
通过jvm创建BootstrapClassLoader实例,调用sun.misc.Launcher#getLauncher()方法获取启动器对象【这里使用到了设计模式中的单例模式】Launcher类的构造方法中,创建了两个类加载器:sun.misc.Launcher.ExtClassLoader#getExtClassLoader[扩展类加载器]、sun.misc.Launcher.AppClassLoader#getAppClassLoader[应用程序类加载器]JVM默认会使用Launcher的getClassLoader()方法返回的类加载器AppClassLoader用于加载我们的类 // // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package sun.misc; import java.io.File; import java.io.IOException; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.net.URLClassLoader; import java.net.URLStreamHandler; import java.net.URLStreamHandlerFactory; import java.nio.file.Paths; import java.security.AccessControlContext; import java.security.AccessController; import java.security.CodeSource; import java.security.PermissionCollection; import java.security.PrivilegedAction; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import java.security.ProtectionDomain; import java.security.cert.Certificate; import java.util.HashSet; import java.util.StringTokenizer; import java.util.Vector; import sun.net.www.ParseUtil; public class Launcher { private static URLStreamHandlerFactory factory = new Launcher.Factory(); private static Launcher launcher = new Launcher(); private static String bootClassPath = System.getProperty("sun.boot.class.path"); private ClassLoader loader; private static URLStreamHandler fileHandler; public static Launcher getLauncher() { return 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); String var2 = System.getProperty("java.security.manager"); if (var2 != null) { SecurityManager var3 = null; if (!"".equals(var2) && !"default".equals(var2)) { try { var3 = (SecurityManager)this.loader.loadClass(var2).newInstance(); } catch (IllegalAccessException var5) { } catch (InstantiationException var6) { } catch (ClassNotFoundException var7) { } catch (ClassCastException var8) { } } else { var3 = new SecurityManager(); } if (var3 == null) { throw new InternalError("Could not create SecurityManager: " + var2); } System.setSecurityManager(var3); } } public ClassLoader getClassLoader() { return this.loader; } //省略一些其他代码 }双亲委派机制,就是在加载类的时候,级别较低的类加载器首先会委托它的父级类加载器进行加载,如果父级类加载器加载不到才会交给当前类加载器进行加载。
双亲委派机制有什么意义:
沙箱安全:它可以保证java中的核心类不会被用户定义的类给覆盖,从而保证了系统的安全防止类的重复加载:每个类加载器,都实现了缓存机制,当类第一次被加载之后就会被缓存起来,当再次加载的时候,就不用再每次都按照双亲委派机制来加载类,这样可以提升类的加载效率为什么需要自定义类加载器?
如果在实际开发中,可能有时我们想要加载一些外部目录下的类,这个时候jvm提供的类加载器就无法满足我们的需求了,这个时候我们需要自定义类加载器,用以加载外部目录中的类。
