官方文檔的描述,反射通常用于需要檢查或修改 Java 虛擬機中運行的應(yīng)用程序的運行時行為的程序。反射是一種功能強大的技術(shù),可以使應(yīng)用程序執(zhí)行不可能執(zhí)行的操作。
1. 獲取類對象的幾種方式
所有反射操作的入口都是 java.lang.Class。所以,我們首先要知道怎么去獲取到 Class,即類對象。根據(jù)代碼是否有權(quán)訪問對象、類的名稱、類型或現(xiàn)有類,有幾種方法可以獲得 Class。
1.1 Object.getClass()
如果有類的實例,則可以直接 getClass() 方法獲取對應(yīng)的 Class。getClass 是 Obejct 類中的方法,所以只要是繼承自 Obejct 的類都可以調(diào)用。
例如:
public class Box {
public void method() {
}
}
public static void main(String[] args) {
Box box = new Box();
Class clazz = box.getClass();
}
1.2 ".class"
如果沒有類的實例,但是類是可以訪問到的,那么可以通過追加 .class 來獲得這個類。這也是獲取基本類型的最簡單方法。
例如:
Class clazzBox = Box.class;
Class clazzBoolean = boolean.class; //不能通過 getClass() 獲取
1.3 Class.forName()
如果類的包名和類名是已知的,那么可以通過靜態(tài)方法 Class.forName() 獲取類對象。此方法可能會拋出 ClassNotFoundException 異常。
例如:
try {
Class box = Class.forName("testjava.Box");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
1.4 TYPT 屬性
對于基本類型,最方便快捷地獲取 Class 的方式是通過追加 .class。但是,我們還可以通過另外一種方式獲取基本類型的類對象。
我們知道,每一種基本類型都會對應(yīng)的有一個包裝類,比如,boolean 的 包裝類為 Boolean、int 的包裝類為 Integer。每一個包裝類中,都一個靜態(tài)的 TYPE 變量,它就是用來存放包裝類對應(yīng)的基本類型的類對象的。
所以,我們可以這樣獲取基本類型的類對象:
Class clazzInt = Integer.TYPE;
if (clazzInt == int.class) { //int.class 和 Integer.TYPE 是相同的
System.out.println("true");
}
2. 獲取構(gòu)造器實例化對象和屬性信息
2.1 構(gòu)造函數(shù)
構(gòu)造函數(shù)用于創(chuàng)建類的實例,完成一些初始化操作,構(gòu)造方法不能被繼承。
構(gòu)造函數(shù)可能包含名稱、修飾符、參數(shù)和一系列的異常等。可以通過 java.lang.reflect.Constructor 提供的方法獲取到相應(yīng)的信息。
public class Box {
private Box() {
System.out.println("***** Box()");
}
public Box(String name) {
System.out.println("***** Box(name) name:" + name);
}
}
public static void main(String[] args) {
Class boxClass = Box.class;
//獲取 Box 類的所有構(gòu)造方法
Constructor[] allConstructor = boxClass.getDeclaredConstructors();
for (Constructor constructor : allConstructor) {
System.out.println("***** " + constructor.toGenericString());
//獲取構(gòu)造方法的參數(shù)
Type[] gpTypes = constructor.getGenericParameterTypes();
for (int j = 0; j < gpTypes.length; j++) {
System.out.println("***** GenericParameterType " + j + " :" + gpTypes[j]);
}
System.out.println("***** --------------------------");
}
}
打印
***** private testjava.Box()
***** --------------------------
***** public testjava.Box(java.lang.String)
***** GenericParameterType 0 :class java.lang.String
***** --------------------------
通過反射實例化對象,有兩種方式:
- 直接使用
Class.newInstance()實例化對象。 - 先獲取到類的構(gòu)造方法
Constructor,然后通過Constructor.newInstance()實例化對象。
兩者相比,通常建議使用后者,因為前者調(diào)用的是無參數(shù)的構(gòu)造函數(shù)實例化對象,并且需要無參構(gòu)造函數(shù)是可訪問的。而后者對參數(shù)沒有要求,并且就算是 private 的也是可以調(diào)用的。
例如:
public class Box {
private String name;
private Box() {
name = "Box";
System.out.println("***** Box()");
}
public Box(String name) {
this.name = name;
System.out.println("***** Box(name) name:" + name);
}
public String getName() {
return name;
}
}
public static void main(String[] args) {
Class boxClass = Box.class;
//獲取 Box 類的所有構(gòu)造方法
Constructor[] allConstructor = boxClass.getDeclaredConstructors();
Constructor constorZeroParams = null; //無參構(gòu)造方法
Constructor constorParams = null; //有參構(gòu)造方法
for (Constructor constructor : allConstructor) {
if (constructor.getGenericParameterTypes().length == 0) {
constorZeroParams = constructor;
} else {
constorParams = constructor;
}
}
if (constorZeroParams != null) {
constorZeroParams.setAccessible(true);
try {
Box box = (Box) constorZeroParams.newInstance();
//Box box = Box.class.newInstance(); //會報異常 java.lang.IllegalAccessException: Class testjava.TestJava can not access a member of class testjava.Box with modifiers "private"
System.out.println("name:" + box.getName());
} catch (Exception e) {
e.printStackTrace();
}
}
if (constorParams != null) {
constorParams.setAccessible(true);
try {
Box box = (Box) constorParams.newInstance("haha");
System.out.println("name:" + box.getName());
} catch (Exception e) {
e.printStackTrace();
}
}
}
打印結(jié)果:
***** Box()
name:Box
***** Box(name) name:haha
name:haha
2.2 方法
java.lang.reflect.Method 類提供了 API 去獲取方法的信息:修飾符、返回值、參數(shù)、注解信息和異常等。
public class Box {
private String name;
private Box() {
name = "Box";
System.out.println("***** Box()");
}
public Box(String name) {
this.name = name;
System.out.println("***** Box(name) name:" + name);
}
public void setName(String name) {
this.name = name;
System.out.println("***** setName(name) name:" + name);
}
public String getName() {
return name;
}
private void method1() {
System.out.println("private 方法");
}
public void method2() {
System.out.println("public 方法");
}
public int method3() {
System.out.println("帶返回值 方法");
return 0;
}
public <E extends RuntimeException> void method4() throws E {
System.out.println("異常 方法");
}
public static void staticMethod() {
System.out.println("靜態(tài) 方法");
}
}
public static void main(String[] args) {
Class boxClass = Box.class;
Method[] allMethods = boxClass.getMethods();
for (Method method : allMethods) {
System.out.println("---------------------");
//獲取所有的方法
System.out.println("method: " + method.toGenericString());
//參數(shù)類型
Type[] gpTypes = method.getGenericParameterTypes();
Class[] pTypes = method.getParameterTypes();
for (int i = 0; i < gpTypes.length; i++) {
System.out.println("pType: " + pTypes[i]);
System.out.println("gpType: " + gpTypes[i]);
}
//異常類型
Class<?>[] xType = method.getExceptionTypes();
Type[] gxType = method.getGenericExceptionTypes();
for (int i = 0; i < xType.length; i++) {
System.out.println("ExceptionType: "+ xType[i]);
System.out.println("GenericExceptionType: " + gxType[i]);
}
//返回值
Class<?> returnClazz = method.getReturnType();
System.out.println("return class: " + returnClazz);
}
System.out.println("---------------------");
//獲取指定的方法并使用
try {
Method method = boxClass.getDeclaredMethod("setName", String.class);
method.setAccessible(true);
//成員方法,需要先實例化一個對象
Constructor constructor = null;
Constructor[] constructors = boxClass.getDeclaredConstructors();
for (Constructor cons : constructors) {
if (cons.getGenericParameterTypes().length == 0) {
constructor = cons;
break;
}
}
constructor.setAccessible(true);
Box box = (Box) constructor.newInstance();
method.invoke(box, "method");
System.out.println("name: " + box.getName());
//調(diào)用靜態(tài)函數(shù),不需要實例化對象
Method staticMethod = boxClass.getDeclaredMethod("staticMethod");
staticMethod.invoke(null, null);
} catch (Exception e) {
e.printStackTrace();
}
}
打印結(jié)果:
---------------------
method: public java.lang.String testjava.Box.getName()
return class: class java.lang.String
---------------------
method: public void testjava.Box.setName(java.lang.String)
pType: class java.lang.String
gpType: class java.lang.String
return class: void
---------------------
method: public static void testjava.Box.staticMethod()
return class: void
---------------------
method: public int testjava.Box.method3()
return class: int
---------------------
method: public <E> void testjava.Box.method4() throws E
ExceptionType: class java.lang.RuntimeException
GenericExceptionType: E
return class: void
---------------------
method: public void testjava.Box.method2()
return class: void
---------------------
method: public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
pType: long
gpType: long
pType: int
gpType: int
ExceptionType: class java.lang.InterruptedException
GenericExceptionType: class java.lang.InterruptedException
return class: void
---------------------
method: public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
pType: long
gpType: long
ExceptionType: class java.lang.InterruptedException
GenericExceptionType: class java.lang.InterruptedException
return class: void
---------------------
method: public final void java.lang.Object.wait() throws java.lang.InterruptedException
ExceptionType: class java.lang.InterruptedException
GenericExceptionType: class java.lang.InterruptedException
return class: void
---------------------
method: public boolean java.lang.Object.equals(java.lang.Object)
pType: class java.lang.Object
gpType: class java.lang.Object
return class: boolean
---------------------
method: public java.lang.String java.lang.Object.toString()
return class: class java.lang.String
---------------------
method: public native int java.lang.Object.hashCode()
return class: int
---------------------
method: public final native java.lang.Class<?> java.lang.Object.getClass()
return class: class java.lang.Class
---------------------
method: public final native void java.lang.Object.notify()
return class: void
---------------------
method: public final native void java.lang.Object.notifyAll()
return class: void
---------------------
***** Box()
***** setName(name) name:method
name: method
靜態(tài) 方法
屬性
同樣的,可以通過 java.lang.reflect.Field 獲取到類的所有屬性相關(guān)的信息。
public class Box {
public static final String TAG = "Box";
public String name = "box";
private int age = 18;
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
public static void main(String[] args) {
Class boxClass = Box.class;
//獲取所有屬性
Field[] fields = boxClass.getFields();
for (Field field : fields) {
System.out.println("field: " + field.toGenericString());
}
try {
//修改 public 的 name 變量
Field nameField = boxClass.getField("name");
//實例化一個 Box 對象
Object box = boxClass.getConstructor().newInstance();
//獲取 name 值
String name = (String) nameField.get(box);
System.out.println("name 修改前:" + name);
//修改 name 值
nameField.set(box, "filedName");
name = (String) nameField.get(box);
System.out.println("name 修改后:" + name);
//修改 private 的 age 屬性
Field ageField = boxClass.getDeclaredField("age");//getField 和 getDeclaredField 的區(qū)別是 前者只能獲取public的,后者是獲取所有的
ageField.setAccessible(true); //設(shè)置訪問權(quán)限
//獲取 age 的值
int age = (int) ageField.get(box);
System.out.println("age 修改前:" + age);
//修改 age 的值
ageField.set(box, 24);
age = (int) ageField.get(box);
System.out.println("age 修改后:" + age);
//獲取靜態(tài)的變量 TAG 的值
Field tagField = boxClass.getDeclaredField("TAG");
String tag = (String) tagField.get(null); //不需要具體對象
System.out.println("tag:" + tag);
} catch (Exception e) {
e.printStackTrace();
}
}
field: public static final java.lang.String testjava.Box.TAG
field: public java.lang.String testjava.Box.name
name 修改前:box
name 修改后:filedName
age 修改前:18
age 修改后:24
tag:Box
參考: