Java的反射(reflection)機制是指在程序的運行狀態中,可以構造任意一個類的對象,可以瞭解任意一個對象所屬的類,可以瞭解任意一個類的成員變量和方法,可以調用任意一個對象的屬性和方法。這種動態獲取程序信息以及動態調用對象的功能稱爲Java語言的反射機制。
我們知道對於一個類或者說是class文件比較準確,是JVM將描述的類的數據從class文件加載到內存(方法區),並且對數據進行校驗和初始化,最終形成可以被虛擬機直接使用的Java類型。其實對於任何一個Java中的任意一個類型,都有一個對應的Class對象用於描述該類的數據,Class是Java反射機制的核心API,它是我們獲取類數據的基礎。一般來說,我們有三種方法去獲取一個類的Class對象:
package cn.org.microservice.java.reflection;
//創建一個類,然後獲取該類的Class對象
public class Reflection {
.......
//1.通過對象的方式獲取
Reflection reflection = new Reflection();
Class<? extends Reflection> clazz1 = reflection.getClass();
//2.通過Class.forName()獲取
String className = "cn.org.microservice.java.reflection.Reflection";
Class<?> clazz2 = Class.forName(className);
//3.直接通過類型.class獲取
Class<Reflection> clazz3 = Reflection.class;
//4.通過 通過ClassLoader的loadClass()獲取
Class<?> clazz4 = ClassReflection.class.getClassLoader().loadClass(className);
//代碼中clazz1,clazz2.clazz3.clazz4指向的是同一個Class對象
//打印出的的是同一個對象
System.out.println(clazz1 + "," + clazz2 + "," + clazz3+ "," + clazz4);
System.out.println(clazz1.hashCode() + "," + clazz2.hashCode()+ "," + clazz3.hashCode() + "," + clazz4.hashCode());
}
每一個類都會對應且只有一個Class實例,這個示例存儲這該類的數據信息,包括包(Package),類名稱(String),全類名稱(String),構造方法(Constructor),方法(Method),字段(Field),父類(Class),接口(Class),註解等相關信息,我麼可以在運行時或者這些信息:相關API如下:
public class ClassReflection {
public static void main(String[] args) throws ClassNotFoundException {
String className = "cn.org.microservice.java.reflection.Reflection";
Class<Reflection> clazz1 = Reflection.class;
//獲取包信息
Package package1 = clazz1.getPackage();
//獲取構造方法信息
Constructor<?>[] constructors = clazz1.getConstructors();
Constructor<?> constructor = clazz1.getConstructor();
//獲取字段信息,其中getField只能獲取public聲明的字段,getDeclaredField可以獲取所有字段
Field field = clazz1.getField("name");
Field[] fields1 = clazz1.getFields();
Field field2 = clazz1.getDeclaredField("protectedField");
Field[] fields2 = clazz1.getDeclaredFields();
//獲取方法信息 其中getField只能獲取getMethod聲明的字段,getDeclaredMethod可以獲取所有字段
Method Method1 = clazz1.getMethod("setName", String.class);
Method[] Methods1 = clazz1.getMethods();
Method Method2 = clazz1.getDeclaredMethod("privateMethod",int.class);
Method[] Methods2 = clazz1.getDeclaredMethods();
//獲取接口信息
Class <?>[] clazzs1 = clazz1.getInterfaces();
Type [] interfacetypes = clazz1.getGenericInterfaces();
//獲取父類信息
Class<? super Reflection> clazzs2 = clazz1.getSuperclass();
Type superClasstypes = clazz1.getGenericSuperclass();
//獲取註解信息
Annotation [] auAnnotations = clazz1.getAnnotations();
//獲取類的修飾符
clazz1.getModifiers();
//是否是接口
clazz1.isInterface();
//獲取全類名
clazz1.getName();
//獲取類名
clazz1.getSimpleName();
}
}
Field類:
Field類用於描述Class中的Field的相關信息。我們可以通過getField(s)獲取public修飾的字段、getDeclaredField(s)等方法獲取非public方法獲取的字段。然後通過Field實例獲取Field的相關信息:
String className = "cn.org.microservice.java.reflection.Reflection";
Class<?> clazz = Class.forName(className);
Field field = clazz.getField("name");
Reflection r = new Reflection();
// 獲取字段名稱
field.getName();
// 獲取字段上註解
Annotation[] annotations = field.getAnnotations();
//爲獲取Reflection的字段name的值
field.get(r);
//爲Reflection字段name設置值
field.set(r, "pharos");
Constructor類:
Constructor類用於描述Class中的Constructor的相關信息。我們可以通過getConstructor(s)獲取public修飾的構造方法、getDeclaredConstructor(s)等方法獲取非public修飾的構造方法。然後通過Constructor實例獲取Constructor的相關信息,並且可以通過構造方法實例化對象。下面是常用的Constructor API。
//通過無參構造方法實例化對象
Constructor<?> constructor = clazz.getConstructor();
Reflection reflection = (Reflection) constructor.newInstance();
//通過有參構造方法實例化對象
Constructor<?> constructor1 = clazz.getConstructor(String.class);
Reflection reflection1 = (Reflection) constructor1.newInstance("pharos");
Constructor<?> constructor1 = clazz.getConstructor(String.class);
Reflection reflection1 = (Reflection) constructor1.newInstance("pharos");
Constructor<?> constructor2 = clazz.getDeclaredConstructor(int.class);
//如果是非public修飾的構造方法,使用其初始化對象時,需要調用setAccessible,並設置爲true,可訪問
constructor2.setAccessible(true);
Reflection reflection2 = (Reflection) constructor2.newInstance(10);
//獲取構造方法上的註解
constructor.getAnnotations();
//獲取構造方法中參數註解
constructor.getParameterAnnotations();
//獲取所有參數
constructor.getParameters();
//獲取參數數量
constructor.getParameterCount();
//獲取參數類型
constructor.getParameterTypes();
Annotation類:一般我們使用getAnnotations()獲取到所有註解,然後根據不同的註解類型,做處理。
Annotation [] annotations = Reflection.class.getAnnotations();
for(Annotation annotation : annotations) {
System.out.println(annotation.annotationType());
}
Package類:一把我們使用getPackage()獲取一個類的包,也可以通過靜態方法getPackages()獲取虛擬機加載的所有包。
Package package1 = Reflection.class.getPackage();
System.out.println(package1.getName());
Package [] package2 =Package.getPackages();
for(Package package3 : package2) {
System.out.println(package3.getName());
}
Method類:Method類用於描述Class中的Method的相關信息。我們可以通過getMethod(s)獲取public修飾的方法,通過getDeclaredMethod(s)獲取非public修飾的方法。
Class<Reflection> clazz1 = Reflection.class;
Method method1 = clazz1.getDeclaredMethod("privateMethod", int.class);
Reflection instance = clazz1.newInstance();
//同構造方法類似,我麼可以獲取方法上的註解,參數中的註解,參數,參數類型,參數數量
method1.getAnnotations();
method1.getParameterAnnotations();
method1.getParameters();
method1.getParameterCount();
method1.getParameterTypes();
/獲取返回值類型
method1.getReturnType();
//我們可以通過invoke方法執行獲取到的方法
Method method1 = clazz1.getDeclaredMethod("privateMethod", int.class);
//與構造方法類似,如果是非public修飾的方法,調用前需要設置訪問屬性爲true,
method1.setAccessible(true);
method1.invoke(instance, 10);
其實Java的反射用起來還是比較簡單的,就是通過類的Class實例,獲取類的數據,然後根據獲取到的數據進行處理,詳細的Java 反射API 可以參考官方的java.lang.reflect包文檔