Java反射機制是指程序在運行狀態中,對於任何一個類,咱們都可以知道這個類的全部屬性和方法(包括private、protected等)。對於任何一個對象,咱們都可以對它的屬性和方法進行調用。咱們把這種動態獲取對象信息和調用對象方法的功能稱之爲反射機制。html
在程序運行時,當一個類加載完成以後,在堆內存的方法區中就產生了一個Class類型的對象(一個類只會對應一個Class對象,絕對不會產生第二個),這個Class對象就包含了完整的類的結構信息。咱們建立該類的對象後就能夠經過這個對象看到類的結構。這個對象就像一面鏡子,透過這個鏡子看到類的結構。因此,咱們形象的稱之爲:反射。這麼說很是的抽象,不能理解,可是經過後面代碼的體現就會變得容易理解,暫時先了解一下便可。java
到後面還會學習到框架的知識(如Spring、Hibernate等等),框架的組成結構能夠理解爲這個公式:框架=反射+註解+設計模式。其中反射就是框架的靈魂所在。因此反射在Java的學習中很是重要。設計模式
那麼Java反射機制主要提供的功能有哪些:數組
java反射API中經常使用的類介紹:框架
Class類是用來描述類的類,它是一個十分特殊的類,沒有構造方法。Class對象的加載過程以下:當程序運行時,咱們編寫的每個類,編譯完成後,都會生成 類名.class 文件,當咱們咱們new對象或者類加載器加載的時候,JVM就會加載咱們的 類名.class 文件,而且加載到內存中,而後在將 類名.class 文件讀取而且同時會生成該類惟一的一個Class對象,用於表示該類的全部信息。ide
Class表明一個類,在運行的Java應用程序中表示類或接口。在Class類中提供了不少有用的方法,這裏對他們簡單的介紹。此處參考學習
一、得到類相關的方法:測試
二、得到類中屬性相關的方法:ui
三、得到類中註解相關的方法:this
四、得到類中構造器相關的方法:
五、得到類中方法相關的方法:
六、類中其餘重要的方法:
Class類中的方法還有有不少,這裏只列舉了部分,須要學習更多的能夠自行去查看Class的API文檔。
注意:Class並非只有普通類或接口才能獲取,其中基本數據類型、數組、枚舉、註解、void等均可以獲取其Class對象,甚至Class這個類自己也能夠獲取Class對象,簡單舉例:
Class<Object> c1 = Object.class; Class<String> c2 = String.class; Class<Integer> c3 = int.class; Class<int[]> c4 = int[].class; Class<int[][]> c5 = int[][].class; Class<ElementType> c6 = ElementType.class; Class<Override> c7 = Override.class; Class<Class> c8 = Class.class; Class<Void> c9 = void.class; int arr[]=new int[10]; int arr1[]=new int[100]; Class<? extends int[]> c10 = arr.getClass(); Class<? extends int[]> c11 = arr1.getClass(); //只要元素類型和維度相同,就是同一個Class System.out.println(c10==c11);
既然反射機制必定會用到Class這個類,那麼就必須先獲取它,獲取Class對象有四種方式。注意:反射這裏全部舉例都用Person類來做爲演示,因此先建立一個Person類,後面會一直用這個。
public class Person { //公共的成員變量 public String name; //default成員變量 int age; //私有的成員變量 private String address; public Person() { } //私有構造方法 private Person(String name, int age) { this.name = name; this.age = age; } public Person(String name, int age, String address) { this.name = name; this.age = age; this.address = address; } getter/setter方法省略 //公共方法 public void display1(){ System.out.println("公共方法..."); } //私有方法 private void display2(){ System.out.println("私有方法..."); } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + ", address='" + address + '\'' + '}'; } }
四種方式分別是:
①、經過Object類中的getClass方法獲取。這種方式要先建立類的對象,這樣再使用反射就畫蛇添足了,不推薦。
②、經過類名.class 直接獲取。這種方式須要導入相應類的包,依賴性較強,不推薦。
③、使用Class類中靜態方法forName(String className)獲取。因此這種方式最經常使用。
④、使用類加載器ClassLoader來獲取。這種方式瞭解便可,用的不多。
代碼演示以下:
public class Reflection { public static void main(String[] args) throws ClassNotFoundException { //第一種方式:getClass Person person = new Person(); Class<? extends Person> clazz1 = person.getClass(); System.out.println("getClass獲取:"+clazz1); //第二種方式:類名.class Class<Person> clazz2 = Person.class; System.out.println("類名.class獲取:"+clazz2); //第三種方式:forName try { Class<?> clazz3 = Class.forName("com.thr.Person"); System.out.println("forName()方法獲取:"+clazz3); } catch (ClassNotFoundException e) { e.printStackTrace(); } //第四種方式:類加載器ClassLoader ClassLoader classLoader = Reflection.class.getClassLoader(); Class<?> clazz4 = classLoader.loadClass("com.thr.Person"); System.out.println("類加載器ClassLoader獲取:"+clazz4); //能夠發現返回都是true,說明引用的同一個對象 System.out.println(clazz1 == clazz2); System.out.println(clazz1 == clazz4); } }
這四種方式推薦使用Class類中的靜態方法forName(String className)來獲取。並且不管使用哪一種方式獲取,結果都是同一個類。
在咱們正常建立對象的實例時,其內部的私有元素是不能訪問的,可是使用反射則能夠輕易的獲取,前面建立的Person類中屬性、方法和構造器都有被private關鍵字所修飾。因此接下來演示一下怎麼獲取成員變量、方法和構造器。
獲取屬性使用到的是Field這個類,它內部的方法主要是獲取、設置某個屬性的方法,例如:getXXX()、setXXX();
public class Reflection { public static void main(String[] args) throws Exception { //獲取Person的Class對象 Class<?> clazz = Class.forName("com.thr.Person"); //一、經過反射獲取全部public的成員變量,private、protected、default是不能獲取到的 Field[] fields1 = clazz.getFields(); for (Field field : fields1) { System.out.println(field.getName()); } System.out.println("------------------------"); //二、經過反射單個獲取public成員變量 Field name = clazz.getField("name"); System.out.println(name); Field age = clazz.getDeclaredField("age"); System.out.println(age); Field address = clazz.getDeclaredField("address"); address.setAccessible(true); System.out.println(address); System.out.println("------------------------"); //三、經過反射獲取全部變量,包括private、protected、default。 Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { //四、獲取成員變量名字 System.out.println(field.getName()); //五、獲取權限修飾:0是default、1是public、2是private、4是protected。或者使用Modfier.toString(modifiers)輸出 int modifiers = field.getModifiers(); System.out.println(modifiers); //六、獲取成員變量數據類型 Class<?> type = field.getType(); System.out.println(type); System.out.println("---------------"); } } }
獲取方法用到的是Method類,內部方法也是一些getXXX()和setXXX的方法,這些方法就不演示了,和上面獲取Field大同小異,能夠參考上面的例子。而後咱們主要來演示一些與上面不同的方法。
爲了測試獲取方法上面的Annotation方法,我建立了一個MyAnnotation註解,而後讓其做用在Person類的display1()方法上面。
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { String[] value() default "tang_hao"; }
須要注意的是@Retention必需要設置爲RUNTIME類型,不然是獲取不到的。
public class Reflection { public static void main(String[] args) throws Exception { //獲取Person的Class對象 Class<?> clazz = Class.forName("com.thr.Person"); //一、經過反射獲取全部方法 Method[] methods = clazz.getDeclaredMethods(); for (Method method : methods) { //二、獲取方法的名稱 System.out.println("名稱:"+method.getName()); //三、獲取方法上面的註解 Annotation[] annotations = method.getAnnotations(); for (Annotation annotation : annotations) { System.out.println("註解:"+annotation); } //四、獲取方法返回值類型 Class<?> returnType = method.getReturnType(); System.out.println("返回值類型:"+returnType); //五、獲取形參列表 Class<?>[] parameterTypes = method.getParameterTypes(); if (!(parameterTypes==null||parameterTypes.length==0)){ for (Class<?> parameterType : parameterTypes) { System.out.println("形參列表:"+parameterType.getName()); } } System.out.println("----------------"); } } }
若是咱們還想要使用反射來調用方法,那麼就須要使用到這個方法:
public class Reflection { public static void main(String[] args) throws Exception { //獲取Person的Class對象 Class<?> clazz = Class.forName("com.thr.Person"); Object o = clazz.newInstance(); Method display1 = clazz.getDeclaredMethod("display1"); display1.invoke(o); Method display2 = clazz.getDeclaredMethod("display2"); display2.setAccessible(true); display2.invoke(o); } }
獲取方法用到的是Constructor類,構造器的獲取比較的簡單,簡單演示一下:
public class Reflection { public static void main(String[] args) throws Exception { //獲取Person的Class對象 Class<?> clazz = Class.forName("com.thr.Person"); //一、獲取public的構造器 Constructor<?>[] constructors = clazz.getConstructors(); for (Constructor<?> constructor : constructors) { System.out.println(constructor); } System.out.println("-----------------------"); //二、獲取全部的構造器 Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors(); for (Constructor<?> declaredConstructor : declaredConstructors) { System.out.println(declaredConstructor); } System.out.println("-----------------------"); //三、使用反射建立實例,默認構造器 Constructor<?> constructor = clazz.getConstructor(); System.out.println(constructor.getName()); Object o = constructor.newInstance(); Person p= (Person) o; System.out.println(p.toString()); //有參構造器 Constructor<?> constructor1 = clazz.getConstructor(String.class, int.class,String.class); Object o1 = constructor1.newInstance("tang_hao",20,"China"); Person p1= (Person) o1; System.out.println(p1.toString()); } }
其實後面還有獲取父類、泛型、接口、所在包等等其餘的就很少說了,其實都是大同小異,理解了上面這些,後面這些應該也會了。
從前面學到這裏咱們大概對反射也應該有一個簡單的瞭解了,利用反射能夠獲取成員變量、方法、構造器、註解等屬性,甚至是私有的方法,這在普通建立實例的方式是不可能的。雖然咱們在平時寫代碼不多用到反射技術,可是咱們要知道後面會學習框架知識,而框架的靈魂就是反射,因此學好反射基礎對咱們來講是很是有必要的。