1.通过ClassLoader对象的loadClass()方法
比如以下方式:
ClassLoader.getSystemClassLoader().loadClass("com.my.test.Hello")
去看一下loadClass的源码:
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
最终调用:
//可以看到参数resolve的解释,如果为true才会对这个类进行resolve
/* @param resolve
* If <tt>true</tt> then resolve the class
* */
protected 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();
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
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;
}
}
resolve其实是连接过程(验证verification,准备preparation,解析resolution),具体代码如下:
/**
* Links the specified class. This (misleadingly named) method may be
* used by a class loader to link a class. If the class <tt>c</tt> has
* already been linked, then this method simply returns. Otherwise, the
* class is linked as described in the "Execution" chapter of
* <cite>The Java™ Language Specification</cite>.
*
* @param c
* The class to link
*
* @throws NullPointerException
* If <tt>c</tt> is <tt>null</tt>.
*
* @see #defineClass(String, byte[], int, int)
*/
protected final void resolveClass(Class<?> c) {
resolveClass0(c);
}
private native void resolveClass0(Class<?> c);
可以看到,ClassLoader对象的loadClass()方法传入的resolve值为false,因此它不会对类进行连接这一过程,根据类的生命周期我们知道,如果一个类没有进行验证和准备的话,是无法进行初始化过程的,也就是说该方法的调用不会导致初始化<cinit>的执行。
2. 类名.class
Class a=Test.class
该种方式同样不会对类进行初始化。
3. Class.forName()
Class test = Class.forName("com.my.Test");
forName函数的源码如下:
public static Class<?> forName(String className)
throws ClassNotFoundException {
Class<?> caller = Reflection.getCallerClass();
return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}
forName0是一个native方法。调用forName(string)方法等同于调用Class.forName(className, true, currentLoader)方法。源码如下:
public static Class<?> forName(String name, boolean initialize,
ClassLoader loader)
throws ClassNotFoundException
{
Class<?> caller = null;
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
// Reflective call to get caller class is only needed if a security manager
// is present. Avoid the overhead of making this call otherwise.
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);
}
}
}
return forName0(name, initialize, loader, caller);
}
initialize为true表示会进行初始化initialization步骤,即静态初始化(会初始化类变量,静态代码块)。
这也就是为什么加载MySQL等一些数据库驱动程序的时候要使用这个方法的原因。比如MySQL的数据库驱动中有如下代码:
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}
需要使用forName方式加载使得静态代码块得以初始化。
4.object.getClass()
这个方法没什么好说的了,你通过一个对象调用其getClass()方法能获得其对应类的Class对象,既然对象已经存在了,那么说明这个类已经被装载到了虚拟机中。
public final native Class<?> getClass();