1、Class.forName()和ClassLoader.loadClass()和new XX的区别

Class.forName():将类的.class文件加载到jvm中之外,还会对类进行解释,执行类中的static代码块

ClassLoader.loadClass():只会将.class文件加载到jvm中,不会执行static中的内容,只有在newInstance才会去执行static块。

《深入理解Java虚拟机》p214讲类的加载过程:加载、验证、准备、解析和初始化。

其中

加载:主要在内存中生成class文件对应的Class对象,作为方法区这个类各种数据的访问入口。

验证:验证Class文件的字节流中的信息符合当前虚拟机的要求,并且不会危害虚拟机自身安全。

准备:为类变量分配内存并设置类变量的初始值。

解析:常量池中的符号引用替换为直接引用。

初始化:初始化阶段才开始执行类中定义的Java程序代码。

我们看Class.forName源码

    @CallerSensitivepublic 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*/@CallerSensitivepublic 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);}

提示我们第二个参数表示是否初始化,看java参考手册

https://docs.oracle.com/javase/specs/jls/se8/html/jls-12.html#jls-12.4

new可以理解粗略的理解为:   【加载class文件到jvm + 初始化static代码块】(Class.forName) +构造实例(newInstance)

 /*** Creates a new instance of the class represented by this {@code Class}* object.  The class is instantiated as if by a {@code new}* expression with an empty argument list.  The class is initialized if it* has not already been initialized.** <p>Note that this method propagates any exception thrown by the* nullary constructor, including a checked exception.  Use of* this method effectively bypasses the compile-time exception* checking that would otherwise be performed by the compiler.* The {@link* java.lang.reflect.Constructor#newInstance(java.lang.Object...)* Constructor.newInstance} method avoids this problem by wrapping* any exception thrown by the constructor in a (checked) {@link* java.lang.reflect.InvocationTargetException}.** @return  a newly allocated instance of the class represented by this*          object.* @throws  IllegalAccessException  if the class or its nullary*          constructor is not accessible.* @throws  InstantiationException*          if this {@code Class} represents an abstract class,*          an interface, an array class, a primitive type, or void;*          or if the class has no nullary constructor;*          or if the instantiation fails for some other reason.* @throws  ExceptionInInitializerError if the initialization*          provoked by this method fails.* @throws  SecurityException*          If a security manager, <i>s</i>, is present and*          the caller's class loader is not the same as or an*          ancestor of the class loader for the current class and*          invocation of {@link SecurityManager#checkPackageAccess*          s.checkPackageAccess()} denies access to the package*          of this class.*/@CallerSensitivepublic T newInstance()throws InstantiationException, IllegalAccessException{if (System.getSecurityManager() != null) {checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), false);}// NOTE: the following code may not be strictly correct under// the current Java memory model.// Constructor lookupif (cachedConstructor == null) {if (this == Class.class) {throw new IllegalAccessException("Can not call newInstance() on the Class for java.lang.Class");}try {Class<?>[] empty = {};final Constructor<T> c = getConstructor0(empty, Member.DECLARED);// Disable accessibility checks on the constructor// since we have to do the security check here anyway// (the stack depth is wrong for the Constructor's// security check to work)java.security.AccessController.doPrivileged(new java.security.PrivilegedAction<Void>() {public Void run() {c.setAccessible(true);return null;}});cachedConstructor = c;} catch (NoSuchMethodException e) {throw (InstantiationException)new InstantiationException(getName()).initCause(e);}}Constructor<T> tmpConstructor = cachedConstructor;// Security check (same as in java.lang.reflect.Constructor)int modifiers = tmpConstructor.getModifiers();if (!Reflection.quickCheckMemberAccess(this, modifiers)) {Class<?> caller = Reflection.getCallerClass();if (newInstanceCallerCache != caller) {Reflection.ensureMemberAccess(caller, this, null, modifiers);newInstanceCallerCache = caller;}}// Run constructortry {return tmpConstructor.newInstance((Object[])null);} catch (InvocationTargetException e) {Unsafe.getUnsafe().throwException(e.getTargetException());// Not reachedreturn null;}}

其中根据newInstance是创建一个代表这个类对象的实例,如果没有初始化会触发初始化。

代码的主要逻辑是查找无参数构造方法,然后通过反射来调用构造实例。

写个测试类验证一下:

/*** 加载测试demo类* * @author: 明明如月 liuwangyanghdu@163.com* @date: 2019-04-09 12:43*/
public class LoadTestClass {static {System.out.println("静态代码块");}public LoadTestClass() {System.out.println("构造方法");}public static void test() {System.out.println("这是静态方法");}
}

测试类


