關於Java的反射機制,百度百科上是這樣解釋的:JAVA反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意方法和屬性;這種動態獲取信息以及動態調用對象方法的功能稱爲java語言的反射機制。
從百度百科的解釋中,我們可以知道反射其實就是可以動態的獲取任意一個類的所有屬性和方法,包括私有的。下面我們就通過幾個簡單的例子來學習反射的基本用法。
首先我們先創建一個Person類,這個類包括一些公共的,私有的屬性和方法。
package test.base;
public class Person {
private String name; //這是私有的屬性
int age;
public String address;
public Person(){} //無參公共的構造方法
private Person(String name,int age,String address){ //有參私有的構造方法
this.name = name;
this.age = age;
this.address = address;
}
public void setName(String name) {
this.name = name;
}
public void show(){ //無返回值,無參,公共的普通方法
System.out.println("這是公共的,無返回值的,無參數的show方法。。。。");
}
private String show(String methodName){ //有返回值,有參,私有的普通方法
return "這是私有的,有返回值的,參數爲" + methodName + "的show方法。。。";
}
@Override
public String toString() {
return "姓名:"+ name + "\t年齡:" + age + "\t\t地址:" + address;
}
}
我們要使用反射,首先就先要獲取類的字節碼文件對象,現在我們通過三種方式來獲取字節碼文件對象,創建GetClass類。
package test.base;
public class GetClass {
public static void main(String[] args) throws ClassNotFoundException {
//方式一
Person p = new Person();
Class<?> c1 = p.getClass();
//方法二
Class<?> c2 = Person.class;
//方法三
Class<?> c3 = Class.forName("test.base.Person");//forName裏面的參數要帶上類的包名
System.out.println(c1 == c2); //運行結果:true
System.out.println(c1 == c3); //運行結果:true
}
}
從上面的運行結果我們可以得出,c1 = c2 = c3,即這三種方式得到的對象都是指向同一個Person。但我們建議以後使用方式三,因爲方式三隻要有名稱即可,不用像方式一那樣要創建對象,所以比較方便;而且也不用像方式二那樣要明確用到類中的靜態成員,所以擴展性更強。
接下來我們來獲取類的構造方法,創建GetConstructor類。
package test.base;
import java.lang.reflect.Constructor;
public class GetConstructor{
public static void main(String[] args) throws Exception {
// 使用方式三獲取字節碼文件對象
Class<?> c = Class.forName("test.base.Person");
// 獲取Constructor對象(4種)
// public Constructor<?>[] getConstructors():獲得所有公共構造方法對象
// public Constructor<T> getConstructor(Class<?>... parameterTypes):返回反映此 Class 對象所表示的類的指定公共構造方法對象。
// public Constructor<?>[] getDeclaredConstructors():獲得所有構造方法對象(包括私有的)
// public Constructor<T> getDeclaredConstructor(Class<?>...parameterTypes) :返回反映此 Class 對象所表示的類或接口的指定構造方法對象。(包括私有的)
// 上面方法中返回的是Constructor對象,還要調用Constructor類的的newInstance方法才能使用person類的構造方法
// 將作爲變量傳遞給構造方法調用的對象數組;基本類型的值被包裝在適當類型的包裝器對象(如 Float 中的 float)中。
// public T newInstance(Object... initargs)
// 獲得所有構造方法對象
Constructor<?>[] cons = c.getDeclaredConstructors();
for (Constructor<?> con : cons) {
System.out.println(con);
}
System.out.println("---------------------------------------------------------");
// 獲取單個Constructor對象並調用無參構造方法
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance(); // 這裏就類似於Object per = new Person();對obj進行強制類型轉化之後就可以利用obj對person類進行訪問。
System.out.println(obj);
// 獲取單個帶參私有構造方法
Constructor<?> con1 = c.getDeclaredConstructor(String.class,int.class,String.class); // 參數表示的是:你要獲取的構造方法的構造參數個數及數據類型的class字節碼文件對象(如果是無參構造方法的話就不用寫參數)
//當要通過反射訪問類的私有成員(包括成員變量,構造方法和成員方法)時,要將Constructor類中的setAccessible設置爲true。
con1.setAccessible(true); // 值爲true則指示反射的對象在使用時應該取消Java語言訪問檢查。
Object obj1 = con1.newInstance("獨孤九劍",22,"南苑1棟407");
System.out.println(obj1);
}
}
運行結果:
第一行和第二行是用Constructor類的getDeclaredConstructors()方法獲取所有的構造方法對象;
第三行是獲取無參的構造方法對象並使用;
第四行是獲取有參的構造方法對象並使用。
獲取類的屬性,創建GetField類。
package test.base;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
public class GetField{
public static void main(String[] args) throws Exception {
// 使用方式三獲取類的字節碼文件對象
Class<?> c = Class.forName("test.base.Person");
// 獲取Field對象(4種)
// public Field getField(String name);
// public Field[] getFields();
// public Field getDeclaredField(String name);
// public Field[] getDeclaredFields()
// 上面方法中返回的是Field對象,還要調用Field類的的set方法才能設置person類的屬性。
// 第一個參數表示應該修改其字段的對象,第二個參數表示正被修改的 obj 的屬性的新值 。(當此屬性爲static屬性時,第一個參數可以寫成null)
// public void set(Object obj,Object value)
// 通過無參構造方法創建對象
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
// 獲取address並對其賦值(address爲公共的屬性)
Field addressField = c.getField("address");
addressField.set(obj, "南苑1棟407");
// 獲取name並對其賦值(name爲私有的屬性)
Field nameField = c.getDeclaredField("name");
nameField.setAccessible(true); //私有的成員(包括屬性和方法)要設置setAccessible方法爲true。
nameField.set(obj, "獨孤九劍");
// 獲取age並對其賦值(age爲默認的屬性)
Field ageField = c.getDeclaredField("age"); //非公共的成員要用getDeclaredField()方法調用。
ageField.set(obj, 22);
System.out.println(obj);
//上面通過反射給person類的成員屬性賦值的方式效果跟下面跟是一樣的
Person p = new Person();
p.address = "南苑1棟407";
p.setName("獨孤九劍");
p.age = 22;
System.out.println(p);
}
}
運行結果:
第一行的是通過反射獲取每個屬性對象並設置相應的值;
第二行是通過創建對象給每個屬性賦值。
獲取類的普通方法,創建GetMethod類。
package test.base;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class GetMethod{
public static void main(String[] args) throws Exception {
// 使用方式三獲取類的字節碼文件對象
Class<?> c = Class.forName("test.base.Person");
// 獲取Method對象(4種)
// public Method[] getMethods(); // 獲取自己的包括父親的公共方法
// public Method getMethod(String name,Class<?>... parameterTypes); 第一個參數表示的方法名,第二個參數表示的是方法的參數的class類型
// public Method[] getDeclaredMethods(); // 獲取自己的所有的方法
// public Method getDeclaredMethod(String name,Class<?>... parameterTypes);
// 上面方法中返回的是Method對象,還要調用Method類的的invoke方法才能使用person類的方法
// 因爲不知道返回值的確切類型,所以返回Object類型,第一個參數表示對象是誰,第二參數表示調用該方法的實際參數
// public Object invoke(Object obj,Object... args)
// 獲取無參構造方法
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
// 獲取公共無參無返回值的方法並調用
Method m1 = c.getMethod("show");
m1.invoke(obj);
System.out.println("---------------------------------------------------");
// 獲取私有的有參有返回值的方法並調用
Method m2 = c.getDeclaredMethod("show", String.class);
m2.setAccessible(true);
System.out.println(m2.invoke(obj, "獨孤九劍"));
}
}
運行結果:
第一行是獲取公共的,無參的方法對象並調用;
第二行是獲取私有的,有參的方法對象並調用。
以上就是反射的基本用法。