有關Java反射的使用看這一篇就夠了
1. 簡介
本篇文章不探討反射的實現機制或者說實現原理,僅僅從使用的角度去講解我們常用的一些API介面,方便自己以後需要使用時信手拈來,同時也方便廣大博友能夠快速瞭解API的使用。
什麼是反射?
反射是java語言的一個特性,它允許一個java的類獲取他所有的成員變數和方法並且顯示出來,這樣說起來有些抽象,例如我們可以通過反射去例項化一個物件,並不非得使用new這個關鍵字來例項化,同時我們也可以通過反射知道Java類中有哪些變數、哪些方法等等,這些特性在C或者C++語言中是不存在的。
反射中,我們會常用到三個類,分別為Class、Method和Field,通過類名相信大家已經能夠知道大概意思,分別代表類、方法和變數,本篇文章將會用大量的Demo來講解這三個類中的部分常用重要API的作用,Demo中用到的一個實體類如下:
-
public class Person {
-
private String name;
-
public Person() {
-
System.out.println("Person()...");
-
}
-
public Person(String name) {
-
this.name = name;
-
System.out.println("Person(String name)...");
-
}
-
private void privateMethod(){
-
System.out.println("privateMethod-->name="+this.name);
-
}
-
protected void protectedMethod(){
-
System.out.println("protectedMethod-->name="+this.name);
-
}
-
void defaultMethod(){
-
System.out.println("defaultMethod-->name="+this.name);
-
}
-
public void setName(String name) {
-
this.name = name;
-
System.out.println("setName()...");
-
}
-
public String getName() {
-
System.out.println("getName()...");
-
return name;
-
}
-
@Override
-
public String toString() {
-
return "name: "+this.name;
-
}
-
}
2. Class 類
2.1 獲取Class例項
-
// 方法一 forName函式
-
Class c= Class.forName("Person");
-
// 方法二 getClass()函式
-
Person person = new Person();
-
Class c = person.getClass();
-
// 方法三 使用類字面常量
-
Class c=Person.class;
2.2 通過反射來例項化物件
-
// 方法一
-
try {
-
Class c = Person.class;
-
Person person = (Person) c.newInstance();
-
System.out.println(person instanceof Person); // true
-
} catch (InstantiationException e) {
-
e.printStackTrace();
-
} catch (IllegalAccessException e) {
-
e.printStackTrace();
-
}
-
// 方法二
-
try {
-
Class c = Class.forName("reflect.demo.Person");
-
Person person = (Person) c.newInstance();
-
System.out.println(person instanceof Person); // true
-
} catch (ClassNotFoundException e) {
-
e.printStackTrace();
-
} catch (IllegalAccessException e) {
-
e.printStackTrace();
-
} catch (InstantiationException e) {
-
e.printStackTrace();
-
}
以上方式都是呼叫的Person類無參構造,如果需要呼叫Person(String name),改如何呢?
-
// 首先獲取到Person類中的有參構造,通過建構函式例項化物件
-
try {
-
Class c = Person.class;
-
Constructor constructor = c.getConstructor(new Class[]{String.class});
-
// 呼叫的是有參構造
-
Person person = (Person) constructor.newInstance("jack");
-
System.out.println(person.toString()); // name: jack
-
} catch (NoSuchMethodException e) {
-
e.printStackTrace();
-
} catch (IllegalAccessException e) {
-
e.printStackTrace();
-
} catch (InstantiationException e) {
-
e.printStackTrace();
-
} catch (InvocationTargetException e) {
-
e.printStackTrace();
-
}
2.3 獲取類中所有的方法物件
-
try {
-
Class c = Person.class;
-
// 方式一
-
Method [] methods = c.getMethods(); // 獲取所有公共方法,且包括父類的共有方法
-
for (Method method:methods) {
-
System.out.print(method.getName()+", ");
-
}
-
System.out.println("");
-
// 方式二
-
methods = c.getDeclaredMethods(); // 獲取所有方法,包括私有、共有等,但是隻定義在該類中
-
for (Method method:methods) {
-
System.out.print(method.getName()+", ");
-
}
-
} catch (Exception e) {
-
e.printStackTrace();
-
}
-
輸出:
-
toString, getName, setName, wait, wait, wait, equals, hashCode, getClass, notify, notifyAll,
-
toString, getName, setName, protectedMethod, privateMethod, defaultMethod,
-
// 其他
-
Method getMethod(String name, Class<?>... parameterTypes) // 獲取某個方法物件,只能是共有的
-
Method getDeclaredMethod(String name, Class<?>... parameterTypes) // 獲取獲取某個方法物件,只能是當前類的,可以是private、public等
2.4 獲取類中欄位物件
-
try {
-
Class c = Person.class;
-
Field[] fields = c.getFields(); // 同 getMethods
-
for (Field field : fields) {
-
System.out.print(field.getName() + ", ");
-
}
-
System.out.println("5***************");
-
fields = c.getDeclaredFields(); // 同 getDeclaredMethods
-
for (Field field : fields) {
-
System.out.print(field.getName() + ", ");
-
}
-
} catch (Exception e) {
-
e.printStackTrace();
-
}
2.5 其他方法
- getName : 返回類名,包括包名
- getSimpleName: 返回類名,不包含包名
- cast(Object obj):將obj型別物件轉化為當前class型別物件
-
Class cc = Person.class;
-
// getName: reflect.demo.Person getSimpleName: Person
-
System.out.println("getName: "+cc.getName()+" getSimpleName: "+cc.getSimpleName());
3. Method類
- getName() 獲取方法名
- invoke(Object obj, Object... args) 執行obj中該方法
- Parameter[] getParameters() 獲取方法中的引數
- setAccessible(boolean flag) 設定訪問許可權
-
Person per = new Person();
-
Class c = Person.class;
-
Method method;
-
try {
-
method = c.getDeclaredMethod("setName", String.class);
-
System.out.println(method.getName()); // 輸出: setName
-
method.invoke(per, "hello reflect"); // 呼叫了 setName方法
-
System.out.println(per); // 輸出 name: hello reflect
-
Parameter[] parameters = method.getParameters();
-
for (Parameter parameter:parameters) {
-
System.out.print(parameter.getName()+", "); // 輸出:arg0
-
}
-
} catch (Exception e) {
-
e.printStackTrace();
-
}
-
// 如何反射呼叫私有方法
-
try {
-
method = c.getDeclaredMethod("privateMethod");
-
method.setAccessible(true); // 私有方法必須設定訪問許可權為true
-
method.invoke(per); // 呼叫privateMethod方法
-
} catch (Exception e) {
-
e.printStackTrace();
-
}
4. Field API
- String getName() 獲取變數名
- setAccessible(boolean flag) 設定訪問許可權
- Object get(Object obj) 獲取obj物件中改變數值
- set(Object obj, Object value) 將obj物件中的改變數值設定為value
-
Class c = Person.class;
-
Person person = new Person("lvjie");
-
try {
-
// Field field = c.getField("name"); // 只能獲取到共有變數,包括父類
-
Field field = c.getDeclaredField("name"); // 可以獲取當前類中定義的任何變數,包括private、public等
-
System.out.println(field.getName()); // name
-
field.setAccessible(true); // 私有變數必須設定訪問許可權為true
-
System.out.println(field.get(person)); // 獲取該物件中的變數值,私有變數需要新增訪問許可權
-
System.out.println(person); // name: lvjie
-
field.set(person, "jack"); // 對該物件的變數設定值, 私有變數需要新增訪問許可權
-
System.out.println(person); // name: jack
-
} catch (Exception e) {
-
e.printStackTrace();
-
}
以上相關API在使用反射過程中是經常遇見的,當然還有其他相關API,例如獲取該類、方法和變數上使用到的註解等等,這些在註解的使用上會做講解。反射配合註解,會產生巨大的功能,目前許多開源庫也是基於這兩點技術來實現,例如Android中的 EventBus、ButterKnife、DBFlow等等。
如果對Java註解感興趣,請看下一篇Java註解全面總結