【重难点】【JVM 02】反射在 JVM 层面的实现流程、Class.forName() 和 ClassLoader.loadClass 的区别

【重难点】【JVM 02】反射在 JVM 层面的实现流程、Tomcat 的请求流程和 JVM 的类加载情况

一、反射在 JVM 层面的实现

反射调用有两种方式,一种是本地实现,另一种是委派实现

这里围绕 Method.invoke 方法展开,先看 invoke() 源码:

public Object invoke(Object obj, Object... args)
	throws IllegalAccessExce[tion, IllegalArgumentException, InvocationTargetException{

	if(!override){
		if(!Reflection.quickCheckMemberAccess(clazz, modifiers)){
			Class<?> caller = Reflection.getCallerClass();
			checkAccess(caller, clazz, obj, modifiers);
		}
	}
	MethodAccessor ma = methodAccessor;			//read volatile
	if(ma == null){
		ma = acquireMethodAccessor();
	}
	return ma.invoke(obj, args);
} 

invoke() 是由 MethodAccessor 接口实现的,这个接口有两个实现类

  1. DelegatingMethodAccessorImpl 委派实现
  2. NativeMethodAvvessorImpl 本地实现

这两种实现不是相互独立的,而是相互协作的

实例:

public class InvokeDemo{
	public static void target(int i){
		new Exception("#"+i).pringtStackTrace();
	}
	public static void main(String[] args) 
		throws ClassNotFountException, NoSuchMethodException, InvocationTargetException, IllegalAccussException{

		Class<?> invokeDemo1 = Class.forName("com.example.demo.invoke_Demo.InvokeDemo");
		Method method1 = invokeDemo1.getMethod("target",int.class);
		method1.invoke(null,0);
	}
}

运行之后,便可以在异常栈中查找方法调用的路线:

java.lang.Exception: #0
	at com.example.demo.invoke_demo.InvokeDemo.target(InvokeDemo.java:9)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at com.example.demo.invoke_demo.InvokeDemo.main(InvokeDemo.java:15)

这里,我们能看到,invoke 方法是先调用委派实现,然后再将请求传到本地方法实现,最后再传到目标方法使用

二、Class.forName() 和 ClassLoader.loadClass 的比较

1.相同点

都可以用来对类进行加载

2.不同点

  • Class.forName 除了将类的 .class 文件加载到 JVM 之外,还会对类进行解释,执行类中的 static 代码块
  • ClassLoader.loadClass 只干一件事,就是将 .class 文件加载到 JVM,不会执行 static 代码块,只有在 newInstance 才会去执行
  • Class.forName 可以通过参数控制,实现和 ClassLoader.loadClass 一样的功能

Class.forName(className) 方法,内部实际调用的是 Class.forName(className, true, classloader);

第 2 个 boolean 参数表示类是否需要初始化,不写则默认需要

一旦初始化,就会触发目标对象的 static 代码块,static 变量也会被再次初始化

ClassLoader.loadClass(className) 方法,内部实际调用的方法是 ClassLoader.loadClass(className, false);

第 2 个 boolean 参数,表示目标对象是否进行连接,false 表示不进行连接

不进行连接意味着不进行包括初始化等一系列步骤,那么 static 代码块不会被触发,static 变量也不会初始化

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

313YPHU3

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值