java中Class.forName方法的作用详解

本文详细解析了Java JDBC中Class.forName方法的作用,揭示其如何初始化指定的类,并通过实例说明Class.forName与DriverManager注册驱动的关系,为理解JDBC连接数据库提供了清晰的视角。

转载:https://www.jb51.net/article/117468.htm
前言
在做JAVA EE开发的过程中,更多的是使用框架来提高开发效率.越来越发现,之前很基础的一些东西,都忘记的差不多了.从今天开始慢慢的复习一下基础.今天在看JDBC的时候,就有一个有趣的地方,之前学的时候,也没在意.这个Class.forName究竟是什么鬼.

import com.mysql.jdbc.Driver;
  
import java.sql.*;
  
/**
 * @author honway.liu
 * @date 2016/12/8 下午11:07
 * @email gm100861@gmail.com
 * @blog http://linuxsogood.org
 */
public class JdbcDemo {
  
 public static void main(String[] args) throws SQLException, ClassNotFoundException {
 //低版本使用
//  String url = "jdbc:mysql://127.0.0.1:3306/mydb";
  //高版本使用
 String url = "jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false";

  String username = "root";
  String password = "redhat";
   //低版本使用
  //Class.forName("com.mysql.jdbc.Driver");
   //高版本使用
   Class.forName("com.mysql.cj.jdbc.Driver");
  Connection connection = DriverManager.getConnection(url, username, password);
  String sql = "SELECT * FROM msg";
  PreparedStatement prepareStatement = connection.prepareStatement(sql);
  ResultSet resultSet = prepareStatement.executeQuery();
  while(resultSet.next()){  //列出全部数据-调整
  String address = resultSet.getString("address");
  System.out.println(address);
  }
 }
}

其中第一步,搞的我有点想不通.为啥Class.forName传入了一段字符串之后,就知道我连接的数据库是mysql? 有点不科学啊.Class.forName到底做了啥.下面就开始到源码中,一探究竟.

@CallerSensitive
public static Class<?> forName(String className)
   throws ClassNotFoundException {
 Class<?> caller = Reflection.getCallerClass();
 return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}

发现它调用了forName0方法,继续跟踪再看看

private static native Class<?> forName0(String name, boolean initialize,
          ClassLoader loader,
          Class<?> caller)
 throws ClassNotFoundException;

native方法,源码也只能到此结束了.看下官方文档,怎么说吧.
https://docs.oracle.com/javase/7/docs/api/java/lang/Class.html#forName(java.lang.String)
发现官方文档,还是描述的很清楚的.

Returns the 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 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 loader is null, the class is loaded through the bootstrap class loader.
 The class is initialized only if the initialize parameter is true and if it has not been 
initialized earlier.

嗯,描述的还算是很清楚.返回一个给定类或者接口的一个Class对象,如果没有给定classloader,那么会使用根类加载器.如果initalize这个参数传了true,那么给定的类如果之前没有被初始化过,那么会被初始化.我们在JDBC第一步的时候,传入的参数是com.mysql.jdbc.Driver. 也就是说这个类会被初始化.我们看一下这个类里面的内容.

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!");
  }
 }
  
  
 public Driver() throws SQLException {
  // Required for Class.forName().newInstance()
 }
}

我们发现这个类也是超级简单的.一个构造函数和一个静态代码块.我们知道,类在初始化的时候,静态代码块的内容会被执行的.也就是说我们Class.forName和直接写DriverManager.registerDriver(new Driver)两者功能是等同的.我们换成这种写法.再试试看.

public class JdbcDemo {
  
 public static void main(String[] args) throws SQLException, ClassNotFoundException {
  String url = "jdbc:mysql://127.0.0.1:3306/mydb";
  String username = "root";
  String password = "redhat";
  //Class.forName("com.mysql.jdbc.Driver");
  DriverManager.registerDriver(new Driver());
  Connection connection = DriverManager.getConnection(url, username, password);
  String sql = "SELECT * FROM msg";
  PreparedStatement prepareStatement = connection.prepareStatement(sql);
  ResultSet resultSet = prepareStatement.executeQuery();
  resultSet.next();
  String address = resultSet.getString("address");
  System.out.println(address);
 }
}

发现代码,还是正常的执行了.
总结一下:
Class.forName方法的作用,就是初始化给定的类.而我们给定的MySQL的Driver类中,它在静态代码块中通过JDBC的DriverManager注册了一下驱动.我们也可以直接使用JDBC的驱动管理器注册mysql驱动.从而代替使用Class.forName.
好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对脚本之家的支持。