/*** 加载测试类** @author: 明明如月 liuwangyanghdu@163.com* @date: 2019-04-09 12:42*/
public class LoadTest {@Testpublic void test(){LoadTestClass.test();}@Testpublic void forName() throws ClassNotFoundException {Class<?> aClass = Class.forName("com.chujianyun.common.clazz.LoadTestClass");System.out.println(aClass);}@Testpublic void newTest() {new LoadTestClass();}@Testpublic void loader() throws ClassNotFoundException {Class<?> aClass = ClassLoader.getSystemClassLoader().loadClass("com.chujianyun.common.clazz.LoadTestClass");System.out.println(aClass);}@Testpublic void loaderNewInstance() throws ClassNotFoundException, IllegalAccessException, InstantiationException {Class<?> aClass = ClassLoader.getSystemClassLoader().loadClass("com.chujianyun.common.clazz.LoadTestClass");System.out.println(aClass);Object result = aClass.newInstance();System.out.println(result instanceof LoadTestClass);}
}

测试类分别输出:

另外可以看到调用静态方法前,会触发静态代码块的调用(也会触发类的加载)。

二、为什么加载数据库驱动要用Class.forName()?

其实JDBC4.0以后(mysql-connector-java 5.1.6之后) + java6以后,不再需要显示调用Class.forName()加载驱动了。

下面是摘录的一段话,简单明了:

JDBC 4.0的特性
得益于Mustang中的Java SE 服务提供商机制,Java开发人员再也不必用类似Class.forName() 的代码注册JDBC驱动来明确加载JDBC。当调用DriverManager.getConnection()方法时,DriverManager类将 自动设置合适的驱动程序。该特性向后兼容,因此无需对现有的JDBC代码作任何改动。

https://www.ibm.com/developerworks/cn/java/j-lo-jse65/#N100EE

JDBC 4.0 的规范规定,所有 JDBC 4.0 的驱动 jar 文件必须包含一个 java.sql.Driver,它位于 jar 文件的 META-INF/services 目录下

接下来详细展开:

以mysql驱动 8.0.11为例,采用了SPI机制(这里不展开,详细了解可参考这篇文章:https://juejin.im/post/5af952fdf265da0b9e652de3)

Java SPI 实际上是“基于接口的编程+策略模式+配置文件”组合实现的动态加载机制。

使用时可以这么写:

String url = "jdbc:mysql://localhost:3306/test";
Connection conn = DriverManager.getConnection(url,username,password);
package com.mysql.cj.jdbc;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!");}}
}

驱动的类静态代码块中,调用DriverManager的注册驱动方法new一个自己当参数传给驱动管理器。

 public static synchronized void registerDriver(java.sql.Driver driver)throws SQLException {registerDriver(driver, null);}

另外最关键的是,驱动管理器的静态代码块有加载初始化驱动的方法

/*** Load the initial JDBC drivers by checking the System property* jdbc.properties and then use the {@code ServiceLoader} mechanism*/
static {loadInitialDrivers();println("JDBC DriverManager initialized");
}
   private static void loadInitialDrivers() {String drivers;try {drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {public String run() {return System.getProperty("jdbc.drivers");}});} catch (Exception ex) {drivers = null;}// If the driver is packaged as a Service Provider, load it.// Get all the drivers through the classloader// exposed as a java.sql.Driver.class service.// ServiceLoader.load() replaces the sun.misc.Providers()AccessController.doPrivileged(new PrivilegedAction<Void>() {public Void run() {ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);Iterator<Driver> driversIterator = loadedDrivers.iterator();/* Load these drivers, so that they can be instantiated.* It may be the case that the driver class may not be there* i.e. there may be a packaged driver with the service class* as implementation of java.sql.Driver but the actual class* may be missing. In that case a java.util.ServiceConfigurationError* will be thrown at runtime by the VM trying to locate* and load the service.** Adding a try catch block to catch those runtime errors* if driver not available in classpath but it's* packaged as service and that service is there in classpath.*/try{while(driversIterator.hasNext()) {driversIterator.next();}} catch(Throwable t) {// Do nothing}return null;}});println("DriverManager.initialize: jdbc.drivers = " + drivers);if (drivers == null || drivers.equals("")) {return;}String[] driversList = drivers.split(":");println("number of Drivers:" + driversList.length);for (String aDriver : driversList) {try {println("DriverManager.Initialize: loading " + aDriver);Class.forName(aDriver, true,ClassLoader.getSystemClassLoader());} catch (Exception ex) {println("DriverManager.Initialize: load failed: " + ex);}}}

先查找 jdbc.drivers 属性的指,然后SPI机制查找驱动

ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);

可以研究下ServiceLoader的源码

可以看到加载META-INF/services/ 文件夹下类名为文件名(这里相当于Driver.class.getName())的资源,然后将其加载到虚拟机。

