【重難點】【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 接口實作的,這個接口有兩個實作類
- DelegatingMethodAccessorImpl 委派實作
- 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 變量也不會初始化