<think>我们正在讨论反射机制中的Class.forName方法。根据引用[1]和引用[2],Class.forNameJava反射中用于动态加载类的重要方法。它通过类的全限定名来加载对应的类,并返回该类的Class对象。 引用[3]和引用[4]指出,Class.forNameClassLoader.loadClass的区别在于,Class.forName默认会初始化类(即执类的静态代码块),而ClassLoader.loadClass会立即初始化类,直到类第一次被使用时才初始化。 引用[5]进一步说明,Class.forName调用时,其内部调用的forName0方法的第二个参数默认为true,表示加载类的同时进初始化。 因此,Class.forName的主要作用包括: 1. 动态加载类:在运时根据字符串形式的类名加载类。 2. 初始化类:在加载类的同时,执类的静态初始化块和静态变量的赋值。 下面是一个示例代码,展示Class.forName的使用: ```java public class ClassForNameExample { public static void main(String[] args) throws ClassNotFoundException { // 使用Class.forName加载类 Class<?> clazz = Class.forName("com.example.DemoClass"); System.out.println("类加载完成"); } } class DemoClass { static { System.out.println("DemoClass的静态初始化块执"); } } ``` 运上述代码,输出将是: ``` DemoClass的静态初始化块执 类加载完成 ``` 这是因为Class.forName在加载类的同时,执了静态初始化块。 总结Class.forName作用: - 动态加载类:在运时根据字符串类名获取Class对象。 - 初始化类:触发类的静态初始化代码(静态块和静态变量赋值)。 - 在需要立即初始化类的场景下(如加载数据库驱动时)非常有用。 需要注意,如果想初始化类,可以使用Class.forName(String name, boolean initialize, ClassLoader loader)方法,将initialize参数设置为false,或者使用ClassLoader.loadClass方法。 §§相关问题§§ 1. Class.forName在加载数据库驱动时为什么必须使用? 2. 如何避免Class.forName初始化类? 3. Class.forName和new关键字创建对象有什么区别? 4. 动态加载类有什么实际应用场景? 5. 使用Class.forName时常见的异常有哪些? </think>### Java反射机制中`Class.forName()`的作用详解 `Class.forName()`是Java反射机制的核心方法,主要用于**动态加载类**和**触发类的初始化**。其核心作用如下: #### 1. 动态加载类(核心功能) - 根据**全限定类名字符串**(如`"java.lang.String"`)在运时加载类 - 返回该类的`Class`对象引用,可用于后续反射操作 ```java // 示例:加载StringClass<?> stringClass = Class.forName("java.lang.String"); ``` #### 2. 触发类初始化(关键特性) - 默认情况下(即单参数方法)会执类的**静态初始化块**: ```java class Demo { static { System.out.println("静态初始化块执!"); } } // 加载时触发静态块 Class.forName("com.example.Demo"); // 输出:静态初始化块执! ``` - 完成静态变量的赋值操作 #### 3. 主要应用场景 1. **数据库驱动加载**(经典用例): ```java // JDBC驱动注册 Class.forName("com.mysql.cj.jdbc.Driver"); ``` 驱动类在静态块中向`DriverManager`注册自己[^1][^2] 2. **动态扩展机制**: ```java // 通过配置文件加载插件 String pluginClass = config.getProperty("plugin"); Class<?> clazz = Class.forName(pluginClass); Object plugin = clazz.newInstance(); ``` 3. **框架开发**: - Spring等框架的依赖注入 - ORM框架的实体类映射 #### 4. 与`ClassLoader.loadClass()`的区别 | 特性 | `Class.forName()` | `ClassLoader.loadClass()` | |---------------------|---------------------------|---------------------------| | 类初始化 | 默认执静态块 | 静态块 | | 执时机 | 立即初始化 | 延迟初始化 | | 方法参数 | 类名字符串 | 类名字符串+类加载器 | | 典型应用 | JDBC驱动加载 | OSGi模块化系统 | | 资源消耗 | 较高(含初始化开销) | 较低 | > 可通过重载方法控制初始化为: > `Class.forName(String name, boolean initialize, ClassLoader loader)` > 第二个参数为`false`时触发初始化[^3][^4][^5] #### 5. 注意事项 1. **类找到异常**:`ClassNotFoundException` 2. **初始化风险**:静态块中的代码可能抛出异常 3. **性能影响**:频繁动态加载会增加开销 4. **安全管理**:需配置相应的`SecurityManager`权限
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值