三、Java中的反射
1、反射技術簡介
Java反射技術應用廣泛,它能夠配置:類的全限定名、方法和引數,完成物件的初始化,甚至可以反射某些方法。這樣就大大增強了Java的可配置性,Spring IOC的基本原理也是如此。
2、Java反射機制的定義
Java反射機制是指在執行狀態中,對於任意一個類,都能夠知道這個類的屬性和方法。對於任意一個物件,都能夠呼叫它的任意一個方法和屬性。這種動態獲取的資訊以及動態呼叫方法的功能成為Java的反射機制。用一句話總結就是Java的反射機制可以在執行狀態中知道任意一個類的屬性和方法。
優點
通過配置就可以生成物件,可以解除程式的耦合性,非常靈活。而且在專案上線後,後期可以很方便的對專案功能進行更新。
缺點
執行慢,對程式的效能有一定的影響。因為使用反射基本上是一種解釋性操作。這類操作總是慢於直接執行相同的操作。
3、Java類和類型別
類是java.lang.Class類的例項物件,而Class是所有類的類。對於普通的物件,我們一般都會這樣建立和表示:
Person p = new Person();
上面說了,所有的類都是Class的物件,那麼如何表示呢,可不可以通過如下方式呢:
Class clazz = new Class();
但是我們檢視Class的原始碼時,是這樣寫的:
private Class(ClassLoader loader) {// Initialize final field for classLoader. The initialization value of non-null // prevents future JIT optimizations from assuming this final field is null. classLoader = loader;
}
可以看到構造器是私有的,只有JVM可以建立Class的物件,因此不可以像普通類一樣new一個Class物件,雖然我們不能new一個Class物件,但是卻可以通過已有的類得到一個Class物件,共有三種方式,如下:
//這說明任何一個類都有一個隱含的靜態成員變數class,這種方式是通過獲取類的靜態成員變數class得到的 Class clazz1 = Person.class; //這種方式是通過一個類的物件的getClass()方法獲得的 Person p = new Person(); Class<? extends Person> clazz2 = p.getClass(); //這種方法是Class類呼叫forName方法,通過一個類的全量限定名獲得 Class<?> clazz3 = Class.forName("cn.zhuangxp.reflect.Person");
這裡的clazz1、clazz2、clazz3都是Class物件,他們是完全一樣的,而且有個學名,叫做Person的類型別。類型別就是類的型別,就是描述一個類是什麼,有那些屬性和方法。所以我們可以通過類的型別,來獲取一個類屬性和方法,並且呼叫它的屬性和方法,這就是反射的基礎。
舉個簡單的例子
Person類
package cn.zhuangxp.reflect; /** * @author BEAN_BAG * @date 2018年4月29日 * @description Person實體類 */ public class Person { private String name; private int age; private String hobby; public String msg = "HelloWorld"; public String phone = "13066612345"; private String email = "[email protected]"; public Person() { } public Person(String name, int age){ this.name = name; this.age = age; } private Person(String name, int age, String hobby) { this.name = name; this.age = age; this.hobby = hobby; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getHobby() { return hobby; } public void setHobby(String hobby) { this.hobby = hobby; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + ", hobby='" + hobby + '\'' + '}'; } public void say(String name, int age) { System.out.println("我叫:" + name + ",今年:" + age); } private void play(String hobby) { System.out.println("我的愛好是:" + hobby); } }
測試類
public class Demo01 { public static void main(String[] args) throws ClassNotFoundException { //這說明任何一個類都有一個隱含的靜態成員變數class,這種方式是通過獲取類的靜態成員變數class得到的 Class clazz1 = Person.class; System.out.println(clazz1.getName()); //這種方式是通過一個類的物件的getClass()方法獲得的 Person p = new Person(); Class<? extends Person> clazz2 = p.getClass(); System.out.println(clazz2.getName()); //這種方法是Class類呼叫forName方法,通過一個類的全量限定名獲得 Class<?> clazz3 = Class.forName("cn.zhuangxp.reflect.Person"); System.out.println(clazz3.getName()); } }
結果
cn.zhuangxp.reflect.Person
cn.zhuangxp.reflect.Person
cn.zhuangxp.reflect.Person
4、Java反射的相關操作
獲取指定成員方法Method
public class Demo02 { public static void main(String[] args) throws Exception { Class<?> clazz = Class.forName("cn.zhuangxp.reflect.Person"); Object instance = clazz.newInstance(); /** * 返回某個類的指定的公共(public)成員方法 */ Method method1 = clazz.getMethod("say",String.class,int.class); method1.invoke(instance,"BEAN_BAG",23); /** * 返回某個類的指定已宣告方法 */ Method method2 = clazz.getDeclaredMethod("play", String.class); //取消Java許可權控制檢查 method2.setAccessible(true); method2.invoke(instance,"乒乓球"); } }
結果
我叫:BEAN_BAG,今年:23
我的愛好是:乒乓球
獲取所有方法
public class Demo03 { public static void main(String[] args) throws ClassNotFoundException { Class<?> clazz = Class.forName("cn.zhuangxp.reflect.Person"); /** * 返回某個類的所有公共(public)方法,,包括其繼承類的公共方法, * 也包括它所實現的介面的方法。 */ Method[] methods = clazz.getMethods(); for (Method m : methods) { System.out.println(m.getName()); } System.out.println("================================================="); /** * 返回某個類的所有方法,包括公共、保護、預設訪問和私有的方法。 * 但是不包括繼承的方法,當然也包括它所實現的介面的方法。 */ Method[] methods1 = clazz.getDeclaredMethods(); for (Method m : methods1){ System.out.println(m.getName()); } } }
結果
toString getName setName setMsg say setHobby getMsg setAge getHobby getAge wait wait wait equals hashCode getClass notify notifyAll ================================================= toString getName setName play setMsg say setHobby getMsg setAge getHobby getAge
獲取成員變數Field
public class Demo04 { public static void main(String[] args) throws Exception { Class<?> clazz = Class.forName("cn.zhuangxp.reflect.Person"); Object instance = clazz.newInstance(); /** * 獲得該類中指定的public成員變數,包括其父類變數 */ Field field = clazz.getField("msg"); Object msg = field.get(instance); System.out.println(msg.toString()); System.out.println("==============================================="); /** * 獲得該類中所有的自身宣告的變數,不包括其父類的變數 */ Field field1 = clazz.getDeclaredField("email"); field1.setAccessible(true); Object email = field1.get(instance); System.out.println(email.toString()); } }
結果
HelloWorld =============================================== [email protected]163.com
獲取所有的成員變數
public class Demo05 { public static void main(String[] args) throws Exception { Class<?> clazz = Class.forName("cn.zhuangxp.reflect.Person"); Object instance = clazz.newInstance(); /** * 獲得該類所有的public成員變數,包括其父類變數 */ Field[] fields = clazz.getFields(); for (Field f : fields){ Object o = f.get(instance); System.out.println(o.toString()); } System.out.println("==========================================="); /** * 獲得該類自身宣告的所有變數,不包括其父類的變數 */ Field[] fields1 = clazz.getDeclaredFields(); for (Field f : fields1){ System.out.println(f.getName()); } } }
結果
HelloWorld 13066612345 =========================================== name age hobby msg phone email
獲取構造器
public class Demo06 { public static void main(String[] args) throws Exception { Class<?> clazz = Class.forName("cn.zhuangxp.reflect.Person"); Object instance = clazz.newInstance(); /** * 獲得該類的public構造器 */ Constructor<?> constructor = clazz.getConstructor(String.class,int.class); Object o = constructor.newInstance("BEAN_BAG", 23); System.out.println(o); System.out.println("========================================="); /** * 獲得該類所有的構造器 */ Constructor<?> constructor1 = clazz.getDeclaredConstructor(String.class,int.class,String.class); constructor1.setAccessible(true); Object o1 = constructor1.newInstance("mm",22,"English"); System.out.println(o1); } }
結果
Person{name='BEAN_BAG', age=23, hobby='null'} ========================================= Person{name='mm', age=22, hobby='English'}
獲取所有的構造器
public class Demo07 { public static void main(String[] args) throws Exception { Class<?> clazz = Class.forName("cn.zhuangxp.reflect.Person"); Object instance = clazz.newInstance(); /** * 獲得該類所有的public構造器 */ Constructor<?>[] constructors = clazz.getConstructors(); for (Constructor c : constructors){ System.out.println(c.getName()); } System.out.println("===================================="); /** * 獲得該類所有的構造器 */ Constructor<?>[] constructors1 = clazz.getDeclaredConstructors(); for (Constructor c : constructors1){ System.out.println(c.getName()); } } }
結果
cn.zhuangxp.reflect.Person cn.zhuangxp.reflect.Person ==================================== cn.zhuangxp.reflect.Person cn.zhuangxp.reflect.Person cn.zhuangxp.reflect.Person
5、通過反射了解泛型的本質
Java中集合的泛型,是防止錯誤輸入的,只在編譯階段有效。如果繞過編譯期,到執行期就無效了。所以,我們可以通過Java的反射繞過泛型檢查,完整程式碼如下:
public class Demo08 { public static void main(String[] args) throws Exception { List<String> list = new ArrayList<String>(); list.add("zhuang14");//允許新增 //list.add(25);//新增int型別報錯,因為泛型為String //獲取list的類型別 Class<? extends List> clazz = list.getClass(); //獲取list集合的add方法 Method addMethod = clazz.getMethod("add", Object.class); addMethod.invoke(list,25); //結果 System.out.println(list);//[zhuang14, 25] } }