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