本文基於 JDK8,Oracle官網對反射的解釋是html
Reflection enables Java code to discover information about the fields, methods and constructors of loaded classes, and to use reflected fields, methods, and constructors to operate on their underlying counterparts, within security restrictions. The API accommodates applications that need access to either the public members of a target object (based on its runtime class) or the members declared by a given class. It also allows programs to suppress default reflective access control.反射使 Java 代碼能夠發現有關已加載類的字段,方法和構造函數的信息,並在安全性限制內使用反射對這些字段,方法和構造函數進行操做。java
簡而言之,指在 Java 程序運行時git
這種動態獲取類的內容,建立對象、以及動態調用對象的方法及操做屬性的機制爲反射。即便該對象的類型在編譯期間是未知,該類的 .class 文件不存在,也能夠經過反射直接建立對象。github
優點spring
劣勢segmentfault
反射的源 java.lang.Class,Class 類是 JDK 對咱們自定義的類和內置類的統一描述,Class 類中存儲了類運行時的類型信息。在 Class 類,能夠獲取以下圖所示類的公共信息。Class 類與反射的聯繫密切相關,Class 類和 java.lang.reflect 一塊兒對反射提供了支持。
定義兩個類 Boy 和 Person,Person 做爲 Boy 的父類,做爲接下演示這個部分反射用法的類。api
Person.class安全
public class Person { public String name; private int age; public void talk() { System.out.println(name + "is talking"); } }
Boy.class微信
public class Boy extends Person { public int height; private int weight; public static String description; public Boy() {} private Boy(int height) { this.height = height; } public Boy(int height, int weight) { this.height = height; this.weight = weight; } public static void playBasketball() { System.out.println("play basketball!"); } public static void playBall(String ballType) { System.out.println("play " + ballType + "!"); } private void pickUpGirl() { System.out.println("pick up girl!"); } public int getWeight() { return weight; } public int getHeight() { return height; } }
Class<Boy> clazz = Boy.class; // 經過類的 class 屬性 Class<?> clazz2 = new Boy().getClass(); // 經過運行時類對象的 getClass 方法獲取 Class<?> clazz3 = Class.forName("com.hncboy.corejava.reflection.Boy"); // 經過類的全限定名獲取 Class<?> clazz4 = Main.class.getClassLoader().loadClass("com.hncboy.corejava.reflection.Boy"); // 經過類加載器獲取
一個類的基本信息包含了修飾符,class 關鍵字,類名,父類,接口等信息,經常使用方法以下:oracle
int modifier = clazz.getModifiers(); // 獲取類的修飾符 Package pack = clazz.getPackage(); // 獲取類的包名 String fullClassName = clazz.getName(); // 獲取類的全路徑名稱 String simpleClassName = clazz.getSimpleName(); // 獲取類的簡單名稱 ClassLoader classLoader = clazz.getClassLoader(); // 獲取類的類加載器 Class<?>[] interfacesClasses = clazz.getInterfaces(); // 獲取類實現的接口列表 Class<?> superClass = clazz.getSuperclass(); // 獲取類的父類 Annotation[] annotations = clazz.getAnnotations(); // 獲取類的註解列表
經過一個測試類,測試以上方法:
public class Test { public static void main(String[] args) { Class<Boy> clazz = Boy.class; // 獲取類的修飾符,若是有多個修飾符,取相加後的結果 int modifiers = clazz.getModifiers(); System.out.println("modifiers: " + modifiers); System.out.println("modifiers toString: " + Modifier.toString(modifiers)); // 獲取類的包名 Package pack = clazz.getPackage(); System.out.println("package: " + pack); // 獲取類的全路徑名稱:包名 + 類名 String fullClassName = clazz.getName(); System.out.println("fullClassName: " + fullClassName); // 獲取類的簡單名稱:只有類名 String simpleClassName = clazz.getSimpleName(); System.out.println("simpleClassName: " + simpleClassName); // 獲取類的類加載器 ClassLoader classLoader = clazz.getClassLoader(); System.out.println("classLoader: " + classLoader); // 獲取類實現的接口列表 Class<?>[] interfacesClasses = clazz.getInterfaces(); System.out.println("interfacesClasses: " + Arrays.toString(interfacesClasses)); // 獲取類的父類 Class<?> superClass = clazz.getSuperclass(); System.out.println("superClass: " + superClass); // 獲取類的註解列表 Annotation[] annotations = clazz.getAnnotations(); System.out.println("annotations: " + Arrays.toString(annotations)); } }
運行輸出結果以下所示:
modifiers: 1 modifiers toString: public package: package com.hncboy.corejava.reflection fullClassName: com.hncboy.corejava.reflection.Boy simpleClassName: Boy classLoader: sun.misc.Launcher$AppClassLoader@18b4aac2 interfacesClasses: [] superClass: class com.hncboy.corejava.reflection.Person annotations: []
其中 getModifiers 以整數形式編碼返回此類或接口的 Java 語言修飾符。經過查 java.lang.reflect.Modifier 的 API 可知,返回結果有以下類型,全部修飾符的值都爲二進制運算的結果,經過位運算判斷修飾符是最快的。
字段的信息存儲在 Field 類中, Field 提供有關類或接口的單個字段的信息,以及對它們的動態訪問,而且能夠對變量進行修改。反射字段能夠是類(靜態)字段或實例字段。經常使用方法以下:
Field[] fields = clazz.getFields(); // 獲取類中全部的公有字段 Field[] declaredFields = clazz.getDeclaredFields(); // 獲取類中定義的字段 Field nameField = clazz.getField("name"); // 獲取指定名稱的公有字段 Field declaredField = clazz.getDeclaredField("likeDesc"); // 獲取指定名稱類中定義的字段 int modifiersField = likeDescField.getModifiers(); // 獲取字段的修飾 nameField.setAccessible(true); // 指定字段強制訪問 nameField.set(person, "hncboy"); // 成員字段賦值(需指定對象) descriptionField.set(null, "hncboy"); // 靜態字段賦值
經過一個測試類,測試以上方法:
public class Test { public static void main(String[] args) throws Exception { Class<Boy> clazz = Boy.class; // 獲取類中全部的公有字段,包含繼承的 Field[] fields = clazz.getFields(); for (Field field : fields) { System.out.println(field); } // 獲取指定名稱的公有字段,包含繼承的 Field nameField = clazz.getField("name"); System.out.println(nameField); // 獲取本類中定義的全部字段,不包含繼承的,包含私有的 Field[] declaredFields = clazz.getDeclaredFields(); for (Field field : declaredFields) { System.out.println(field); } // 獲取本類中指定名稱類中定義的字段 Field weightField = clazz.getDeclaredField("weight"); System.out.println(weightField.getModifiers()); // 給指定字段賦值(需指定對象) Boy boy = clazz.newInstance(); // 將該字段設置爲強制訪問 weightField.setAccessible(true); weightField.set(boy, 120); System.out.println(boy.getWeight()); // 靜態字段賦值,靜態字段不須要指定對象 Field descField = clazz.getField("description"); descField.set(null, "靜態屬性"); System.out.println(Boy.description); } }
運行輸出結果以下所示:
public int com.hncboy.corejava.reflection.Boy.height public static java.lang.String com.hncboy.corejava.reflection.Boy.description public java.lang.String com.hncboy.corejava.reflection.Person.name public java.lang.String com.hncboy.corejava.reflection.Person.name public int com.hncboy.corejava.reflection.Boy.height private int com.hncboy.corejava.reflection.Boy.weight public static java.lang.String com.hncboy.corejava.reflection.Boy.description 2 120 靜態屬性
在直接訪問私有 private 變量 weight 時,會報以下的錯誤,不能訪問 Boy 類的私有變量。經過 Field 繼承的 java.lang.reflect.AccessibleObject 類中的 setAccessible(boolean flag) 能夠開啓權限,setAccessible 方法經過調用 native setAccessible0 方法取消 Java 語言訪問檢查權限。
Exception in thread "main" java.lang.IllegalAccessException: Class com.hncboy.corejava.reflection.Test can not access a member of class com.hncboy.corejava.reflection.Boy with modifiers "private" at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102) at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:296) at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:288) at java.lang.reflect.Field.set(Field.java:761) at com.hncboy.corejava.reflection.Test.main(Test.java:41)
方法的信息存儲在 Method 類中, Method 提供有關類或接口上單個方法的信息,以及對單個方法的訪問。反射方法能夠是類方法或實例方法(包括抽象方法)。經常使用方法以下:
Method[] methods = clazz.getMethods(); // 獲取類中全部的公有方法 Method[] declaredMethods = clazz.getDeclaredMethods(); // 獲取本類的全部方法 Method talkMethod = clazz.getMethod("talk", String.class); // 獲取類中指定名稱和參數的公有方法 Method pugMethod = clazz.getDeclaredMethod("pickUpGirls"); // 獲取本類中定義的指定名稱和參數的方法 int modifiers = pugMethod.getModifiers(); // 獲取方法的修飾符 talkMethod.invoke(boy, "I tell you"); // 指定對象進行成員方法的調用 pugMethod.setAccessible(true); // 指定方法的強制訪問 pickUpGirlsMethod.invoke(null); // 指定靜態方法的調用
經過一個測試類,測試以上方法:
public class Test { public static void main(String[] args) throws Exception { Class<Boy> clazz = Boy.class; // 獲取類中定義的方法,包含繼承的(Object) Method[] methods = clazz.getMethods(); for (Method method : methods) { System.out.println(method); } // 獲取類中指定的方法(無參) Method talkMethod = clazz.getMethod("talk"); System.out.println(talkMethod.getName()); // 獲取類中指定的方法(有參) Method playMethod = clazz.getMethod("playBall", String.class); System.out.println(playMethod.getName()); // 獲取本類中的全部方法,不包含繼承,包含私有的 Method[] declaredMethods = clazz.getDeclaredMethods(); for (Method method : declaredMethods) { System.out.println(method); } // 獲取本類中特定的的方法 Method declaredMethod = clazz.getDeclaredMethod("pickUpGirl"); System.out.println(declaredMethod.getName()); // 底層是基於構造器的建立無參構造器 Boy boy = clazz.newInstance(); // 調用公有有參方法 playMethod.invoke(boy, "足球"); // 調用私有無參方法,須要設置強制訪問 declaredMethod.setAccessible(true); declaredMethod.invoke(boy); // 調用靜態方法 Method playBasketBallMethod = clazz.getDeclaredMethod("playBasketball"); playBasketBallMethod.invoke(null); } }
運行輸出結果以下所示:
public static void com.hncboy.corejava.reflection.Boy.playBasketball() public int com.hncboy.corejava.reflection.Boy.getWeight() public int com.hncboy.corejava.reflection.Boy.getHeight() public static void com.hncboy.corejava.reflection.Boy.playBall(java.lang.String) public void com.hncboy.corejava.reflection.Person.talk() public final void java.lang.Object.wait() throws java.lang.InterruptedException public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException public boolean java.lang.Object.equals(java.lang.Object) public java.lang.String java.lang.Object.toString() public native int java.lang.Object.hashCode() public final native java.lang.Class java.lang.Object.getClass() public final native void java.lang.Object.notify() public final native void java.lang.Object.notifyAll() talk playBall private void com.hncboy.corejava.reflection.Boy.pickUpGirl() public static void com.hncboy.corejava.reflection.Boy.playBasketball() public int com.hncboy.corejava.reflection.Boy.getWeight() public int com.hncboy.corejava.reflection.Boy.getHeight() public static void com.hncboy.corejava.reflection.Boy.playBall(java.lang.String) pickUpGirl play 足球! pick up girl! play basketball!
在經過 getMethods 獲取全部父類的公有方法,Boy 類的父類包含 Person 類和 Object 類,因此總共輸出 14 個公有方法。getMethod 或 getDeclaredMethod 方法獲取指定方法名無參的方法時,參數能夠省略,直接傳入方法名,獲取帶參數的方法時,若是類型錯誤會報 NoSuchMethodException 異常,以下所示。經過 method 的 invoke 方法傳入實例對象調用實例方法,調用靜態方法傳入 null 便可。
Exception in thread "main" java.lang.NoSuchMethodException: com.hncboy.corejava.reflection.Boy.playBall(int) at java.lang.Class.getMethod(Class.java:1786) at com.hncboy.corejava.reflection.Test.main(Test.java:29)
構造器的信息存儲在 Constructor 類中, Constructor 提供有關類的單個構造函數的信息,以及對類的訪問。經常使用方法以下:
Constructor<?>[] constructors = clazz.getConstructors(); // 獲取類中全部的公有構造器 Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors(); // 獲取類中全部的構造器 Constructor<?> declaredConstructor = clazz.getDeclaredConstructor(); // 獲取類中無參的構造器 Constructor<?> constructor = clazz.getDeclaredConstructor(String.class, String.class); // 獲取類中有參構造器 int modifiers = constructor.getModifiers(); // 獲取構造器的修飾符 declaredConstructor.newInstance(); // 構造器實例對象 declaredConstructor.setAccessible(true); // 指定構造器的強制訪問 constructor.newInstance("hncboy"); // 有參構造調用 clazz.newInstance(); // 直接調用默認無參構造
經過一個測試類,測試以上方法:
public class Test { public static void main(String[] args) throws Exception { Class<Boy> clazz = Boy.class; // 獲取類中全部的公有構造器 Constructor<?>[] constructors = clazz.getConstructors(); for (Constructor<?> constructor : constructors) { System.out.println(constructor); } // 獲取類中全部的構造器,包含私有的 Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors(); for (Constructor<?> constructor : declaredConstructors) { System.out.println(constructor); } // 獲取類中無參的構造器 Constructor<?> noParamsConstructor = clazz.getDeclaredConstructor(); System.out.println(noParamsConstructor); // 獲取類中指定參數構造器 Constructor<?> constructor1 = clazz.getDeclaredConstructor(int.class); Constructor<?> constructor2 = clazz.getDeclaredConstructor(int.class, int.class); System.out.println(noParamsConstructor.getModifiers()); System.out.println(constructor1.getModifiers()); System.out.println(constructor2.getModifiers()); // 調用構造器 Boy boy = (Boy) noParamsConstructor.newInstance(); System.out.println(boy); constructor1.setAccessible(true); boy = (Boy) constructor1.newInstance(177); System.out.println(boy.getHeight()); } }
運行輸出結果以下所示:
public com.hncboy.corejava.reflection.Boy(int,int) public com.hncboy.corejava.reflection.Boy() public com.hncboy.corejava.reflection.Boy(int,int) private com.hncboy.corejava.reflection.Boy(int) public com.hncboy.corejava.reflection.Boy() public com.hncboy.corejava.reflection.Boy() 1 2 1 com.hncboy.corejava.reflection.Boy@4b67cf4d 177
getConstructors 方法獲取類中的全部公有構造器,構造器不能繼承,全部只能獲取本類中的。getDeclaredConstructors 獲取本類的全部構造器,包含私有的。在獲取特定參數構造器時,傳入的要與構造器的參數同樣,如 int.class 不能寫成 Integer.class,由於自動拆箱是在編譯過程當中的,而反射是在運行期間的。
經過反射,可以使用 Class.newInstance() 或 Constructor.newInstance() 兩種方式建立對象。Class 類下的 newInstance 是弱類型,只能調用無參的構造方法,若是沒有默認構造方法,會拋出 InstantiationException 實例化異常,經過源碼可知,該方法的本質上是 return tmpConstructor.newInstance((Object[])null); ,也是調用 Constructor 的 newInstance 方法 。 而 Constructor 類下的 newInstance 能夠調用任意參數的構造器。
單例模式
public class Hungry { private static final Hungry INSTANCE = new Hungry(); private Hungry() {} public static Hungry getInstance() { return INSTANCE; } }
public class Lazy { private static Lazy instance; private Lazy() {} public static Lazy getInstance() { if (instance == null) { synchronized (Lazy.class) { if (instance == null) { instance = new Lazy(); } } } return instance; } }
public class SingletonDestroyer { public static void main(String[] args) throws Exception { // 破壞懶漢模式 Lazy lazyInstance = Lazy.getInstance(); Constructor<Lazy> lazyConstructor = Lazy.class.getDeclaredConstructor(); lazyConstructor.setAccessible(true); Lazy lazyInstanceReflect = lazyConstructor.newInstance(); System.out.println(lazyInstance); System.out.println(lazyInstanceReflect); // 破壞餓漢模式 Hungry hungryInstance = Hungry.getInstance(); Constructor<Hungry> hungryConstructor = Hungry.class.getDeclaredConstructor(); hungryConstructor.setAccessible(true); Hungry hungryInstanceReflect = hungryConstructor.newInstance(); System.out.println(hungryInstance); System.out.println(hungryInstanceReflect); } }
運行結果以下,經過反射機制能夠破環單例模式,將私有化的構造器經過強制訪問建立對象。
com.hncboy.corejava.reflection.Lazy@4b67cf4d com.hncboy.corejava.reflection.Lazy@7ea987ac com.hncboy.corejava.reflection.Hungry@12a3a380 com.hncboy.corejava.reflection.Hungry@29453f44
IOC(Inversion of Control) 控制反轉,他是一種設計思想,並不是實際的技術,最核心的思想就是將預先設計的對象實例建立的控制權交給程序(IOC 容器)。 IOC 容器本質上是一個 K-V 結構的 Map。IOC 的實現原理就是工廠模式加反射機制。
經過 Spring 文檔可查看 Bean 實例的三種建立方式:
步驟以下:
public class A { public A() { System.out.println("調用 A 的無參構造器"); } public static B createBInstance() { System.out.println("調用 A 的靜態方法 createBInstance"); return new B(); } public C createCInstance() { System.out.println("調用 A 的實例方法 createCInstance"); return new C(); } } class B {} class C {}
經過模擬該配置文件來進行對象的建立
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 建立方式1:無參構造器建立 A 對象--> <bean id="a" class="com.hncboy.corejava.reflection.A"/> <!-- 建立方式2:靜態工廠建立,調用 A 的 createBObj 方法來建立名爲 b 的對象放入容器 --> <bean id="b" class="com.hncboy.corejava.reflection.A" factory-method="createBInstance"/> <!-- 建立方式3:實例工廠建立,調用實例 a 的 createBObj 方法來建立名爲 c 的對象放入容器 --> <bean id="c" factory-bean="a" factory-method="createCInstance"/> </beans>
/** * 存放 bean 的基本信息 */ public class BeanConfig { private String id; private String clazz; private String factoryMethod; private String factoryBean; /* getter、setter 省略 */ }
/** * 定義 map 存放 map */ public class IOCContainer { private static Map<String, Object> container = new HashMap<>(); public static void putBean(String id, Object object) { container.put(id, object); } public static Object getBean(String id) { return container.get(id); } }
建立方式1:無參構造器建立。bean 的內容包括 id 和 clazz
建立方式2:靜態工廠建立。bean 的內容包括 id 和 factory-method
建立方式3:實例工廠建立。bean 的內容包括 id,factory-bean 和 factory-method
public class Init { public static void main(String[] args) throws Exception { List<BeanConfig> beanConfigs = parseXmlToBeanConfig(); // 將解析的 BeanConfig 進行實例化 for (BeanConfig beanConfig : beanConfigs) { if (beanConfig.getClazz() != null) { Class<?> clazz = Class.forName(beanConfig.getClazz()); if (beanConfig.getFactoryMethod() != null) { // 建立方式2:靜態工廠建立 Method method = clazz.getDeclaredMethod(beanConfig.getFactoryMethod()); IOCContainer.putBean(beanConfig.getId(), method.invoke(null)); } else { // 建立方式1:無參構造器建立 IOCContainer.putBean(beanConfig.getId(), clazz.newInstance()); } } else if (beanConfig.getId() != null) { // 建立方式3:實例工廠建立 Object object = IOCContainer.getBean(beanConfig.getFactoryBean()); Method method = object.getClass().getDeclaredMethod(beanConfig.getFactoryMethod()); IOCContainer.putBean(beanConfig.getId(), method.invoke(object)); } else { System.out.println("缺乏配置,沒法建立對象!"); } } } /** * 模擬解析 XML 中的 bean * * @return */ private static List<BeanConfig> parseXmlToBeanConfig() { List<BeanConfig> beanConfigs = new ArrayList<>(); // 模擬無參構造器建立對象 BeanConfig beanConfig1 = new BeanConfig(); beanConfig1.setId("a"); beanConfig1.setClazz("com.hncboy.corejava.reflection.A"); beanConfigs.add(beanConfig1); // 模擬靜態工廠建立對象 BeanConfig beanConfig2 = new BeanConfig(); beanConfig2.setId("b"); beanConfig2.setClazz("com.hncboy.corejava.reflection.A"); beanConfig2.setFactoryMethod("createBInstance"); beanConfigs.add(beanConfig2); // 模擬實例工廠建立對象 BeanConfig beanConfig3 = new BeanConfig(); beanConfig3.setId("c"); beanConfig3.setFactoryBean("a"); beanConfig3.setFactoryMethod("createCInstance"); beanConfigs.add(beanConfig3); return beanConfigs; } }
運行結果以下:
調用 A 的無參構造器 調用 A 的靜態方法 createBInstance 調用 A 的實例方法 createCInstance
Java反射徹底解析
文章同步到公衆號和Github,有問題的話能夠聯繫做者。