搞清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加載的類不進行解析操作,不進行解析操作就意味着初始化也不會進行,那麼其類的靜態參數就不會初始化,靜態代碼塊也不會被執行。

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