飞道的博客

类加载Class.forName和ClassLoader的区别

352人阅读  评论(0)

Class.forName

Class.forName(String className)和 Class.forName(String name, boolean initialize, ClassLoad loader)

直接上代码,用执行结果来辅助理解:
创建类ClassForName

package com.lyh.study.reflex;

/**
 * Created by liuyunhui on 2020-03-06 11:46.
 * description:
 */
public class ClassForName {
    //静态代码块
    static {
        System.out.println("静态代码块---今天周五了呦");
    }

    //静态变量
    public static String staticValue = defaultStaticValue();

    //给静态变量赋值
    private static String defaultStaticValue() {
        System.out.println("给静态变量赋值---明天就周六了呦");
        return "静态变量默认值";
    }

}

使用Class.forName(String className)

package com.lyh.study.test;

/**
 * Created by liuyunhui on 2019-05-20 10:23.
 * description:
 */
public class Test {
    public static void main(String[] args) {
        try {
            Class.forName("com.lyh.study.reflex.ClassForName");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

结果如下,类进行了初始化:

使用Class.forName(String name, boolean initialize, ClassLoad loader)

package com.lyh.study.test;

/**
 * Created by liuyunhui on 2019-05-20 10:23.
 * description:
 */
public class Test {
    public static void main(String[] args) {
        try {
            System.out.println("Class.forName(\"com.lyh.study.reflex.ClassForName\",false,Test.class.getClassLoader())执行结果:");
            Class.forName("com.lyh.study.reflex.ClassForName",false,Test.class.getClassLoader());
            System.out.println("执行结束");
            
            System.out.println("Class.forName(\"com.lyh.study.reflex.ClassForName\",true,Test.class.getClassLoader())执行结果:");
            Class.forName("com.lyh.study.reflex.ClassForName",true,Test.class.getClassLoader());
            System.out.println("执行结束");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

结果如下,参数initialize为true时类进行了初始化,为false:

源码中,参数initialize的注释为if {@code true} the class will be initialized.意思是如果参数为true,则加载的类将会被初始化。

读取两个方法的源码:

/**
     * Returns the {@code Class} object associated with the class or
     * interface with the given string name.  Invoking this method is
     * equivalent to:
     *
     * <blockquote>
     *  {@code Class.forName(className, true, currentLoader)}
     * </blockquote>
     *
     * where {@code currentLoader} denotes the defining class loader of
     * the current class.
     *
     * <p> For example, the following code fragment returns the
     * runtime {@code Class} descriptor for the class named
     * {@code java.lang.Thread}:
     *
     * <blockquote>
     *   {@code Class t = Class.forName("java.lang.Thread")}
     * </blockquote>
     * <p>
     * A call to {@code forName("X")} causes the class named
     * {@code X} to be initialized.
     *
     * @param      className   the fully qualified name of the desired class.
     * @return     the {@code Class} object for the class with the
     *             specified name.
     * @exception LinkageError if the linkage fails
     * @exception ExceptionInInitializerError if the initialization provoked
     *            by this method fails
     * @exception ClassNotFoundException if the class cannot be located
     */
    @CallerSensitive
    public static Class<?> forName(String className)
                throws ClassNotFoundException {
        Class<?> caller = Reflection.getCallerClass();
        return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
    }
/**
     * Returns the {@code Class} object associated with the class or
     * interface with the given string name, using the given class loader.
     * Given the fully qualified name for a class or interface (in the same
     * format returned by {@code getName}) this method attempts to
     * locate, load, and link the class or interface.  The specified class
     * loader is used to load the class or interface.  If the parameter
     * {@code loader} is null, the class is loaded through the bootstrap
     * class loader.  The class is initialized only if the
     * {@code initialize} parameter is {@code true} and if it has
     * not been initialized earlier.
     *
     * <p> If {@code name} denotes a primitive type or void, an attempt
     * will be made to locate a user-defined class in the unnamed package whose
     * name is {@code name}. Therefore, this method cannot be used to
     * obtain any of the {@code Class} objects representing primitive
     * types or void.
     *
     * <p> If {@code name} denotes an array class, the component type of
     * the array class is loaded but not initialized.
     *
     * <p> For example, in an instance method the expression:
     *
     * <blockquote>
     *  {@code Class.forName("Foo")}
     * </blockquote>
     *
     * is equivalent to:
     *
     * <blockquote>
     *  {@code Class.forName("Foo", true, this.getClass().getClassLoader())}
     * </blockquote>
     *
     * Note that this method throws errors related to loading, linking or
     * initializing as specified in Sections 12.2, 12.3 and 12.4 of <em>The
     * Java Language Specification</em>.
     * Note that this method does not check whether the requested class
     * is accessible to its caller.
     *
     * <p> If the {@code loader} is {@code null}, and a security
     * manager is present, and the caller's class loader is not null, then this
     * method calls the security manager's {@code checkPermission} method
     * with a {@code RuntimePermission("getClassLoader")} permission to
     * ensure it's ok to access the bootstrap class loader.
     *
     * @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
     * @return           class object representing the desired class
     *
     * @exception LinkageError if the linkage fails
     * @exception ExceptionInInitializerError if the initialization provoked
     *            by this method fails
     * @exception ClassNotFoundException if the class cannot be located by
     *            the specified class loader
     *
     * @see       java.lang.Class#forName(String)
     * @see       java.lang.ClassLoader
     * @since     1.2
     */
    @CallerSensitive
    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);
    }

可以看出两个方法最终都执行的是forName0方法,使用Class.forName(String name)方法时,forName0的参数initialize默认传入true。

ClassLoader

package com.lyh.study.test;

/**
 * Created by liuyunhui on 2019-05-20 10:23.
 * description:
 */
public class Test {
    public static void main(String[] args) {
        try {
            ClassLoader.getSystemClassLoader().loadClass("com.lyh.study.reflex.ClassForName");
            System.out.println("执行结束");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

执行结果:

并没有执行静态代码块,说明ClassLoader只是加载了类,并没有对类进行初始化

JVM类加载的步骤


加载:通过类的全限定名获取到类的二进制流,然后加载到JVM中
验证:确保Class文件的字节流中包含的信息符合虚拟机的要求,并且不会威胁虚拟机的安全
准备:为类变量分配内存空间并设置类变量初始值
解析:虚拟机常量池的符号引用替换为字节引用过程
初始化:根据用户指定的代码初始化字段和其他资源,执行static块

应用场景

1.Spring框架的IOC实现就是使用的ClassLoader,我们知道 Spring 的 IOC 中有一个懒加载(延迟加载),如果使用了 Class.forName,那么懒加载这个功能就无法实现了。Spring IOC 为了加快初始化速度,因此大量使用了延时加载技术。而使用 ClassLoader 不需要执行类中的初始化代码,可以加快加载速度,把类的初始化工作留到实际使用到这个类的时候。
2.使用JDBC时通常用Class.forName()来加载数据库连接驱动,这是因为在JDBC规范中明确要求Driver(数据库驱动)类必须向DriverManager注册自己,通过源码我们可以看到Driver注册到DriverManager中的操作写在了静态代码块中。
Driver源码:

package com.mysql.jdbc;

import com.mysql.jdbc.NonRegisteringDriver;
import java.sql.DriverManager;
import java.sql.SQLException;

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    public Driver() throws SQLException {
    }

    static {
        try {
            DriverManager.registerDriver(new Driver());
        } catch (SQLException var1) {
            throw new RuntimeException("Can\'t register driver!");
        }
    }
}

转载:https://blog.csdn.net/weixin_45661382/article/details/104694926
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场