天天看點

【重難點】【JVM 02】反射在 JVM 層面的實作流程、Class.forName() 和 ClassLoader.loadClass 的差別【重難點】【JVM 02】反射在 JVM 層面的實作流程、Tomcat 的請求流程和 JVM 的類加載情況一、反射在 JVM 層面的實作二、Class.forName() 和 ClassLoader.loadClass 的比較

【重難點】【JVM 02】反射在 JVM 層面的實作流程、Tomcat 的請求流程和 JVM 的類加載情況

文章目錄

  • 【重難點】【JVM 02】反射在 JVM 層面的實作流程、Tomcat 的請求流程和 JVM 的類加載情況
  • 一、反射在 JVM 層面的實作
  • 二、Class.forName() 和 ClassLoader.loadClass 的比較
    • 1.相同點
    • 2.不同點

一、反射在 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 變量也不會初始化