Java反射總結

jopen 11年前發布 | 18K 次閱讀 Java Java開發

類裝載器工作機制 

類裝載器就是尋找類的節碼文件并構造出類在JVM 內部表示對象的組件。在Java 中, 
類裝載器把一個類裝入JVM 中,要經過以下步驟: 
1.裝載:查找和導入Class 文件; 
通過一個類的全限定名來獲取定義此類的二進制字節流.然后將這個字節流所代表的靜態存儲結構轉化為方法區的運行時數據結構.最后在Java堆中生成一個代表這個類的java.lang.class對像,作為方法區的數據入口. 

2.鏈接:執行校驗、準備和解析步驟,其中解析步驟是可以選擇的: 
a)校驗:檢查載入Class 文件數據的正確性; 
b)準備:給類的靜態變量分配存儲空間; 
c)解析:將符號引用轉成直接引用; 

3.初始化:對類的靜態變量、靜態代碼塊執行初始化工作。 

類裝載工作由ClassLoader 及其子類負責,ClassLoader 是一個重要的Java 運行時系統組件,它負責在運行時查找和裝入Class 字節碼文件。JVM 在運行時會產生三個 
ClassLoader: 
BootstrapClassLoader 
Extension ClassLoader(擴展類裝載器) 
Application ClassLoader(系統類裝載器)。 

其中,BootstrapClassLoader不是ClassLoader 的子類,它使用C++編寫,因此我們在Java 中看不到它,BootstrapClassLoader負責裝載JRE 的核心類庫,如JRE 目標下的rt.jar、charsets.jar 等。 

Extension ClassLoader 和Application ClassLoader 都是ClassLoader 的子類。其中Extension ClassLoader 負責裝載 JRE 擴展目錄ext 中的JAR 類包;Application 負責裝載Classpath 路徑下的類包。 

getParent()
返回該類加載器的父類加載器。
loadClass(String name)
加載名稱為 name 的類,返回的結果是 java.lang.Class 類的實例。
findClass(String name) 
查找名稱為 name 的類,返回的結果是 java.lang.Class 類的實例。
findLoadedClass(String name) 
查找名稱為 name 的已經被加載過的類,返回的結果是 java.lang.Class 類的實例。
defineClass(String name, byte[] b, int off, int len)
把字節數組 b 中的內容轉換成 Java 類,返回的結果是 java.lang.Class 類的實例。這個方法被聲明為 final 的。
resolveClass(Class<?> c)
鏈接指定的 Java 類。


public class ClassLoaderTest {  
public static void main(String[] args) {  
ClassLoader loader = Thread.currentThread().getContextClassLoader();  
System.out.println("current loader:"+loader);  
System.out.println("parent loader:"+loader.getParent());  
System.out.println("grandparent loader:"+loader.getParent(). getParent());  
}  
}  



current loader:sun.misc.Launcher$AppClassLoader@131f71a  
parent loader:sun.misc.Launcher$ExtClassLoader@15601ea  
//①根裝載器在Java中訪問不到,所以返回null  
grandparent loader:null  



通過以上的輸出信息,我們知道當前的ClassLoader 是AppClassLoader,父ClassLoader是ExtClassLoader,祖父ClassLoader 是根類裝載器,因為在Java 中無法獲得它的句柄,所以僅返回null。 


JVM 裝載類時使用“全盤負責委托機制”,“全盤負責”是指當一個ClassLoader 裝載一個類的時,除非顯式地使用另一個ClassLoader,該類所依賴及引用的類也由這個ClassLoader 載入;“委托機制”是指先委托父裝載器尋找目標類,只有在找不到的情況下才從自己的類路徑中查找并裝載目標類。這一點是從安全角度考慮的,試想如果有人編寫了一個惡意的基礎(如java.lang.String)并裝載到JVM 中將會引起多么可怕的后果。但是由于有了“全盤負責委托機制”,java.lang.String 永遠是由根裝載器來裝載的,這樣就避免了上述事件的發生。

獲取當前線程的ClassLoader:

ClassLoader loader = Thread.currentThread().getContextClassLoader(); 
Class.getClassLoader() ; 
實例化Class類對象的三種方式:
Class.forName("Reflect.Demo"); 
new Demo().getClass(); 
Demo.class; 
Class.forName(className)實際上是調用Class.forName(className, true, this.getClass().getClassLoader())。注意第二個參數,是指Class被loading后是不是必須被初始化。

通過Class實例化其他類的對象
Class<?>  demo=Class.forName("Reflect.Person"); 
Constructor<?> cons[]=demo.getConstructors(); 
Object object = cons[0].newInstance(args); 
通過Class實例化接口 
 Class<?> intes[]=demo.getInterfaces();
取得其他類中的父類
Class<?> superClass=demo.getSuperclass();

調用其他類中的方法
demo = Class.forName("Reflect.Demo"); 
Method method=demo.getMethod("toString"); 
method.invoke(demo.newInstance()); 


Method[] methods = demo.getDeclaredMethods(); 
通過反射操作屬性: 
Field field = demo.getDeclaredField("sex"); 
field.setAccessible(true); 
field.set(obj, "男"); 


Field[] fields = Demo.class.getDeclaredFields(); //類中任何可見性的屬性不包括基類 
fields = Demo.class.getFields();  //只能獲得public屬性包括基類的   
使用數組

Class string = Class.forName("java.lang.String");  
Object object= Array.newInstance(string, 10);  
Array.set(object, 5, "this is a test");  
 String s = (String) Array.get(arr, 5); 

=============================應用==========================
JDK動態代理:
與靜態代理類對照的是動態代理類,動態代理類的字節碼在程序運行時由Java反射機制動態生成,無需程序員手工編寫它的源代碼。動態代理類不僅簡化了編程工作,而且提高了軟件系統的可擴展性,因為Java 反射機制可以生成任意類型的動態代理類。java.lang.reflect 包中的Proxy類和InvocationHandler 接口提供了生成動態代理類的能力。


public class MyHandler implements InvocationHandler {   
    private Object target;   

    public Object bind(Object target) {   
        this.target = target;    
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),   
                target.getClass().getInterfaces(), this);   
    }    
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {   
        System.out.println("事物開始");    
        Object result = method.invoke(target, args);   
        System.out.println("事物結束");   
        return result;   
    }     

}
Cglib動態代理 
JDK的動態代理機制只能代理實現了接口的類,而不能實現接口的類就不能實現JDK的動態代理,cglib是針對類來實現代理的,他的原理是對指定的目標類生成一個子類,并覆蓋其中方法實現增強,但因為采用的是繼承,所以不能對final修飾的類進行代理。 

public class MyCglib implements MethodInterceptor {   
    private Object target;   

    public Object getInstance(Object target) {   
        this.target = target;   
        Enhancer enhancer = new Enhancer();   
        enhancer.setSuperclass(this.target.getClass());   
        // 回調方法   
        enhancer.setCallback(this);   
        // 創建代理對象   
        return enhancer.create();   
    }   
    @Override   
    // 回調方法   
    public Object intercept(Object obj, Method method, Object[] args,   
            MethodProxy proxy) throws Throwable {   
        System.out.println("事物開始");   
        proxy.invokeSuper(obj, args);   
        System.out.println("事物結束");   
        return null;   
    }   
}


 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!