搞清Class.forName()和ClassLoader.loadClass()的区别

反射定义

在运行状态中,对任意一个类,都能知道这个类中所有的属性和方法;对于任意一个对象,都能调用它的任意一个方法和属性。

反射的使用过程中要基于Class对象,那通过Class.forName()或者ClassLoader.loadClass()获取Class有什么区别呢?

查看Class.forName()源码

    @CallerSensitive
    public static Class<?> forName(String className)
                throws ClassNotFoundException {
        //调用三个参数的forName方法
        return forName(className, true, VMStack.getCallingClassLoader());
    }
    ...
    ...
    /*
     * @param name       fully qualified name of the desired class
     * @param initialize if {@code true} the class will be initialized.
     *                   See Section 12.4 of <em>The Java Language Specification</em>.
     * @param loader     class loader from which the class must be loaded
     */
    @CallerSensitive
    public static Class<?> forName(String name, boolean initialize,
                                   ClassLoader loader)
        throws ClassNotFoundException
    {
        if (loader == null) {
            loader = BootClassLoader.getInstance();
        }
        Class<?> result;
        try {
            result = classForName(name, initialize, loader);
        } catch (ClassNotFoundException e) {
            Throwable cause = e.getCause();
            if (cause instanceof LinkageError) {
                throw (LinkageError) cause;
            }
            throw e;
        }
        return result;
    }

    /** Called after security checks have been made. */
    @FastNative
    static native Class<?> classForName(String className, boolean shouldInitialize,
            ClassLoader classLoader) throws ClassNotFoundException;

代码中调用Class.forName(String className)实际上会调到 forName(String name, boolean initialize,ClassLoader loader)方法
看方法注释,我们可以了解到第二个参数的含义是class是否将被初始化;
第三个参数传入的是VMStack.getCallingClassLoader(),这里根据方法注释(class loader from which the class must be loaded)可以知道VMStack.getCallingClassLoader()返回的是Class.forName(String className) 加载className类用的ClassLoader,由我的另一篇文章(《Android中ClassLoader双亲委托机制》)中介绍的那样,我们自己写的Android应用的代码都是PathClassLoader加载。所以说这里的第三个参数是PathClassLoader

重点是第二个参数,传入了true,也就是说类会被初始化
我们知道类的加载流程是:加载-链接-初始化-使用-卸载
在这里插入图片描述
类一旦被初始化了,类的静态变量就回被初始化,静态代码块就会被执行
写个例子测一下

public class TestBean {
    public static String param1 ="testBean";
    static {
        Log.i("himi",param1+",静态代码块被加载");
    }
}
	@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        try {
            Class<?> aClass = Class.forName("com.himi.TestBean");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

输出:

I/himi: testBean,静态代码块被加载

查看ClassLoader.loadClass()源码

public Class<?> loadClass(String name) throws ClassNotFoundException {
        return loadClass(name, false);
}
...
...
/**
 * @param  resolve 
 * If <tt>true</tt> then resolve the class
*/
protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
            // First, check if the class has already been loaded
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                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.
                    c = findClass(name);
                }
            }
            return c;
    }

loadClass(String name)方法调用了loadClass(String name, boolean resolve),resolve为false,即为通过ClassLoader.loadClass加载的类不进行解析操作,不进行解析操作就意味着初始化也不会进行,那么其类的静态参数就不会初始化,静态代码块也不会被执行。
再来看看代码:

	@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        try {
            //Class<?> aClass = Class.forName("com.himi.TestBean");
             Class<?> aClass = getClassLoader().loadClass("com.himi.TestBean");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

控制台不会打印“静态代码块被加载”。

总结

Class.forName()加载的类会被初始化,类中的静态成员变量会被初始化,静态代码块会被执行
通过ClassLoader.loadClass加载的类不进行解析操作,不进行解析操作就意味着初始化也不会进行,那么其类的静态参数就不会初始化,静态代码块也不会被执行。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章