如上图所示,Java中的类的加载靠的是类加载器实现的,其中通过loadClass进行类加载的时候会经历:加载》》验证》》准备》》解析》》初始化
几个步骤。
注意:主类在运行时如果使用到其他类,这些类会被逐步加载,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 0
Java中的类加载器主要有三种:BootstrapClassLoader(引导类加载器)
、ExtClassLoader(扩展类加载器)
、AppClassLoader(应用程序类加载器)
。
jre/lib/rt.jar
、jre/lib/charsets.jar
中的类。jre/lib/ext
下的所有jar包中的类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
类加载过程解析:
BootstrapClassLoader
实例,调用sun.misc.Launcher#getLauncher()
方法获取启动器对象【这里使用到了设计模式中的单例模式】sun.misc.Launcher.ExtClassLoader#getExtClassLoader
[扩展类加载器]、sun.misc.Launcher.AppClassLoader#getAppClassLoader
[应用程序类加载器]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;
}
//省略一些其他代码
}
双亲委派机制,就是在加载类的时候,级别较低的类加载器首先会委托它的父级类加载器进行加载,如果父级类加载器加载不到才会交给当前类加载器进行加载。
双亲委派机制有什么意义:
为什么需要自定义类加载器?
如果在实际开发中,可能有时我们想要加载一些外部目录下的类,这个时候jvm提供的类加载器就无法满足我们的需求了,这个时候我们需要自定义类加载器,用以加载外部目录中的类。
package com.muzili.jvm;
import java.io.FileInputStream;
import java.io.IOException;
public class MyClassLoaderTest {
public static class MyClassLoader extends ClassLoader{
private String clasasPath;
public MyClassLoader(String clasasPath) {
this.clasasPath = clasasPath;
}
private byte[] loadByte(String name) throws IOException {
name = name.replace(".", "/");
FileInputStream fis=new FileInputStream(clasasPath+"/"+name+".class");
int len=fis.available();
byte[] bytes=new byte[len];
fis.read(bytes);
fis.close();
return bytes;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
byte[] bytes = this.loadByte(name);
return defineClass(name,bytes,0,bytes.length);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
public static void main(String[] args) {
//初始化自定义的类加载器
MyClassLoader myClassLoader=new MyClassLoader("D:\\test");
try {
Class<?> clazz = myClassLoader.loadClass("com.muzili.jvm.User1");
System.out.println(clazz.getClassLoader());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
运行结果:
com.muzili.jvm.MyClassLoaderTest$MyClassLoader@4554617c
Process finished with exit code 0
package com.muzili.jvm;
import java.io.FileInputStream;
import java.io.IOException;
public class MyClassLoaderTest2 {
public static class MyClassLoader extends ClassLoader{
private String clasasPath;
public MyClassLoader(String clasasPath) {
this.clasasPath = clasasPath;
}
private byte[] loadByte(String name) throws IOException {
name = name.replace(".", "/");
FileInputStream fis=new FileInputStream(clasasPath+"/"+name+".class");
int len=fis.available();
byte[] bytes=new byte[len];
fis.read(bytes);
fis.close();
return bytes;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
byte[] bytes = this.loadByte(name);
return defineClass(name,bytes,0,bytes.length);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/**
* 打破双亲委派机制
* @param name
* @return
* @throws ClassNotFoundException
*/
@Override
public Class<?> loadClass(String name ,boolean resolve) throws ClassNotFoundException {
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
//直接将双亲委派机制取消不做任何处理会导致java运行时非常重要的类无法加载
if (!name.startsWith("com.muzili.jvm")){
c=this.getParent().loadClass(name);
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
}
public static void main(String[] args) {
//初始化自定义的类加载器
MyClassLoader myClassLoader=new MyClassLoader("D:\\test");
try {
Class<?> clazz = myClassLoader.loadClass("com.muzili.jvm.User1");
System.out.println(clazz);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
结果输出:
class com.muzili.jvm.User1