java反射教程
什麽是反射,為什麽它是有用的,以及如何使用它?
1.什麽是反射?
“反射通常是JVM中運行的程序需要檢測和修改運行時程序的行為的一種能力。”這個概念通常與內省(Introspection)混淆。以下是這兩個術語在維基百科中的定義:
- 內省是指計算機程序在運行時檢查對象類型的一種能力,通常也可以稱作運行時類型檢查。
- 反射是指計算機程序在運行時可以訪問、檢測和修改它本身狀態或行為的一種能力。
從他們的定義可以看出,內省是反射的一個子集。有些語言支持內省,但不支持反射,如C++。
內省例子:instanceof運算符用於確定一個對象是否屬於一個特定的類。
if(obj instanceof Dog){Dog d = (Dog)obj;
d.bark();
}
反射例子:Class.forName()方法通過類或接口的名稱(一個字符串和完全限定名)來返回對應Class的對象。forName方法會對類初始化。
Class<?> c = Class.forName("classpath.and.classname");
Object dog = c.newInstance();
Method m = c.getDeclaredMethod("bark", new Class<?>[0]);
m.invoke(dog);
在Java中,更關註於反射而非內省,因為你不能改變一個對象的結構。
2.為什麽我們需要反射?
反射能夠讓我們:
- 運行時檢測一個對象所屬的類;
- 運行時構造一個類的對象;
- 運行時檢測一個類的字段和方法;
- 運行時調用一個對象的任意方法;
- 修改構造函數、方法、字段的訪問權限,AccessibleObject的setAccessible(boolean flag)方法;
- 等等
反射是框架中常用的方法。
例如,JUnit通過反射來查找標記為 @Test 註解的方法,並在運行單元測試時調用這些方法。
對於Web框架,產品開發人員在配置文件中定義接口和類的實現。通過使用反射,框架可以快速動態地初始化所需要的類。
例如,Spring框架使用bean的配置,如:
<bean id="someID" class="com.programcreek.Foo"><property name="someField" value="someValue" />
</bean>
當Spring上下文處理<bean>元素時,將使用Class.forName(String)與參數"com.programcreek.Foo"來實例化類。然後,它會再次使用反射來得到<property>元素對應的setter方法並設置指定的值。
相同的機制也用於Servlet web applications:
<servlet><servlet-name>someServlet</servlet-name>
<servlet-class>com.programcreek.WhyReflectionServlet</servlet-class>
<servlet>
3.如何使用反射?
通過以下幾個典型的小例子來學習如何使用反射。
例1:獲取對象的類名稱。
package myreflection;import java.lang.reflect.Method;
public class ReflectionHelloWorld {
public static void main(String[] args){
Foo f = new Foo();
System.out.println(f.getClass().getName());
}
}
class Foo {
public void print() {
System.out.println("abc");
}
}
輸出:
myreflection.Foo例2:調用未知對象的方法。
對於下面的代碼示例,假象一下對象的類型是未知的。通過使用反射,代碼可以使用該對象,並找出對象有一個名為“print”的方法,然後調用它。
package myreflection;import java.lang.reflect.Method;
public class ReflectionHelloWorld {
public static void main(String[] args){
Foo f = new Foo();
Method method;
try {
method = f.getClass().getMethod("print", new Class<?>[0]);
method.invoke(f);
} catch (Exception e) {
e.printStackTrace();
}
}
}
class Foo {
public void print() {
System.out.println("abc");
}
} abc
例3:從Class實例創建對象
package myreflection;public class ReflectionHelloWorld {
public static void main(String[] args){
//create instance of "Class"
Class<?> c = null;
try{
c=Class.forName("myreflection.Foo");
}catch(Exception e){
e.printStackTrace();
}
//create instance of "Foo"
Foo f = null;
try {
f = (Foo) c.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
f.print();
}
}
class Foo {
public void print() {
System.out.println("abc");
}
}
示例4:獲取構造函數和創建實例。
package myreflection;import java.lang.reflect.Constructor;
public class ReflectionHelloWorld {
public static void main(String[] args){
//create instance of "Class"
Class<?> c = null;
try{
c=Class.forName("myreflection.Foo");
}catch(Exception e){
e.printStackTrace();
}
//create instance of "Foo"
Foo f1 = null;
Foo f2 = null;
//get all constructors
Constructor<?> cons[] = c.getConstructors();
try {
f1 = (Foo) cons[0].newInstance();
f2 = (Foo) cons[1].newInstance("abc");
} catch (Exception e) {
e.printStackTrace();
}
f1.print();
f2.print();
}
}
class Foo {
String s;
public Foo(){}
public Foo(String s){
this.s=s;
}
public void print() {
System.out.println(s);
}
} null
abc
此外,你可以使用Class實例來獲取該類實現的接口,父類,聲明的字段等。
例5:通過反射來修改數組的大小。
package myreflection;import java.lang.reflect.Array;
public class ReflectionHelloWorld {
public static void main(String[] args) {
int[] intArray = { 1, 2, 3, 4, 5 };
int[] newIntArray = (int[]) changeArraySize(intArray, 10);
print(newIntArray);
String[] atr = { "a", "b", "c", "d", "e" };
String[] str1 = (String[]) changeArraySize(atr, 10);
print(str1);
}
// change array size
public static Object changeArraySize(Object obj, int len) {
Class<?> arr = obj.getClass().getComponentType();
Object newArray = Array.newInstance(arr, len);
//do array copy
int co = Array.getLength(obj);
System.arraycopy(obj, 0, newArray, 0, co);
return newArray;
}
public static void print(Object obj) {
Class<?> c = obj.getClass();
if (!c.isArray()) {
return;
}
System.out.println("\nArray length: " + Array.getLength(obj));
for (int i = 0; i < Array.getLength(obj); i++) {
System.out.print(Array.get(obj, i) + " ");
}
}
}
輸出:
Array length: 101 2 3 4 5 0 0 0 0 0
Array length: 10
a b c d e null null null null null
總結
上面的代碼示例僅僅展現了Java反射提供的非常小的一部分功能。閱讀這些例子只能讓你初識Java反射,你需要在Oracle的官網閱讀相關文檔來了解更多信息。
java反射教程