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
實例代碼:
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接口,有利於擴展性。