作为一个稀有的Java妹子,所写的所有博客都只是当作自己的笔记,留下证据自己之前是有用心学习的~哈哈哈哈(如果有不对的地方,也请大家指出,不要悄悄咪咪的不告诉我)
ClassLoader
1.ClassLoader的作用
Class Loader是用来把.class字节码文件加载到jvm内存中的,也就是把.class文件加载为Class对象。
2.ClassLoader的分类
Java内置了三个重要的ClassLoader,分别是 BootstrapClassLoader、ExtensionClassLoader 和 AppClassLoader。
BootstrapClassLoader 负责加载 JVM 运行时核心类,这些类位于 JAVA_HOME/lib/rt.jar 文件中,我们常用内置库 java.xxx.* 都在里面,比如 java.util.、java.io.、java.nio.、java.lang. 等等。这个 ClassLoader 比较特殊,它是由 C 代码实现的,我们将它称之为「根加载器」。
ExtensionClassLoader 负责加载 JVM 扩展类,比如 swing 系列、内置的 js 引擎、xml 解析器 等等,这些库名通常以 javax 开头,它们的 jar 包位于 JAVA_HOME/lib/ext/*.jar 中,有很多 jar 包。
AppClassLoader 才是直接面向我们用户的加载器,它会加载 Classpath 环境变量里定义的路径中的 jar 包和目录。我们自己编写的代码以及使用的第三方 jar 包通常都是由它来加载的。
3.双亲委派模型
当类加载器接受到加载类的请求时,首先是查看当前类是否已经加载,未加载的话查找是否有父级类加载器,有的话把加载类的操作交给父类加载器来完成,最终加载操作会到BootstrapClassLoader,如果BootstrapClassLoader不能加载这类,则让ExtensionClassLoader加载,如果ExtensionClassLoader也不能加载,就让AppClassLoader加载。
这样加载的好处就是使得每个类只被加载一次,且重要的基础类,如String,如果用户自己定义一个String,加载后就会破坏程序,而双亲委派模型保证String只会被BootstrapClassLoader加载,自定义的String无法完成加载。
4.源码解析
public abstract class ClassLoader {
//每个ClassLoader都有自己的父级ClassLoader
private final ClassLoader parent;
//加载类方法
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// 检查类是否已被加载
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
//未被加载则查看父级加载器是否为null
if (parent != null) {
//不为空则把加载操作给父级类加载
c = parent.loadClass(name, false);
} else {
//为null说明此时已经是BootstrapClassLoader
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
//如果类为空,说明父级加载器不能加载此类,自己加载
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;
}
}
5.Class.forName()和ClassLoader.loadClass()的区别
这两者都是加载类的,把.class文件转换为Class对象加载到jvm内存中,区别在于Class.forName会加载并初始化类,而Class Loader.loadClass()只是加载类,等到有new操作或者其他初始化类的时候才会真正的初始化类,这里也说明了Java中的类加载是懒加载机制,并不是在启动时初始化所有的类,启动时只是加载所有的类。
Class.forName()也是会调用ClassLoader.loadClass(),本质上也是用过类加载器来完成加载类的操作。
//Class里有ClassLoader变量,记录该类的类加载器
private final ClassLoader classLoader;
public static Class<?> forName(String className)
throws ClassNotFoundException {
Class<?> caller = Reflection.getCallerClass();
//true就代表初始化类,参数列表中也有ClassLoader
return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}
举例说明Class.forName()会初始化类:
public class ClassLoaderDemo {
static {
System.out.println("init ClassLoaderDemo");
}
}
public class ClassLoaderDemoTest {
@Test
public void testClassLoaderDemo(){
try {
Class clazz = Class.forName("com.jdk_source_code.demo.util.ClassLoaderDemo");
System.out.println("########以下是采用类加载类加载类########");
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
systemClassLoader.loadClass("com.jdk_source_code.demo.util.ClassLoaderDemo");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
结果:
init ClassLoaderDemo
########以下是采用类加载类加载类########
可以看出,Class.forName()会初始化类,静态代码块也会执行,而ClassLoader.loadClass()不会初始化类,Spring的IOC是用的ClassLoader,而数据库驱动是用的Class.forName(“com.mysql.cj.jdbc.Driver”),因为JDBC规范中明确要求Driver(数据库驱动)类必须向DriverManager注册自己,而注册的代码是在静态方法块中。
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
// Register ourselves with the DriverManager
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}
public Driver() throws SQLException {
// Required for Class.forName().newInstance()
}
}