Java反射 之 初體驗
筆者最近在java編程實踐中接觸到Java的反射機制,於是粗淺地做了一些瞭解,在此記錄一下。
java反射的主要作用體現在爲JVM動態加載類,獲取類的成員,方法,構造器等提供了一種手段。
Java反射框架主要提供以下功能:
- 1.在運行時判斷任意一個對象所屬的類;
- 2.在運行時構造任意一個類的對象;
- 3.在運行時判斷任意一個類所具有的成員變量和方法(通過反射甚至可以調用private方法);
- 4.在運行時調用任意一個對象的方法
重點:是運行時而不是編譯時
反射的主要用途在於開發框架,很多框架(比如Spring)都是配置化的(比如通過XML文件配置JavaBean,Action之類的),爲了保證框架的通用性,它們可能需要根據配置文件加載不同的對象或類,調用不同的方法,這個時候就必須用到反射——運行時動態加載需要加載的對象。
1、獲得Class對象
方法有三種
(1)使用Class類的forName靜態方法:
1 2 3 4 5 | public static Class<?> forName(String className) ``` 在JDBC開發中常用此方法加載數據庫驅動: ```java Class.forName(driver); |
(2)直接獲取某一個對象的class,比如:
1 2 | Class<?> klass = int.class; Class<?> classInt = Integer.TYPE; |
(3)調用某個對象的getClass()方法,比如:
1 2 | StringBuilder str = new StringBuilder("123"); Class<?> klass = str.getClass(); |
2、判斷是否爲某個類的實例
一般地,我們用instanceof關鍵字來判斷是否爲某個類的實例。同時我們也可以藉助反射中Class對象的isInstance()方法來判斷是否爲某個類的實例,它是一個Native方法:
1 | public native boolean isInstance(Object obj); |
3、創建實例
通過反射來生成對象主要有兩種方式。
(1)使用Class對象的newInstance()方法來創建Class對象對應類的實例。
1 2 | Class<?> c = String.class; Object str = c.newInstance(); |
(2)先通過Class對象獲取指定的Constructor對象,再調用Constructor對象的newInstance()方法來創建實例。這種方法可以用指定的構造器構造類的實例。
1 2 3 4 5 6 7 | //獲取String所對應的Class對象 Class<?> c = String.class; //獲取String類帶一個String參數的構造器 Constructor constructor = c.getConstructor(String.class); //根據構造器創建實例 Object obj = constructor.newInstance("23333"); System.out.println(obj); |
4、獲取方法
獲取某個Class對象的方法集合,主要有以下幾個方法:
getDeclaredMethods()方法返回類或接口聲明的所有方法,包括公共、保護、默認(包)訪問和私有方法,但不包括繼承的方法。
1 | public Method[] getDeclaredMethods() throws SecurityException |
getMethods()方法返回某個類的所有公用(public)方法,包括其繼承類的公用方法。
1 | public Method[] getMethods() throws SecurityException |
getMethod方法返回一個特定的方法,其中第一個參數爲方法名稱,後面的參數爲方法的參數對應Class的對象
1 | public Method getMethod(String name, Class<?>... parameterTypes) |
5、獲取構造器信息
獲取類構造器的用法與上述獲取方法的用法類似。主要是通過Class類的getConstructor方法得到Constructor類的一個實例,而Constructor類有一個newInstance方法可以創建一個對象實例:
1 | public T newInstance(Object ... initargs) |
此方法可以根據傳入的參數來調用對應的Constructor創建對象實例~
6、獲取類的成員變量(字段)信息
主要是這幾個方法,在此不再贅述:
getFiled: 訪問公有的成員變量
getDeclaredField:所有已聲明的成員變量。但不能得到其父類的成員變量
getFileds和getDeclaredFields用法同上(參照Method)
7、調用方法
當我們從類中獲取了一個方法後,我們就可以用invoke()方法來調用這個方法。invoke方法的原型爲:
1 2 3 | public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException |
下面給一個例子:
public class test1 {
public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Class<?> klass = methodClass.class;
//創建methodClass的實例
Object obj = klass.newInstance();
//獲取methodClass類的add方法
Method method = klass.getMethod("add",int.class,int.class);
//調用method對應的方法 => add(1,4)
Object result = method.invoke(obj,1,4);
System.out.println(result);
}
}
class methodClass {
public final int fuck = 3;
public int add(int a,int b) {
return a+b;
}
public int sub(int a,int b) {
return a+b;
}
}
8、利用反射創建數組
數組在Java裏是比較特殊的一種類型,它可以賦值給一個Object Reference。下面我們看一看利用反射創建數組的例子:
/**
* 用反射來創建數組
* @param args
*/
public static void main(String[] args) {
try {
Class<?> clazz = Class.forName("java.lang.String");
Object array = Array.newInstance(clazz, 10);
Array.set(array, 0, "Nuist");
Array.set(array, 1, " is");
Array.set(array, 2, " a");
Array.set(array, 3, " good");
Array.set(array, 4, " school.");
for(int i = 0; i < 5; i ++) {
if(null != Array.get(array, i) || !((String) Array.get(array, i)).equals("")) {
System.out.print(Array.get(array, i).toString());
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
1 2 3 4 | public static Object newInstance(Class<?> componentType, int length) throws NegativeArraySizeException { return newArray(componentType, length); } |
而newArray()方法是一個Native方法,它在Hotspot JVM裏的具體實現我們後邊再研究,這裏先把源碼貼出來
1 2 | private static native Object newArray(Class<?> componentType, int length) throws NegativeArraySizeException; |