1 前言
类的生命周期,分为加载,连接(验证,准备,解析),初始化,使用,卸载这几个过程。
Class.forName
和ClassLoader.loadClass
都会执行加载过程,将指定的类加载到内存中供使用。
但加载后,在默认情况下:Class.forName
会初始化,ClassLoader.loadClass
在加载后什么都不会做。
2 Class.forName
java.lang.Class
中有两个forName方法,参数不同,都是调用了private方法forName0
:
private static native Class<?> forName0(String name, boolean initialize, ClassLoader loader, Class<?> caller) throws ClassNotFoundException;
参数:
- name:类的全名
- initialize:是否初始化
- loader:使用的类加载器
- caller:调用者的类
返回:使用loader加载的全名为name的类
forName(String className)
forName(String name, boolean initialize, ClassLoader loader)
2.1 仅指定className
public static Class<?> forName(String className)
throws ClassNotFoundException {
// 获取调用者的类
Class<?> caller = Reflection.getCallerClass();
// 使用调用者的调用者的ClassLoader加载,加载后对类进行初始化。
return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}
复制代码
2.2 同时指定ClassLoader
public static Class<?> forName(String name, boolean initialize,
ClassLoader loader)
throws ClassNotFoundException
{
Class<?> caller = null;
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
//有SecurityManager时,才需要获取调用者的类。
caller = Reflection.getCallerClass();
if (sun.misc.VM.isSystemDomainLoader(loader)) {
ClassLoader ccl = ClassLoader.getClassLoader(caller);
if (!sun.misc.VM.isSystemDomainLoader(ccl)) {
sm.checkPermission(
SecurityConstants.GET_CLASSLOADER_PERMISSION);
}
}
}
// 指定类全名,是否初始化,ClassLoader,调用者的类 调用forName0
return forName0(name, initialize, loader, caller);
}
复制代码
3 ClassLoader.loadClass
参数:
- name:类的全名
- resolve:加载后时候进行生命周期中的连接部分
返回:使用当前ClassLoader加载的全名为name的类
过程:
- 1 调用
findLoadedClass(name)
检查类是否已经加载,如果没有加载过,则继续; - 2 parent存在,根据双亲委派模型,调用
parent.loadClass(name, false)
,优先从parent中执行loadClass(但不进行连接); - 3 parent不存在,则调用
findBootstrapClassOrNull(name)
判断是否在Bootstrap Classloader中加载过; - 4 如果类仍未找到,则执行findClass查找类,findClass由ClassLoader的子类实现,如
java.net.URLClassLoader
; - 5 最后,根据
resolve
参数,确定是否执行连接。
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// 检查类是否已经加载
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
// 双亲委派,优先从parent中执行loadClass,但执行时,不进行连接。
if (parent != null) {
c = parent.loadClass(name, false);
} else {
// name是否在Bootstrap Classloader中加载过,是的话则返回该类。
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();
// 执行findClass查找类,findClass由ClassLoader的子类实现。
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;
}
}
复制代码
ClassLoader中还有一个不带resolve的loadClass方法,在类加载完后不执行连接:
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
复制代码
4 总结
Class.forName和ClassLoader.loadClass都会执行加载过程。不同的是,在类加载后:
- Class.forName:默认执行初始化,但可以指定;
- ClassLoader.loadClass:默认不做任何事(不连接,不初始化),但可以指定是否连接。