Java反射機制詳細介紹

1、反射的介紹html

    反射是指程序能夠訪問,檢測,修改它自己狀態或行爲的一種能力。java的反射機制是指在程序運行狀態中,給定任意一個類,均可以獲取到這個類的屬性和方法;給定任意一個對象均可以調用這個對象的屬性和方法,這種動態的獲取類的信息和調用對象的方法的功能稱之爲java的反射機制。 一言以蔽之:反射機制可讓你在程序運行時,拿到任意一個類的屬性和方法並調用它。java

    主要做用:api

    a、運行時構造一個類的對象;
    b、運行時獲取一個類所具備的的成員變量和方法;
    c、運行時調用任意一個對象的方法;
    d、生成動態代理; 其實反射最主要的功能我以爲是與框架搭配使用。
數組

    其實上面的能夠總結爲:反射:運行時類信息    安全

    e、類型轉換前先作檢查。oracle

2、Class對象的獲取框架

    一、對象的getClass()方法。
    二、類的.class(最安全/性能最好)屬性。
    三、運用Class.forName(String className)動態加載類,className須要是類的全限定名(最經常使用)。ide

代碼:性能

public class Complet {
	
	public static void main(String[] args) throws ClassNotFoundException {
		
		Class<?> c =	Complet.class;
		
		Class<? extends Class> class1 = c.getClass();
		
		Class c1=Class.forName("Complet");
		System.out.println(c1.getPackage());
		
	}
}

    想在運行時使用類型信息,必須獲取對象(好比類Base對象)的Class對象的引用,使用功能Class.forName(「Base」)能夠實現該目的,或者使用base.class。注意,有一點頗有趣,使用功能」.class」來建立Class對象的引用時,不會自動初始化該Class對象,使用forName()會自動初始化該Class對象。爲了使用類而作的準備工做通常有如下3個步驟:this

  • 加載:由類加載器完成,找到對應的字節碼,建立一個Class對象。
  • 連接:驗證類中的字節碼,爲靜態域分配空間。
  • 初始化:若是該類有超類,則對其初始化,執行靜態初始化器和靜態初始化塊。

實例代碼:

package bhz.classinfo;

public class Base {
	 static int num = 1;
	    
	    static {
	        System.out.println("Base " + num);
	    }
	}
package bhz.classinfo;

public class Main {
    public static void main(String[] args) throws ClassNotFoundException {
        // 不會初始化靜態塊
        Class clazz1 = Base.class;
        System.out.println("------");
        // 會初始化
        Class clazz2 = Class.forName("bhz.classinfo.Base");
    }
}

運行結果:

先打印「--------」,而後打印靜態代碼塊的結果。

可是注意並非全部的Class.forName都會觸發靜態代碼塊:

Class.forName("bhz.classinfo.Base",false,classLoader);

上面這句代碼中,第二個參數傳入false,查看源碼:

public static Class<?> forName(  
    String name,  
    //若是initialize = false,那麼類會被加載,但不會執行靜態代碼塊。  
    boolean initialize,//whether the class must be initialized;  
    ClassLoader loader  
)  
--------------------------------------------------------------
jdk 7.0
-----------------------------
  @CallerSensitive
    public static Class<?> forName(String name, boolean initialize,
                                   ClassLoader loader)
        throws ClassNotFoundException
    {
        Class<?> caller = null;
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            // Reflective call to get caller class is only needed if a security manager
            // is present.  Avoid the overhead of making this call otherwise.
            caller = Reflection.getCallerClass();
            if (sun.misc.VM.isSystemDomainLoader(loader)) {
                ClassLoader ccl = ClassLoader.getClassLoader(caller);
                if (!sun.misc.VM.isSystemDomainLoader(ccl)) {
                    sm.checkPermission(
                        SecurityConstants.GET_CLASSLOADER_PERMISSION);
                }
            }
        }
        return forName0(name, initialize, loader, caller);
    }

因此沒法加載。查看Class的方法,默認是傳的true,自動加載靜態代碼塊的。

總結:

Class對象的生成方式以下:
1.類名.class ----->  說明: JVM將使用類裝載器, 將類裝入內存(前提是:類尚未裝入內存),不作類的初始化工做.返回Class的對象。
2.Class.forName("類名字符串")  ----->(注:類名字符串是包名+類名)  說明:裝入類,並作類的靜態初始化,返回Class的對象。
3.實例對象.getClass() ----> 說明:對類進行靜態初始化、非靜態初始化;返回引用.運行時真正所指的對象(由於:子對象的引用可能會賦給父對象的引用變量中)所屬的類的Class的對象。

