Class.forName()用法详解

本文详细介绍了Class.forName()方法的功能及应用场景,对比了newInstance()与new关键字的区别,并解释了为何在加载数据库驱动时仅使用Class.forName()。
主要功能
Class.forName(xxx.xx.xx)返回的是一个类
Class.forName(xxx.xx.xx)的作用是要求JVM查找并加载指定的类,
也就是说JVM会执行该类的静态代码段

下面,通过解答以下三个问题的来详细讲解下Class.forName()的用法。
一.什么时候用Class.forName()?
先来个热身,给你一个字符串变量,它代表一个类的包名和类名,你怎么实例化它?你第一想到的肯定是new,但是注意一点:
A a = (A)Class.forName(“pacage.A”).newInstance();
这和你 A a = new A(); 是一样的效果。

现在言归正传。
动态加载和创建Class 对象,比如想根据用户输入的字符串来创建对象时需要用到:
String str = “用户输入的字符串” ;
Class t = Class.forName(str);
t.newInstance();

在初始化一个类,生成一个实例的时候,newInstance()方法和new关键字除了一个是方法,一个是关键字外,最主要有什么区别?它们的区别在于创建对象的方式不一样,前者是使用类加载机制,后者是创建一个新类。那么为什么会有两种创建对象方式?这主要考虑到软件的可伸缩、可扩展和可重用等软件设计思想。

Java中工厂模式经常使用newInstance()方法来创建对象,因此从为什么要使用工厂模式上可以找到具体答案。 例如:
class c = Class.forName(“Example”);
factory = (ExampleInterface)c.newInstance();

其中ExampleInterface是Example的接口,可以写成如下形式:
String className = “Example”;
class c = Class.forName(className);
factory = (ExampleInterface)c.newInstance();

进一步可以写成如下形式:
String className = readfromXMlConfig;//从xml 配置文件中获得字符串
class c = Class.forName(className);
factory = (ExampleInterface)c.newInstance();

上面代码已经不存在Example的类名称,它的优点是,无论Example类怎么变化,上述代码不变,甚至可以更换Example的兄弟类Example2 , Example3 , Example4……,只要他们继承ExampleInterface就可以。

从JVM的角度看,我们使用关键字new创建一个类的时候,这个类可以没有被加载。但是使用newInstance()方法的时候,就必须保证:
1、这个类已经加载;
2、这个类已经连接了。
而完成上面两个步骤的正是Class的静态方法forName()所完成的,这个静态方法调用了启动类加载器,即加载 java API的那个加载器。

现在可以看出,newInstance()实际上是把new这个方式分解为两步,即首先调用Class加载方法加载某个类,然后实例化。 这样分步的好处是显而易见的。我们可以在调用class的静态加载方法forName时获得更好的灵活性,提供给了一种降耦的手段。

二.new 和Class.forName()有什么区别?
其实上面已经说到一些了,这里来做个总结:
首先,newInstance( )是一个方法,而new是一个关键字;
其次,Class下的newInstance()的使用有局限,因为它生成对象只能调用无参的构造函数,而使用 new关键字生成对象没有这个限制。
简言之:
newInstance(): 弱类型,低效率,只能调用无参构造。
new: 强类型,相对高效,能调用任何public构造。
Class.forName(“”)返回的是类。
Class.forName(“”).newInstance()返回的是object 。
三.为什么在加载数据库驱动包的时候有用的是Class.forName( ),却没有调用newInstance( )?
在Java开发特别是数据库开发中,经常会用到Class.forName( )这个方法。
通过查询Java Documentation我们会发现使用Class.forName( )静态方法的目的是为了动态加载类。
通常编码过程中,在加载完成后,一般还要调用Class下的newInstance( )静态方法来实例化对象以便操作。因此,单单使用Class.forName( )是动态加载类是没有用的,其最终目的是为了实例化对象。

有数据库开发经验朋友会发现,为什么在我们加载数据库驱动包的时候有的却没有调用newInstance( )方法呢?
即有的jdbc连接数据库的写法里是Class.forName(xxx.xx.xx);而有一 些:Class.forName(xxx.xx.xx).newInstance(),为什么会有这两种写法呢?
刚才提到,Class.forName(“”);的作用是要求JVM查找并加载指定的类,首先要明白,java里面任何class都要装载在虚拟机上才能运行,而静态代码是和class绑定的,class装载成功就表示执行了你的静态代码了,而且以后不会再走这段静态代码了。
而我们前面也说了,Class.forName(xxx.xx.xx)的作用就是要求JVM查找并加载指定的类,如果在类中有静态初始化器的话,JVM必然会执行该类的静态代码段。
而在JDBC规范中明确要求这个Driver类必须向DriverManager注册自己,即任何一个JDBC Driver的 Driver类的代码都必须类似如下:
public class MyJDBCDriver implements Driver {
static {
DriverManager.registerDriver(new MyJDBCDriver());
}
}
既然在静态初始化器的中已经进行了注册,所以我们在使用JDBC时只需要Class.forName(XXX.XXX);就可以了。

