所謂的反射機制就是java語言在運行時擁有的一項自觀的能力。通過這種能力可以徹底地瞭解自身的情況爲下一步的動作做準備。下面介紹java的反射機制。
Java的反射機制的實現要藉助於4個類:Class、Constructor、Field、Method,其中Class代表的是類對象,Constructor代表類的構造器對象,Field代表類的屬性對象,Method代表類的方法對象。通過這四個對象我們可以粗略看到一個類的各個組成部分。
Class:
程序運行時,java運行時系統會對所有的對象進行運行時類型的處理。這項信息記錄了每個對象所屬的類,虛擬機通常使用運行時類型信息選擇正確的方法來執行。我們可以藉助Object類中定義的getClass()方法要得到指定對象的類對象,然後通過分析這個對象得到我們想要的信息,而Class本身又有很多重要的方法,此處重點將和Constructor,Field,Method類有關係的方法:
ü Class.forName(“類名”) 表示載入指定的類,調用getDeclaredMethods來
獲取這個類中定義的方法列表
實例:
package com.yilong.reflect;
import java.lang.reflect.Method;
public class Test1 {
public static void main(String[] args) {
try {
Class c = Class.forName(args[0]);
Method m[] = c.getDeclaredMethods();
for(int i=0; i<m.length; i++) {
System.out.println(m[i].toString());
}
} catch(Throwable e) {
e.printStackTrace();
}
}
}
執行方法:
編譯:javac –d . Test1.java
運行:java com.yilong.reflect.Test1 java.util.ArrayList
Constructor:
Constructor getConstructor(Class[] params) – 獲得使用特殊的參數類型的公共構造函數;
Constructor[] getConstructors() – 獲得類的所有公共構造函數;
Constructor getDeclaredConstructor(Class[] params) – 獲得使用特定參數類型的構造函數(上面是公共);
Constructor[] getDeclaredConstructors() – 獲得類的所有構造函數(上面是”公共”);
Field:
Field getField(String name) – 獲得命名的公共字段;
Field[] getFields() – 獲得類的所有公共字段;
Field getDeclaredField(String name) – 獲得類聲明的命名的字段;
Fidld[] getDeclaredFields() – 獲得類聲明的所有字段。
Method:
Method getMethods(String name, Class[] params) – 使用特定的參數類型,獲得命名的公共方法;
Method[] getMethods() – 獲得類的所有公共方法;
Method getDeclaredMethod(String name, Class[] params) – 使用特定的參數類型,獲得聲明的命名的方法;
Method[] getDeclaredMethods() – 獲得類聲明的所有的方法
要向使用JAVA的Reflection,需要遵循三個步驟:
第一步:獲得你想要操作的類的java.lang.Class對象。在運行中的JAVA程序中,用
java.lang.Class類來描述類和接口等;
下面是獲得Class對象的一些方法:
Class c = Class.forName(“java.lang.String”);
Class c = int.class;
Class c = Integer.TYPE;
第二步:調用諸如getDeclaredMethods的方法,取得該類中定義的所有方法的列表;
第三步:使用reflection的API來操作這些信息。
下面是一些具體的實例:
Java中使用Class.forName(“”).newInstance();對對象進行實例化,同時還需要掌握對其對象方法的調用方法。
下面結合具體例子進行分析:
Ø 文件com.yilong.test.instance.NewClass.java
package com.yilong.test.instance;
public class NewClass {
public boolean method() {
System.out.println("ok!");
return true;
}
}
Ø 文件com.yilong.test.instance.Main.java
package com.yilong.test.instance;
import java.lang.reflect.Method;
public class Main {
public static void main(String[] args) throws Exception {
Object clazz = Class
.forName("com.yilong.test.instance.NewClass")
.newInstance();
Method method = clazz.getClass().getMethod("method");
Class returntype = method.getReturnType();
method.invoke(clazz);
System.out.println(method.getName() + "|"
+ returntype.getName());
}
}
注意事項1:上面的getMethod()方法只適用於調用的方法沒有參數的情況,如果方法要傳遞參數,還需要指定參數的類型(因爲有重載),對應地,method.invoke()方法也是要傳遞具體的參數值的。
ü public Method getMethod(String name, Class<?>… parameterTypes)
throws NoSuchMethodException,SecurityException
ü public Object invoke(Object obj, Object... args)
throws IllegalAccessException,
IllegalArgumentException, InvocationTargetException
ü 另外,還有一個重載的方法是可以拿到一個類裏定義的所有的方法:
Class clazz = Class.forName("com.yilong.test.instancemethod.NewClass");
Method[] methods = clazz.getMethods();
//clazz只是拿到該類,還沒實例化,如果實例化了,那麼就要調用//newInstance.getClass().getMethods();
下面是囊括了比較多的情況的例子
Ø 文件NewClass.java
package com.yilong.test.instance;
public class NewClass {
public void method0() {
System.out.print("0個參數的方法調用成功!");
}
public boolean method1(int id) {
System.out.print("1個參數的方法調用成功!");
return true;
}
public String method2(int id, String name) {
System.out.print("2個參數的方法調用成功!");
return "success";
}
public int method3(String[] strs) {
System.out.print("參數爲數組的方法調用成功!");
return 1;
}
}
Ø 文件Main.java
package com.yilong.test.instance;
import java.lang.reflect.Method;
public class Main {
public static void main(String[] args) throws Exception {
Object clazz = Class.forName
("com.yilong.test.instance.NewClass").newInstance();
//調用0個參數的方法
Method method0 = clazz.getClass().getMethod("method0");
Class returntype0 = method0.getReturnType();
method0.invoke(clazz);
System.out.println(" -- " + method0.getName() + "|" +
returntype0.getName());
//調用1個參數的方法
Class[] parameterTypes1 = new Class[1];
parameterTypes1[0] = int.class;
Method method1 = clazz.getClass().getMethod("method1",
parameterTypes1);
Class returntype1 = method1.getReturnType();
method1.invoke(clazz, 1);
System.out.println(" -- " + method1.getName() + "|" +
returntype1.getName());
//調用2個參數的方法
Class[] parameterTypes2 = new Class[2];
parameterTypes2[0] = int.class;
parameterTypes2[1] = String.class;
Method method2 = clazz.getClass().getMethod("method2",
parameterTypes2);
Class returntype2 = method2.getReturnType();
method2.invoke(clazz, 2, "ok");
//或者
Object[] arguments = new Object[2];
arguments[0] = 2;
arguments[1] = "ok";
method2.invoke(clazz, arguments);
System.out.println(" -- " + method2.getName() + "|" +
returntype2.getName());
//調用參數爲數組的方法
Class[] parameterType3 = new Class[1];
parameterType3[0] = String[].class;
Method method3 = clazz.getClass().getMethod("method3",
parameterType3);
Class returntype3 = method3.getReturnType();
Object[] arguments3 = new Object[1];
String[] strs = new String[2];
strs[0] = "aa";
strs[1] = "bb";
arguments[0] = strs;
method3.invoke(clazz, arguments3);
System.out.println(" -- " + method3.getName() + "|" +
returntype3.getName());
}
}
注意事項2:通過Class.forName("com.yilong.test.instance.NewClass");方法得到的Class只能是放在classPath下面的class文件生成的,即對應項目中的bin目錄下的文件,也就是說前面的目錄已經默認是”項目根路徑 + bin”。而通過netURL方式new出來得到Class則不一定,具體實例如下:
System.out.println(System.getProperty("user.dir"));
//D:/MyEclipse/Proxy1
URL[] urls = new URL[] {new URL("file:/" +
System.getProperty("user.dir") + "/src")};
URLClassLoader ul = new URLClassLoader(urls);
Class c = ul.loadClass("com.yilong.designpattern.proxy.TimeProxy");
System.out.println(c);
項目中目錄狀態如下:
注意事項3:上述的newInstance()方法只能實例化沒有參數的構造方法,要實例化有參數的構造方法,可以:
Constructor ctr = c.getConstructor(Moveable.class);//指定參數類型
Moveable m = (Moveable)ctr.newInstance(new Tank());//傳遞參數
Ø 文件Main.java
package com.yilong.test.instancemethod;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class Main {
public static void main(String[] args) {
try {
Class clazz = Class.forName
("com.yilong.test.instancemethod.NewClass");
System.out.println(clazz);
//調用1個參數的構造方法
Constructor ctr1 = clazz.getConstructor(int.class);
System.out.println(ctr1);
ctr1.newInstance(1);
//調用2個參數的構造方法
Constructor ctr2 = clazz.getConstructor(int.class,
String.class);
System.out.println(ctr2);
ctr2.newInstance(2, "ok");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
Ø 文件NewClass.java
package com.yilong.test.instancemethod;
public class NewClass {
public NewClass(int id) {
System.out.println("1個參數的構造方法調用成功!");
}
public NewClass(int id, String str) {
System.out.println("2個參數的構造方法調用成功!");
}
public void method(String[] strs, int i) {
System.out.println("success!");
}
}