load/find class 与 forname 在static代码块加载的不同
https://blog.csdn.net/qq_38312744/article/details/80170599
一些文章中说,forname加载static代码块,loadclass不加载,不尽准确
做一些测试,jdk1.8
public class A { static { System.out.println("A 加载"); } public static void teststatic() { ; } public A() { // 隐式加载B B b = new B(); // 打印B的类加载器 System.out.println("B:" + b.getClass().getClassLoader()); } }
1) 直接new
A a = new A();
A 加载
2)forName
Class CA = Class.forName("lc.A");
A 加载
3)loadClass
Class CA = Thread.currentThread().getContextClassLoader().loadClass("lc.A");
无输出,未激活static代码块
4)loadClass+newInstance
Class CA = Thread.currentThread().getContextClassLoader().loadClass("lc.A"); CA.newInstance();
A 加载
实例化激活static代码块
5)loadClass+静态方法
Class CA0 = Thread.currentThread().getContextClassLoader().loadClass("lc.A"); Method method = CA0.getMethod("teststatic"); method.invoke(null); // 会触发static代码块
A加载
反射调用静态方法激活static代码块
6)自定义加载器+loadClass
String dir = "file:/Users/sunyuming/Downloads/jars/MyTest-1.0-SNAPSHOT.jar"; URL url = new URL(dir); URL[] urls2 = {url}; MyUrlClassLoader myUrlClassLoader = new MyUrlClassLoader(urls2); myUrlClassLoader.loadClass("lc.A");
无输出,loadclass未激活static代码块
7) 自定义加载器+laodClass+newInstance
String dir = "file:/Users/sunyuming/Downloads/jars/MyTest-1.0-SNAPSHOT.jar"; URL url = new URL(dir); URL[] urls2 = {url}; MyUrlClassLoader myUrlClassLoader = new MyUrlClassLoader(urls2); Class CA = myUrlClassLoader.loadClass("lc.A"); CA.newInstance();
A 加载
实例化激活static代码块
8)自定义加载器+findClass+newInstance
Class CA0 = Thread.currentThread().getContextClassLoader().loadClass("lc.A"); CA0.newInstance(); String dir = "file:/Users/sunyuming/Downloads/jars/MyTest-1.0-SNAPSHOT.jar"; URL url = new URL(dir); URL[] urls2 = {url}; MyUrlClassLoader myUrlClassLoader = new MyUrlClassLoader(urls2); Class CA = myUrlClassLoader.findClass("lc.A"); CA.newInstance();
A 加载
A加载
findclass绕开双亲委派(因为属于两个类,classpath下一个,jars/中一个
9)完整
A a = new A(); System.out.println(1); Class CA_1 = Class.forName("lc.A"); System.out.println(2); Class CA0 = Thread.currentThread().getContextClassLoader().loadClass("lc.A"); CA0.newInstance(); System.out.println(3); String dir = "file:/Users/sunyuming/Downloads/jars/MyTest-1.0-SNAPSHOT.jar"; URL url = new URL(dir); URL[] urls2 = {url}; MyUrlClassLoader myUrlClassLoader = new MyUrlClassLoader(urls2); Class CA = myUrlClassLoader.findClass("lc.A"); CA.newInstance();
A 加载
B:sun.misc.Launcher$AppClassLoader@5e481248
1
2
B:sun.misc.Launcher$AppClassLoader@5e481248
3
A 加载
B:sun.misc.Launcher$AppClassLoader@5e481248
因此:
=========================================================
再来看jdbc driver
Class.forName("com.mysql.jdbc.Driver"); conn = DriverManager.getConnection(URL, USER, PASSWORD);
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!"); } } }
经过调试,执行顺序为:
Class.forName("com.mysql.jdbc.Driver");
DriverManager.registerDriver(new Driver());
conn = DriverManager.getConnection(URL, USER, PASSWORD);
换为:
Thread.currentThread().getContextClassLoader().loadClass("com.mysql.jdbc.Driver"); conn = DriverManager.getConnection(URL, USER, PASSWORD);
经过调试,执行顺序为:
Thread.currentThread().getContextClassLoader().loadClass("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection(URL, USER, PASSWORD);
DriverManager.registerDriver(new Driver());
换为:
// Class.forName("com.mysql.jdbc.Driver"); // JdbcUtil.class.getClassLoader().loadClass("com.mysql.jdbc.Driver"); /** * 以上两句本例都不需要 * DriverMagager的静态函数getConnection激发DriverManager的静态代码块,在其中 * 扫描当前线程类加载器中一切符合jdbc SPI规范的驱动类,然后加载并实例化 */ conn = DriverManager.getConnection(URL, USER, PASSWORD);
居然也可以,既不用forName,又不用loadClass也可以;
这与我们之前的结论不一致,new 与 forname直接执行static代码块,load/findclass直到真正实例化或调用静态函数时执行static代码块
2020.1.10 我们未曾调用过com.mysql.jdbc.Driver的静态函数或实例化,这个代码能用是因为别的原因:
DriverManager.getConnection 调用DriverManager的静态函数,进而激发DriverManager的static代码块:
static { loadInitialDrivers(); println("JDBC DriverManager initialized"); }
在这个里面做了些文章,更详细的,看:JDBC SPI 类加载机制
=======================================================================
- 两者最大的区别
Class.forName得到的class是已经初始化完成的
Classloder.loaderClass得到的class是还没有链接的
https://www.cnblogs.com/suibianle/p/6676215.html
有些情况是只需要知道这个类的存在而不需要初始化的情况使用Classloder.loaderClass
2020.1.7发现
forname还有一个版本重载:
@CallerSensitive public static Class<?> forName(String name, boolean initialize, ClassLoader loader)
可以选择是否初始化及使用哪个类加载器加载
若不指定,则会使用调用方的类加载器,什么叫调用方的类加载器:两种类别的类加载器(其实是4种)
public static Class<?> forName(String className) throws ClassNotFoundException { Class<?> caller = Reflection.getCallerClass(); return forName0(className, true, ClassLoader.getClassLoader(caller), caller); }