注释有这么一句“Load these drivers, so that they can be instantiated.” 意思是加载SPI扫描到的驱动来触发他们的初始化。既触发他们的static代码块,既

    /*** Registers the given driver with the {@code DriverManager}.* A newly-loaded driver class should call* the method {@code registerDriver} to make itself* known to the {@code DriverManager}. If the driver is currently* registered, no action is taken.** @param driver the new JDBC Driver that is to be registered with the*               {@code DriverManager}* @param da     the {@code DriverAction} implementation to be used when*               {@code DriverManager#deregisterDriver} is called* @exception SQLException if a database access error occurs* @exception NullPointerException if {@code driver} is null* @since 1.8*/public static synchronized void registerDriver(java.sql.Driver driver,DriverAction da)throws SQLException {/* Register the driver if it has not already been added to our list */if(driver != null) {registeredDrivers.addIfAbsent(new DriverInfo(driver, da));} else {// This is for compatibility with the original DriverManagerthrow new NullPointerException();}println("registerDriver: " + driver);}

将自己注册到 驱动管理器的驱动列表中

然后调用驱动管理器的获取连接方法时从这里列表(registeredDrivers)中取

Connection conn = DriverManager.getConnection(url,username,password);

调用 Url,用户名和密码三个参数的获取连接的方法

@CallerSensitivepublic static Connection getConnection(String url,String user, String password) throws SQLException {java.util.Properties info = new java.util.Properties();if (user != null) {info.put("user", user);}if (password != null) {info.put("password", password);}return (getConnection(url, info, Reflection.getCallerClass()));}

具体又调用如下私有方法

 //  Worker method called by the public getConnection() methods.private static Connection getConnection(String url, java.util.Properties info, Class<?> caller) throws SQLException {/** When callerCl is null, we should check the application's* (which is invoking this class indirectly)* classloader, so that the JDBC driver class outside rt.jar* can be loaded from here.*/ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;synchronized(DriverManager.class) {// synchronize loading of the correct classloader.if (callerCL == null) {callerCL = Thread.currentThread().getContextClassLoader();}}if(url == null) {throw new SQLException("The url cannot be null", "08001");}println("DriverManager.getConnection(\"" + url + "\")");// Walk through the loaded registeredDrivers attempting to make a connection.// Remember the first exception that gets raised so we can reraise it.SQLException reason = null;for(DriverInfo aDriver : registeredDrivers) {// If the caller does not have permission to load the driver then// skip it.if(isDriverAllowed(aDriver.driver, callerCL)) {try {println("    trying " + aDriver.driver.getClass().getName());Connection con = aDriver.driver.connect(url, info);if (con != null) {// Success!println("getConnection returning " + aDriver.driver.getClass().getName());return (con);}} catch (SQLException ex) {if (reason == null) {reason = ex;}}} else {println("    skipping: " + aDriver.getClass().getName());}}// if we got here nobody could connect.if (reason != null)    {println("getConnection failed: " + reason);throw reason;}println("getConnection: no suitable driver found for "+ url);throw new SQLException("No suitable driver found for "+ url, "08001");}

其中又判断调用者是否有权限加载驱动类,如果没有就忽略(利用Class.forName尝试加载,加载失败则忽略))从前往后找到第一个可以构造Connection的对象就返回。

    private static boolean isDriverAllowed(Driver driver, ClassLoader classLoader) {boolean result = false;if(driver != null) {Class<?> aClass = null;try {aClass =  Class.forName(driver.getClass().getName(), true, classLoader);} catch (Exception ex) {result = false;}result = ( aClass == driver.getClass() ) ? true : false;}return result;}

因此在调用

Connection conn = DriverManager.getConnection(url,username,password);

这个静态函数前, DriverManager的静态代码块已经被执行,既已经通过SPI机制讲驱动注入到驱动列表中,因此无需在之前调用一次Class.forName。

由于JDBC 4.0之前并没有采用SPI机制也没有用static代码块讲自己注册到驱动管理器的驱动列表中,另外配套的Java版本好像也比较低(是为了适配jdk5),对应的驱动管理器代码和现在也不一样。因此需要手动调用Class.forName来加载class文件到jvm并初始化。

JDK6之后提供了SPI机制,另外mysql-connector-java 5.1.6之后采用了SPI方式编写驱动。

以下是5.1.5版本的结构

5.1.6版本的结构

参考文章:

https://blog.csdn.net/w369033345/article/details/54173818

https://www.cnblogs.com/gaojing/archive/2012/03/15/2413638.html

https://blog.csdn.net/fengyuzhengfan/article/details/38086743

http://www.runoob.com/w3cnote/java-class-forname.html

一篇文章吃透:为什么加载数据库驱动要用Class.forName()相关推荐

  1. driver.class.php line: 109,thinkphp3.2.3 无法加载数据库驱动: Think\Db\Driver\

    这是我在Common conf config.php中的代码 return array( //'配置项'=>'配置值' $options = array( //数据库配置 'DB_TYPE' = ...

  2. java 手动加载数据库驱动_JAVA加载数据库驱动(JDBC)

    JAVA加载数据库驱动(JDBC) 前言 之前,对Class.forName("com.mysql.jdbc.Driver");这条动态加载JDBC驱动感觉很疑惑,故有了这篇短文. ...

  3. 报错解决:加载数据库驱动报错-Loading class `com.mysql.jdbc.Driver‘. The new driver class is `com.mysql.cj.jdb

    Loading class com.mysql.jdbc.Driver'. This is deprecated. The new driver class iscom.mysql.cj.jdbc.D ...

  4. 加载mysql驱动的语句_sql数据库驱动加载语句

    2011年11月27日学习内容总结 1.使用JDBC连接数据库五个步骤,每个步骤的原因.加载驱动:连接不同的数据库要加载不同的驱动:建立连接:访问一个数据库,要知道IP+端口号+数据库名,此外还有用户 ...

  5. 正确加载MySQL驱动的语句_使用数据库客户端工具Oracle SQL Developer加载第三方驱动连接mysql的方法...

    用Oracle SQL Developer时遇到no ocijdbc11 in java.library.path怎么办 不是配置环境变量,而是去选项里面,添加数据库的驱动 jdbc的驱动没有在cla ...

  6. Linux中mod相关的命令 内核模块化 mod相关命令都是用来动态加载内核模块/驱动程序模块...

    Linux中mod相关的命令 内核模块化   mod相关命令都是用来动态加载内核模块/驱动程序模块 http://baike.baidu.com/link?url=lxiKxFvYm-UfJIxMjz ...

  7. 使用DriverManager动态加载JDBC驱动时,报:java.sql.SQLException: No suitable driver found for xxxx 错误

    参考文章: yanbin.blog - 自定义类加载器动态加载 JDBC 驱动 项目场景: 使用DriverManager动态加载JDBC驱动时,报:java.sql.SQLException: No ...

  8. 正确加载MySQL驱动的语句_正确加载MySQL驱动的语句_____________________;_学小易找答案...

    [多选题]电子图书的特点是 [简答题] [单选题]为了准备病理学考试,王同学想在图书馆借一本人民卫生出版社出版的熊小亮编写的<病理学试题库>,经过检索发现该书都被借出去了 , 最方便快捷的 ...

  9. windows无法确定此计算机是否包含,服务器用U盘虚拟光驱装系统,有做RAID5,加载RAID驱动成功后,安装到最后会报“windows无法确定此计算机是否包含有效系统卷”错误...

    http://zhidao.baidu.com/link?url=bC5dyBz_W05bgeQOu0hF7BN7As_GjloE-Z8SeJBj8UWXZNt4eHeisP2Eo59nUwud23p ...

最新文章

  1. babel css3新特性_2018年面试前端总结
  2. 如何在业务层实现响应缓存
  3. a频繁连接不上redis_连接不到redis Caused by:..._慕课问答
  4. node服务器中打开html文件的两种方法
  5. jsp大作业_Servlet与JSP配合使用基本案例
  6. 7-3 算术入门之加减乘除 (10 分)
  7. 解决Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.1:compile (default-compile
  8. Process Monitor
  9. [bzoj1055][HAOI2008]玩具取名
  10. go语言 读文件最后一行_Go 语言核心文件调试
  11. 解决firefox一个邮箱两个账户导致收藏夹不同步的问题
  12. 如何设计高扩展的在线网页制作平台
  13. 带你了解什么叫大数据分析
  14. 光线CMS系统,如何显示幻灯片
  15. js视频封面(截帧)
  16. 调用百度智能云OCR接口识别身份证和户口本
  17. 微信小程序|做一个底部评论视图
  18. 小车自动往返工作原理_自动往返小汽车
  19. linux 查看系统配置
  20. 程序员不得不学的操作系统知识(二)

热门文章

  1. 微信小程序连接蓝牙ble教程(目录)
  2. Matlab 基础03 - Matlab的开源数据集- 数据集列表
  3. 【Java】实现计算器
  4. 示波器表笔旁边的夹子是什么_示波器探头的正确使用方法?
  5. qq互动视频页面加载失败_腾讯视频互动视频 | 创作指南
  6. Easypack之Alpine容器系列:Redmine
  7. Leetcode771:宝石与石头
  8. Redis做接口限流
  9. Clone Graph问题及解法
  10. 应用消息队列设计可以解决哪些实际问题?