寫在前面的話:
很多人會說我直接new一個對象不就完了么,干嘛還用反射來獲取對象。因為new屬于靜態(tài)編譯,而反射屬于動態(tài)編譯,反射只有到運行時他才會去獲得該對象的實例巧婶。從這點上可以看出反射的強大之處,我們接下來就來詳細介紹Java反射機制涂乌。
首先我們先來看幾種獲取class對象的方法艺栈。(鋪墊)####
- 所有的引用數(shù)據(jù)類型(類-類型)的類名、基本數(shù)據(jù)類型都可以通過.class方式獲取其Class對象(對于基本數(shù)據(jù)類型的封裝類還可以通過.TYPE 的方式獲取其Class對象湾盒,但要注意湿右,TYPE實際上獲取的是封裝類對應(yīng)的基本類型的Class對象的引用,那么你可以判斷出int.class==Integer.TYPE 返回true罚勾,int.class==Integer.class返回false)毅人。通過這種方式不會初始化靜態(tài)域。使用.class.TYPE的方式獲取Class對象叫做類的字面常量尖殃。這樣做不僅更簡單丈莺,而且更安全,因為它在編譯時就會受到檢查(因此不需要置于try語句塊中)送丰,并且它根除了對forName方法的引用缔俄,所以也更高效。
- Class的forName(String name)傳入一個類的完整類路徑也可以獲得Class對象器躏,但由于使用的是字符串俐载,必須強制轉(zhuǎn)換才可以獲取泛型的Class<T>的Class對象,并且你必須獲取這個方法可能拋出的ClassNotFoundException異常登失。這種方法可以初始化靜態(tài)域遏佣。
- 還可通過類的對象實例下的getClass()方法來獲取Class對象,即實例名.getClass()揽浙。
再來看看反射機制的定義状婶。####
百科上給出的定義是:Java反射機制是指在運行狀態(tài)中意敛,對于任意一個類,都能夠知道這個類的所有屬性和方法太抓;對于任意一個對象空闲,都能夠調(diào)用它的任意一個方法和屬性;這種動態(tài)獲取的信息以及動態(tài)調(diào)用對象的方法的功能稱為java語言的反射機制走敌。用一句話總結(jié)就是反射可以實現(xiàn)在運行時可以知道任意一個類的屬性和方法。
使用反射機制有什么好處呢逗噩,哪些場合會用到呢掉丽?####
上面反射機制的定義還是很言簡意賅的。因為反射可以在運行時加載异雁、探知捶障、使用完全未知的類,你只要給我一個路徑就可以了纲刀,我通過這個路徑拿到我需要的東西项炼,我的操作與你無關(guān)了,靈活性更高了示绊,類之間的耦合性也就降低了锭部。
同時我們可以獲取到類的私有信息,破壞了類的封裝面褐,使類變得不安全拌禾。使用反射還會降低程序性能。所以說使用反射優(yōu)點突出展哭,缺點同樣突出湃窍。
反射應(yīng)用到的場合:
- 在運行時判斷任意一個對象所屬的類。
Class aClass = Class.forName("com.Dan.Consumer");
System.out.print("?"+aClass.isInstance(new Consumer()));
// public boolean isInstance(Object obj)
// 當(dāng)該 Class 對象表示一個已聲明的類時匪傍,若指定的 Object 參數(shù)是所表示類(或其任一子類)的一個實例您市,則此方法返回 true;否則返回 false役衡。
- 在運行時構(gòu)造任意一個類的對象茵休。
Class aClass1 = Class.forName("com.Dan.Consumer");
Object object = aClass1.newInstance();
// throws InstantiationException, IllegalAccessException
- 在運行時判斷任意一個類所具有的成員變量和方法。
Class aClass2 = Class.forName("com.Dan.Consumer");
Method[] methods = aClass2.getDeclaredMethods();
Field[] fields = aClass2.getDeclaredFields();
- 在運行時調(diào)用任意一個對象的方法映挂。
Class aClass3 = Class.forName("com.Dan.Consumer");
Method[] methods[]= aClass3.getDeclaredMethods();
Object object = aClass3.newInstance();
Object returnObject = methods.invoke(object,null);
簡單的說就是程序運行時泽篮,如果需要外部傳進來一個對象,然后在程序中運用這個對象柑船,我們就可以通過配置文件中類的路徑帽撑,得到這個對象的所有信息,從而加以應(yīng)用鞍时。
Java的很多框架的底層都是用反射來實現(xiàn)的亏拉,比如Struts2扣蜻,JDBC,SQLite...
那反射具體是怎么實現(xiàn)的呢及塘?####
首先我們要知道Java里實現(xiàn)反射的那個類是java.lang.reflect莽使。我們查看API在類摘要里主要有以下四個類:
① java.lang.reflect.AccessibleObject
② java.lang.reflect.Constructor<T>
③ java.lang.reflect.Field
④ java.lang.reflect.Method
其中AccessibleObject 是另外三個類的基類。我們接下來涉及的主要就是這幾個類笙僚。
在本文一開始就介紹了三種獲取class對象的方法芳肌,那么反射用到的是第2個方法。
- 獲取類的對象
Class<?> clazz = Class.forName("com.Dan.Consumer");
Object object = clazz.newInstance();
- 獲取類的方法
public Method[] getDeclaredMethods()
返回 Method 對象的一個數(shù)組肋层,這些對象反映此 Class 對象表示的類或接口聲明的所有方法亿笤,包括公共、保護栋猖、默認(包)訪問和私有方法净薛,但不包括繼承的方法。
public Method getDeclaredMethod(String name,
Class<?>... parameterTypes)
返回一個 Method 對象蒲拉,該對象反映此 Class 對象所表示的類或接口的指定已聲明方法肃拜。
public Method[] getMethods()
返回一個包含某些 Method 對象的數(shù)組,這些對象反映此 Class 對象所表示的類或接口(包括那些由該類或接口聲明的以及從超類和超接口繼承的那些的類或接口)的公共成員方法雌团。
public Method getMethod(String name,
Class<?>... parameterTypes)
返回一個 Method 對象燃领,它反映此 Class 對象所表示的類或接口的指定公共成員方法。
調(diào)用方法(這里就不全貼了辱姨,可以參考下面總示例代碼):
Method method = clazz.getMethod("info", String.class, long.class);//獲取方法
method.invoke(object, "隔壁老王",2017032009);//通過invoke調(diào)用該方法
invoke:如果底層方法是靜態(tài)的柿菩,或底層方法所需的形參數(shù)為 0,那么可以忽略指定的 obj 參數(shù)雨涛,該參數(shù)可以為 null枢舶。
- 獲取成員變量信息
public Field[] getDeclaredFields()
返回 Field 對象的一個數(shù)組,這些對象反映此 Class 對象所表示的類或接口所聲明的所有字段替久。包括公共凉泄、保護、默認(包)訪問和私有字段蚯根,但不包括繼承的字段后众。
public Field getDeclaredField(String name)
返回一個 Field 對象,該對象反映此 Class 對象所表示的類或接口的指定已聲明字段颅拦。
public Field[] getFields()
返回一個包含某些 Field 對象的數(shù)組蒂誉,這些對象反映此 Class 對象所表示的類或接口的所有可訪問公共字段。如果該 Class 對象表示一個類距帅,則此方法返回該類及其所有超類的公共字段右锨。如果該 Class 對象表示一個接口,則此方法返回該接口及其所有超接口的公共字段碌秸。
public Field getField(String name)
返回一個 Field 對象绍移,它反映此 Class 對象所表示的類或接口的指定公共成員字段悄窃。
獲得成員變量:
Field aField = clazz.getDeclaredField("name"); //因為name變量是private的,所以不能用getField方法
aField.setAccessible(true);//值為 true 則指示反射的對象在使用時應(yīng)該取消 Java 語言訪問檢查蹂窖。值為 false 則指示反射的對象應(yīng)該實施 Java 語言訪問檢查轧抗。
aField.set(object,"二大爺");
Object obj = aField.get(object);
System.out.println(obj);
- 獲取構(gòu)造器
public Constructor<?>[] getDeclaredConstructors()
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
public Constructor<?>[] getConstructors()
public Constructor<T> getConstructor(Class<?>... parameterTypes)
構(gòu)造器和上面的方法、屬性都是大同小異的瞬测。需要注意的是横媚,當(dāng)返回一個數(shù)組的時候,返回類型是Constructor<?>[]而不是Constructor<T>[]涣楷,因為從此方法返回之后分唾,該數(shù)組可能被修改以保存不同類的 Constructor 對象。
代碼示例####
一個關(guān)于顧客的類Consumer:
public class Consumer {
private long id;//私有的
public String name;//共有的
/*沒參數(shù)構(gòu)造體*/
public Consumer() {
}
/*有參數(shù)構(gòu)造體*/
public Consumer(long id, String name) {
this.id = id;
this.name = name;
}
/*getter setter*/
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
/*私有的無參buy方法*/
private static void buy() {
System.out.println("私有的無參buy方法");
}
/*共有的有參有返回值consume方法*/
public String consume(String giftName) {
System.out.println("買了一件禮物: " + giftName);
return giftName;
}
}
TestReflect類通過反射得到Consumer類的信息
public class TestReflect {
public static void getProperty() throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
try {
/* 獲取對象類型 */
Class<?> clazz = Class.forName("com.Dan.Consumer");
Object object = clazz.newInstance();
/* 獲取到所有屬性 */
Field[] field = clazz.getDeclaredFields();
for (Field f:field) {
//String fieldName = f.getName();// 取到屬性名字
//System.out.println(fieldName);
System.out.println(f);
}
/* 獲取到所有的方法狮斗,包括私有的,但不包括父類的 */
Method[] methods = clazz.getDeclaredMethods();
for (Method m:methods){
//String methodName = m.getName();
//System.out.println(methodName);
System.out.println(m);
}
/* 所有的構(gòu)造體 */
Constructor[] constructors = clazz.getDeclaredConstructors();
for (Constructor c:constructors){
System.out.println(c);
}
// 調(diào)用方法
Method method = clazz.getMethod("info", String.class, long.class);//獲取方法
method.invoke(object, "隔壁老王",2017032009);
//得到屬性
Field aField = clazz.getDeclaredField("name"); //因為name變量是private的弧蝇,所以不能用getField方法
aField.setAccessible(true);
aField.set(object,"二大爺");
Object obj = aField.get(object);
System.out.println(obj);
// 得到構(gòu)造器
Constructor constructor = clazz.getDeclaredConstructor(long.class, String.class);
constructor.newInstance(2016040221, "王小二");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws InvocationTargetException, NoSuchMethodException,
InstantiationException, IllegalAccessException, NoSuchFieldException {
getProperty();
}
}
// private long com.Dan.Consumer.id
// public java.lang.String com.Dan.Consumer.name
// public java.lang.String com.Dan.Consumer.getName()
// public void com.Dan.Consumer.setName(java.lang.String)
// public long com.Dan.Consumer.getId()
// private static void com.Dan.Consumer.buy()
// public void com.Dan.Consumer.setId(long)
// public void com.Dan.Consumer.info(java.lang.String,long)
// public com.Dan.Consumer()
// public com.Dan.Consumer(long,java.lang.String)
// 我是:隔壁老王碳褒,會員編號: 2017032009。
// 二大爺
寫完嘍看疗!ㄟ(▔,▔)ㄏㄟ(▔,▔)ㄏㄟ(▔,▔)ㄏ
知識重在總結(jié)和梳理沙峻,只有不斷地去學(xué)習(xí)并運用,才能化為自己的東西两芳。當(dāng)你能為別人講明白的時候摔寨,說明自己已經(jīng)掌握了。
歡迎轉(zhuǎn)載怖辆,轉(zhuǎn)載請注明出處是复!
如果有錯誤的地方,或者有您的見解竖螃,還請不嗇賜教淑廊!
喜歡的話,麻煩點個贊特咆!