當咱們編寫一個新的java類時,JVM就會幫咱們編譯成class對象,存放在同名的.class文件中。在運行時,當須要生成這個類的對象,JVM就會檢查此類是否已經裝載內存中。如果沒有裝載,則把.class文件裝入到內存中。如果裝載,則根據class文件生成實例對象。

注意:這就要涉及到類的加載機制,下面是補充知識點:

JVM中類的裝載是由ClassLoader和它的子類來實現的,Java ClassLoader是一個重要的Java運行時系統組件。它負責在運行時查找和裝入類文件的類。
類加載的五個過程:加載、驗證、準備、解析、初始化。

從類被加載到虛擬機內存中開始,到卸御出內存爲止,它的整個生命週期分爲7個階段,加載(Loading)、驗證(Verification)、準備(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)、卸御(Unloading)。其中驗證、準備、解析三個部分統稱爲鏈接。

3、獲取信息

Class類提供了大量的實例方法來獲取該Class對象所對應的詳細信息,Class類大體包含以下方法,其中每一個方法都包含多個重載版本,所以咱們只是作簡單的介紹,詳細請參考JDK文檔

  • 獲取類內信息
獲取內容 方法簽名
構造器 Constructor<T> getConstructor(Class<?>... parameterTypes)
包含的方法 Method getMethod(String name, Class<?>... parameterTypes)
包含的屬性 Field getField(String name)
包含的Annotation <A extends Annotation> A getAnnotation(Class<A> annotationClass)
內部類 Class<?>[] getDeclaredClasses()
外部類 Class<?> getDeclaringClass()
所實現的接口 Class<?>[] getInterfaces()
修飾符 int getModifiers()
所在包 Package getPackage()
類名 String getName()
簡稱 String getSimpleName()
  • 一些判斷類自己信息的方法
判斷內容 方法簽名
註解類型? boolean isAnnotation()
使用了該Annotation修飾? boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)
匿名類? boolean isAnonymousClass()
數組? boolean isArray()
枚舉? boolean isEnum()
原始類型? boolean isPrimitive()
接口? boolean isInterface()
obj是不是該Class的實例 boolean isInstance(Object obj)
  • 使用反射生成並操做對象:

Method Constructor Field這些類都實現了java.lang.reflect.Member接口,程序能夠經過Method對象來執行相應的方法,經過Constructor對象來調用對應的構造器建立實例,經過Filed對象直接訪問和修改對象的成員變量值。

4、動態代理的使用

4.一、JDK的動態代理

    a.實現接口InvocationHandler,而且重寫invoke()方法。

public class DynamicProxyHandler implements InvocationHandler{
	 private Object proxyed;
	 public DynamicProxyHandler(Object proxyed) {
	        this.proxyed = proxyed;
	    }
	    
	    @Override
	    public Object invoke(Object proxy, Method method, Object[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
	    	 
	    	System.out.println("代理工做了.");
	        return method.invoke(proxyed, args);
	    }

}

    b.調用動態代理

public static void main(String[] args) {
	        RealObject real = new RealObject();
            //調用動態代理
	        Interface proxy = (Interface) Proxy.newProxyInstance(
	                Interface.class.getClassLoader(), new Class[] {Interface.class},
	                new DynamicProxyHandler(real));
	        
	        
	        //調用實際方法
	        proxy.doSomething();
	        proxy.somethingElse("luoxn28");
	    }

注意:invoke()方法的返回值能夠是如何的值(Object),咱們通常是返回方法,而後再調用類中進行調用。假設返回的是「String」類型,將不能調用被代理的相關的方法。因此咱們通常是返回method.invoke(proxyed, args)方法。

舉例:

Dubbo源碼的動態代理:

public class InvokerInvocationHandler implements InvocationHandler {

    private final Invoker<?> invoker;
    
    public InvokerInvocationHandler(Invoker<?> handler){
        this.invoker = handler;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String methodName = method.getName();
        Class<?>[] parameterTypes = method.getParameterTypes();
        if (method.getDeclaringClass() == Object.class) {
            return method.invoke(invoker, args);
        }
        if ("toString".equals(methodName) && parameterTypes.length == 0) {
            return invoker.toString();
        }
        if ("hashCode".equals(methodName) && parameterTypes.length == 0) {
            return invoker.hashCode();
        }
        if ("equals".equals(methodName) && parameterTypes.length == 1) {
            return invoker.equals(args[0]);
        }
        return invoker.invoke(new RpcInvocation(method, args)).recreate();
    }

}

注意:invoker是dubbo自定義的invoker接口,有利於擴展性。

相關文章
相關標籤/搜索