1. 簡介
反射可以動態獲取類的完整結構信息,調用對象的方法。
- 優點:靈活性高,只有在運行時才動態創建對象實例
- 缺點:
- 反射獲取方法時是通過獲取所有方法,然后獲得的,時間開銷較高
- 方法調用invoke()時傳入的是object和object[]。不管什么類型都會轉為object會生成對象,同時object[]還需要額外的封裝,遍歷,裝箱開箱等性能消耗
- 每次調用方法都會檢查方法等可見性,也必須檢查參數等類型是否匹配
- 方法難以內聯優化
- 反射是動態加載,無法JIT優化(編譯執行才會用到JIT)
- 反射會破壞類結構,干擾類原有的內部邏輯。
2. 反射的使用
-
獲取Class對象:
//方式一:類名.class方式 Class<ReflectionDemo> reflectionDemoClass = ReflectionDemo.class; //方式二:對象.getClass()方式 ReflectionDemo newReflectionDemo = new ReflectionDemo(); Class<? extends ReflectionDemo> getReflectionDemo = newReflectionDemo.getClass(); //方式三:Class.forName(包名.類名)方式 Class<?> forNamereflectionDemo = Class.forName("xxx.xxx.ReflectionDemo");
-
獲取構造方法Constructor:
private static void ConstructorTest(Class c){ try { //獲取指定參數的構造方法,不傳參則獲取午餐構造。只能獲取public修飾的方法 Constructor constructor = c.getConstructor(int.class); //獲取所有的構造,只能獲取public修飾的方法 Constructor[] constructors = c.getConstructors(); //獲取指定參數的構造方法,不傳參則獲取午餐構造。可以獲取所有修飾符修飾的方法 Constructor declaredConstructor = c.getDeclaredConstructor(int.class); //獲取所有的構造。可以獲取所有修飾符修飾的方法 Constructor[] declaredConstructors = c.getDeclaredConstructors(); //獲取此構造是哪個類的構造 Class declaringClass = constructor.getDeclaringClass(); }catch (Exception e){ } }
- 獲取單個構造方法時,如果有參數,需要傳入參數類型.class,多個參數時按順序填寫多個,無參不傳。
- 獲取構造方法名字不帶Declared的,只能獲取被public修飾的方法。
- 獲取方法名字不帶Declared的,可以獲取任何修飾符修飾的方法。
- 通過getDeclaringClass()獲取當前構造方法是哪個類的構造
-
獲取類的屬性Field:
private static void filedTest(Class c){ try { //獲取指定的成員變量。傳入成員變量名。只能獲取public修飾的成員變量,可以獲取繼承的父類的成員變量 Field field = c.getField("reflectionInt"); //獲取所有的成員變量。只能獲取public修飾的成員變量,可以獲取父類的成員變量 Field[] fields = c.getFields(); //獲取指定的成員變量。傳入成員變量名。可以獲取所有修飾符修飾的成員變量,不可以獲取繼承的父類的成員變量 Field reflectionInt = c.getDeclaredField("reflectionInt"); //獲取所有的成員變量。可以獲取所有修飾符修飾的成員變量,不可以獲取父類的屬性 Field[] declaredFields = c.getDeclaredFields(); //構造一個對象 ReflectionDemo reflectionDemo = new ReflectionDemo(); //通過field字段獲取這個對象這個成員變量的值 //如果對象被private修飾必須設置field.setAccessible(true),否則無法訪問get(),會報錯 field.setAccessible(true); Object o = field.get(reflectionDemo); //通過field字段設置這個對象這個成員變量的值 field.set(reflectionDemo,10); //獲取此屬性是哪個類的成員變量,這里返回的是ReflectionDemo Class<?> declaringClass = field.getDeclaringClass(); }
- 獲取單個成員變量時,需要傳入成員變量名
- 獲取方法名字不帶Declared的,只能獲取被public修飾的成員變量,可以獲取自身及父類的成員變量。
- 獲取方法名字不帶Declared的,可以獲取任何修飾符修飾的成員變量,但是只能獲取自身的成員變量,不能獲取父類的成員變量。
- getField("字段名")獲取的是成員變量,而不是具體的值。可以通過獲取的field.get("對象"),獲取某個對象的這個成員變量的值。如果對象被private修飾則必須設置field.setAccessible(true),否則get()時會出錯
- 可以通過獲取的成員變量field.set(對象,字段值),來設置某個對象的某個成員變量的值
- 通過getDeclaringClass()獲取當前成員變量是哪個類的成員變量。(同一個類的成員變量調用此方法返回值是一樣的)
-
獲取類的方法Method:
private static void methodTest(Class c) { try { //獲取指定方法,第一個參數為方法名,第二個為參數類型的Class,多個參數的話,按順序排列。只能獲取public修飾的方法,可以獲取繼承的父類的public方法 Method method = c.getMethod("reflectionTest", int.class); //獲取所有方法,只能獲取public修飾的方法,可以獲取繼承的父類的public方法 Method[] methods = c.getMethods(); //獲取指定方法,第一個參數為方法名,第二個為參數類型的Class,多個參數的話,按順序排列。能獲取所有的方法,不可以獲取繼承的父類的public方法 Method reflectionTest = c.getDeclaredMethod("reflectionTest", int.class); //獲取所有方法,能獲取所有的方法,不可以獲取繼承的父類的public方法 Method[] declaredMethods = c.getDeclaredMethods(); //調用invoke()執行方法,第一個參數為執行方法的對象,第二個參數為要設置的方法的參數值 //如果方法被private修飾則必須設置method.setAccessible(true),否則報錯 method.setAccessible(true); method.invoke(reflectionDemo,9); //獲取此方法是哪個類的方法 Class<?> declaringClass = method.getDeclaringClass(); } catch (Exception e) { }
- 獲取單個方法時,需要傳入方法名,如果有參數,還需要參數類型.class,多個參數時按順序填寫多個。
- 獲取方法名字不帶Declared的,只能獲取被public修飾的方法,可以獲取自身及父類的public的方法。
- 獲取方法名字不帶Declared的,可以獲取任何修飾符修飾的方法,但是只能獲取自身的方法,不能獲取父類的方法。如果重寫了父類的方法,那么就可以獲取。
- 調用invoke()執行方法,第一個參數為執行方法的對象,第二個參數為要設置的方法的參數值,多個參數直接寫多個。如果方法被private修飾則必須設置method.setAccessible(true),否則報錯。
- 通過getDeclaringClass()獲取當前方法是哪個類的方法。