本篇文章,翻译自Java Reflection Tutorial
什么是反射?为什么要使用反射?如何用它?
什么是反射
在JVM中,当一个程序需要在运行时修改或者检测应用运行的行为时,则需要使用反射。反射的概念容易和自我检查(introspection)相混淆。wiki上关于反射与自我检查的定义如下,
- 自我检查(introspection):能够在运行时检测对象的类型或属性.
- 反射(reflection):能够在运行时检测和修改对象的结构和行为.
从上述定义可知,自我检查是反射的子集。有些语言是支持自我检查,但是不支持反射, 例如C++。
自我检查的例子:instanceof
操作检查对象是否属于某个类。
if(obj instanceof Dog){
Dog d = (Dog)obj;
d.bark();
}
反射的例子:Class.forName()
方法返回与给定的类名的类关联的Class
对象。forName
方法可以使得这个类实例化。
// with reflection
Class> c = Class.forName("classpath.and.classname");
Object dog = c.newInstance();
Method m = c.getDeclaredMethod("bark", new Class>[0]);
m.invoke(dog);
在Java中,反射的功能要比自我检查多,因为自我检查不能修改对象的结构。
为什么需要反射
反射有以下用途:
- 在运行时,检查对象所属的类
- 在运行时,构造一个类的对象
- 在运行时,检查一个类的域和方法
- 在运行时,调用类的任何方法
- 改变构造器、方法和域的访问标志
- 等等...
反射是框架的基本方法。
例如, JUnit使用反射区查找含有@Test注解的方法,并且在运行单元测试时,调用这些方法。
对于web框架,产品开发者定义他们自己的接口和类的实现,并且将这些接口和类放入配置文件。使用反射,可以快速的,动态的初始化这些类。
例如,Spring使用这样的bean配置文件,
当Spring上下文处理这个
元素时,它将使用Class.forName(String className)
和”com.programcreek.Foo“字符串作为参数去实例化这个类。然后,它将再次使用反射,get
属性的值和set
属性的值。
这种机制也被用于Servlet 框架中。
someServlet
com.programcreek.WhyReflectionServlet
如何使用反射
例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”的方法,那么就调用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;
}
// print
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: 10
1 2 3 4 5 0 0 0 0 0
Array length: 10
a b c d e null null null null null
总结
上面代码展示了Java反射的一小部分使用方法,更详细的介绍,参考这里。