相关英文参考文献如下:
we just want to load the driver to jvm only, but not need to user the instance of driver,
so call Class.forName(xxx.xx.xx) is enough, if you call Class.forName(xxx.xx.xx).newInstance(),
the result will same as calling Class.forName(xxx.xx.xx),
because Class.forName(xxx.xx.xx).newInstance() will load driver first,
and then create instance, but the instacne you will never use in usual,
so you need not to create it.

<think>我们正在讨论Java中的Class.forName方法。根据引用[1],Class.forNameJava反射机制中的重要方法,用于动态加载类并初始化类的字节码。它在框架开发和底层实现中常见,如JDBC驱动加载和动态代理。 引用[2]指出,Class.forName(xxx)返回的是一个类,调用.newInstance()后才会创建一个对象。它的作用是要求JVM查找并加载指定的类,并且会执行该类的静态代码段。 引用[3]强调,Class.forName用于装载类,而new是根据加载到内存中的类创建一个实例。它常用于需要根据字符串变量(代表类的全限定名)来实例化对象的情况。 引用[4]补充了细节:Class.forName(String name)生成指定类名的类对象(Class对象),等同于Class.forName(className, true, currentLoader),其中第二个参数true表示会进行类的初始化(即执行静态代码块)。同时,它描述了类加载过程:通过全限定名获取类的二进制字节流,并将字节流转化为方法区的运行时数据结构。特别提到,生成Class对象后,会执行静态块代码,如MySQL驱动的加载。 因此,我们可以总结: 1. Class.forName的主要作用是动态加载类到JVM中,并返回一个Class对象。 2. 在加载过程中,如果指定了初始化(默认情况),则会执行该类的静态初始化块(static块)和静态变量的初始化。 3. 通常,我们使用它来加载那些在编译时无法确定,但在运行时通过字符串形式给出的类。 下面,我将详细解释Class.forName的含义、使用方法和注意事项,并给出代码示例。 ### 1. Class.forName的基本用法 `Class.forName` 是一个静态方法,它有两种重载形式: - `Class<?> forName(String className)` - `Class<?> forName(String name, boolean initialize, ClassLoader loader)` 我们常用的是第一种形式,它等同于 `Class.forName(className, true, currentLoader)`,即会进行初始化(执行静态块),并使用当前类的类加载器。 示例:加载一个类并执行其静态块 ```java public class Test { static { System.out.println("静态初始化块执行"); } } public class Main { public static void main(String[] args) throws ClassNotFoundException { Class<?> clazz = Class.forName("Test"); } } ``` 运行Main类,输出:"静态初始化块执行"。 ### 2. 为什么需要Class.forName? 在Java中,类是在第一次使用时动态加载的。通常,我们通过new关键字创建一个对象时,类会被加载。但有时我们无法在编译时知道具体的类名,而是通过字符串形式在运行时获取。例如: - 数据库驱动加载:在JDBC中,我们使用`Class.forName("com.mysql.cj.jdbc.Driver")`来加载驱动,驱动的静态块中会向DriverManager注册自己。 - 框架中根据配置文件的类名动态加载类(如Spring框架的IoC容器)。 ### 3. 与newInstance()结合创建对象 通过Class.forName获得Class对象后,我们可以调用`newInstance()`方法来创建该类的实例(要求类必须有一个无参构造器): ```java Class<?> clazz = Class.forName("com.example.MyClass"); Object obj = clazz.newInstance(); // 相当于调用无参构造器 ``` 注意:从Java 9开始,`newInstance()`方法被标记为过时,推荐使用`clazz.getDeclaredConstructor().newInstance()`。 ### 4. 关于初始化 当我们使用`Class.forName(className, false, loader)`时,第二个参数为false,则不会执行静态初始化块。这在某些特殊场景下有用,比如我们只需要加载类而不希望触发静态块。 ### 5. 注意事项 - 类名必须是全限定名(包含包名),例如`java.lang.String`。 - 该方法会抛出`ClassNotFoundException`,因此需要处理异常。 - 初始化静态块时,如果静态块中有复杂的操作或异常,可能会导致加载失败。 ### 6. 代码示例:JDBC驱动加载 在JDBC中,我们通常这样加载驱动: ```java Class.forName("com.mysql.cj.jdbc.Driver"); ``` 这是因为在`Driver`类中有一个静态初始化块: ```java public class Driver extends NonRegisteringDriver implements java.sql.Driver { static { try { java.sql.DriverManager.registerDriver(new Driver()); } catch (SQLException E) { throw new RuntimeException("Can't register driver!"); } } // ... } ``` 当调用`Class.forName`时,静态块执行,将驱动注册到`DriverManager`中。 ### 总结 `Class.forName` 是Java反射机制中用于动态加载类的方法,它会执行类的静态初始化块(默认情况下)。主要应用场景包括: - 动态加载类(尤其当类名来自配置、字符串变量时) - 框架开发(如JDBC驱动加载、动态代理等) 但要注意,它只加载类并初始化静态部分,创建对象需要另外调用`newInstance()`(或通过构造器)。 §§相关问题§§ 1. JavaClass.forNameClassLoader.loadClass有什么区别? 2. 为什么JDBC驱动加载需要用Class.forName? 3. Java反射中,除了Class.forName,还有哪些方式可以获取Class对象? 4. Class.forName在加载类时会触发哪些类加载阶段? </think>### Java 中 `Class.forName()` 的含义详解 `Class.forName()` 是 **Java 反射机制的核心方法**,用于动态加载并初始化类的字节码。以下是其核心含义和原理: #### 一、核心作用 1. **动态加载类** 根据**字符串形式的全限定类名**(如 `"java.lang.String"`)查找并加载该类到 JVM 中,返回对应的 `Class` 对象[^1][^2]。 ```java Class<?> clazz = Class.forName("com.example.MyClass"); // 加载类 ``` 2. **触发类初始化**(默认行为) 默认情况下(即 `Class.forName(className)`),会**执行类的静态初始化块(static 代码块)和静态变量赋值**[^2][^4]。 例如 JDBC 驱动注册: ```java Class.forName("com.mysql.cj.jdbc.Driver"); // 触发 Driver 类中的 static { DriverManager.registerDriver(new Driver()); } ``` 3. **与 `new` 关键字的区别** - `Class.forName()` 仅**加载类并初始化静态成员**,但不创建对象实例[^2][^3]。 - 创建对象需额外调用 `clazz.newInstance()`(已废弃)或 `clazz.getConstructor().newInstance()`: ```java Object obj = clazz.getDeclaredConstructor().newInstance(); // 创建实例 ``` --- #### 二、关键参数解析 方法签名: `Class<?> forName(String name, **boolean initialize**, ClassLoader loader)` - **`initialize` 参数**(默认为 `true`) - `true`:执行类的静态初始化(static 块)[^4]。 - `false`:仅加载类到内存,**不执行静态初始化**(适用于避免副作用场景)。 - **`ClassLoader` 参数** 指定类加载器,默认使用当前类的加载器。 --- #### 三、典型应用场景 1. **JDBC 驱动加载** 通过静态块自动向 `DriverManager` 注册驱动[^1]: ```java Class.forName("com.mysql.cj.jdbc.Driver"); // 静态块中注册驱动 ``` 2. **动态扩展功能** 根据配置文件动态加载实现类(如插件化架构): ```java String className = config.getProperty("serviceImpl"); Service service = (Service) Class.forName(className).newInstance(); ``` 3. **框架开发** Spring、Hibernate 等框架利用此方法实现依赖注入和动态代理[^1]。 --- #### 四、注意事项 1. **必须处理 `ClassNotFoundException`** 当类不存在时抛出该异常: ```java try { Class<?> clazz = Class.forName("InvalidClassName"); } catch (ClassNotFoundException e) { // 处理找不到类的情况 } ``` 2. **性能影响** 反射操作比直接调用慢,高频场景需谨慎使用。 3. **替代 `newInstance()`** Java 9+ 推荐使用构造器创建实例: ```java Object obj = clazz.getDeclaredConstructor().newInstance(); ``` --- ### 总结 | **核心特性** | **说明** | |----------------------|--------------------------------------------------------------------------| | **动态加载** | 通过字符串类名加载类到 JVM | | **触发静态初始化** | 默认执行 static 代码块(可通过 `initialize=false` 关闭) | | **返回 Class 对象** | 获取类元信息(方法、字段等),为反射操作提供入口 | | **与实例化分离** | 加载类 ≠ 创建对象,需额外调用构造器 |
评论 27
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值