反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力。Reflection 是 Java 程序开发语言的特征之一,它允许运行中的 Java 程序对自身进行检查,或者说“自审”,并能直接操作程序的内部属性和方法。他主要依托JVM中关于类的元数据信息来完成这一功能,有过C++编程经验的同学应该很了解C++中实现多态的虚表吧,在这里的元数据其实和虚表类似,起码在理解反射的时候可以这么认为。
Java的反射机制主要能实现一下功能:
- 在运行时判断任意一个对象所属的类
- 在运行时构造一个类的对象
- 在运行时判断任意一个类具有的成员变量和方法
- 在运行时调用任意一个对象的方法
注意大前提,都是在运行时,通常的语言的运行时状态是不能修改的,而java的强大的反射机制,使得java程序员可以操作类的运行时状态和信息。
Reflection 是 Java 被视为动态(或准动态)语言的关键,允许程序在执行期 Reflection 任何已知名称之 class 的內部信息,包括 package、type parameters、super class(父类)、implemented interfaces(实现的接口)、inner classes, outer class, fields(属性)、constructors(构造函数)、methods(方法)、modifiers(修饰符),并可于执行期生成instances、变更 fields 內容或调用 methods。换句话说,就是只要知道类的名字,程序员就可以知道类的所有信息,甚至在执行期生成instances、变更 fields 內容或调用 methods。
何谓动态语言:程序在运行期间,允许改变程序的结构或变量类型,如python,在变量使用前不需要声明他的类型,运行时会根据内容自动改变他的类型,但是对于C/C++来说,在变量使用前,必须首先声明他的类型。根据这种定义,java并不是动态语言,但是他却有着和动态语言相关的一个机制那就是反射。用在java身上就是指我们可以在运行期加载、探知和使用编译器完全未知的classes。换句话说,java可以加载一个运行时才得知名称的class,获悉其完整构造(但是不包括method的定义),并生成这种对象实例,或对其field设置或调用其method。
实现java反射机制的相关的类:(以下解释主要来自jdk_api)
Java的反射所需要的类并不多,它们分别是:Field、Constructor、Method、Class、Object,下面我将对这些类做一个简单的说明。
- Field类:提供有关类或接口的属性的信息,以及对它的动态访问权限。反射的字段可能是一个类(静态)属性或实例属性,简单的理解可以把它看成一个封装反射类的属性的类。
- Constructor类:提供关于类的单个构造方法的信息以及对它的访问权限。这个类和Field类不同,Field类封装了反射类的属性,而Constructor类则封装了反射类的构造方法。
- Method类:提供关于类或接口上单独某个方法的信息。所反映的方法可能是类方法或实例方法(包括抽象方法)。 这个类不难理解,它是用来封装反射类方法的一个类。
- Class类:类的实例表示正在运行的 Java 应用程序中的类和接口。枚举是一种类,注释是一种接口。每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。Class对象是java反射机制的入口,有了类的Class对象,就有了类的一切!
- Object类:每个类都使用 Object 作为超类。所有对象(包括数组)都实现这个类的方法。
要获得一个对象的Class对象,有三种方式:
- ClassName.class;
- obj.getClass();
- Class.forName(“class name”);
正如前面说的,有了类的Class的对象,就有了类的一切,这就是类的元数据,反射的一切从这里开始!
Spring 的ioc主要是使用了工厂模式,核心还是java的反射机制。
下面给出一些反射的例子,以便更好的理解反射。
这是两个用于测试的类:
package reflect;
public class TestClass {
}
package reflect;
public class User{
private String name;
private int age;
//在提供了有参的构造函数的时候,尽量显示的给出无参的
public User(){
}
//这里提供了一个有参数的构造函数
public User(String name){
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "NAME :" + this.name + " AGE: " + this.age;
}
public void sayHello(String name,int age){
System.out.println("hello:"+"NAME :" + name + " AGE: " + age);
}
public void sayHello(){
System.out.println("hello nothing");
}
public int add(int a,int b){
return a+b;
}
}
获得类的主要信息:
package reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
public class Reflect {
//
public static void testGetClassName() {
TestClass test = new TestClass();
// 取得取得TestClass类的完整类名
String testName = test.getClass().getName();
System.out.println(testName);
}
//三种获得Class多的方式
public static void testInstance(){
Class<?> demo1= null;
Class<?> demo2= null;
Class<?> demo3= null;
try {
demo1 = Class.forName("reflect.TestClass");
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
demo2 = new TestClass().getClass();
demo3 = TestClass.class;
System.out.println("类名:"+demo1.getName());
System.out.println("类名:"+demo2.getName());
System.out.println("类名:"+demo3.getName());
}
//通过类名使用默认构造函数实例化类对象
public static void testInstanceFromName(){
User user = null;
try {
user = (User)Class.forName("reflect.User").newInstance();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
user.setAge(12);
user.setName("raiet");
System.out.println(user);
}
//使用有参数的构造函数实例化类对象
public static void testInstanceFromNameAndParam(){
String name = "raiet";
//1,取出类名对应的Class
Class<?> clazz = null;
try {
clazz = Class.forName("reflect.User");
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//2,取出所有的构造函数
Constructor<?> cons[] = clazz.getConstructors();
System.out.println("构造函数有:");
for(Constructor con:cons){
System.out.println(con);
}
//3,根据相应的参数构造实例
User user1 = null;
User user2 = null;
try {
user1 = (User)cons[0].newInstance();
user2 = (User)cons[1].newInstance("raiet");
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(user1);
System.out.println(user2);
//也可以使用下面的方式来实现
try {
Constructor con= null;
//取得无参数的构造函数
con = clazz.getConstructor(new Class[]{});
User user = (User) con.newInstance(null);
System.out.println(user);
//取得只有一个带有string类新参数的构造函数
con = clazz.getConstructor(String.class);
//传入构造函数的参数
user = (User) con.newInstance("raiet");
System.out.println(user);
} catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//通过Class调用它的方法
public static void testInvokeMethod(){
System.out.println("test invoke");
Class<?> user = null;
try {
user = Class.forName("reflect.User");
Method method = user.getMethod("sayHello");
method.invoke(user.newInstance());
method = user.getMethod("sayHello", String.class,int.class);
method.invoke(user.newInstance(),"raiet001",25);
method = user.getMethod("add", int.class,int.class);
Object obj = method.invoke(user.newInstance(), 200,300);
System.out.println((Integer)obj);
/*
* 其实上面的每段代码都等价于
* User user = new User();
* user.add(200,300)
* 但是他们的区别就在于,一个是在运行期确定调用关系(参数有外部传入),
* 一个是在编译器就决定了程序的行为
* */
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//根据类名获得类中数据成员及其属性
public static void testGetProperty(){
Class<?> user = null;
try {
user = Class.forName("reflect.User");
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Field[] fields = user.getDeclaredFields();
for(Field field:fields){
int mo;
//获取权限修饰符
mo = field.getModifiers();
String priv = Modifier.toString(mo);
//获取属性类型
Class<?> type = field.getType();
System.out.println(priv + " " + type.getName() + " "+field.getName() + ";");
}
}
//返回类的所有信息。。。。
//返回类的所有的方法名,包括修饰符,返回值,异常(住哟通过反射机制可以取出所有的method,包括private的)
public static void getAllMethods(){
Class<?> obj = null;
try {
obj = Class.forName("java.lang.String");
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("输出所有的方法信息");
Method[] methods = obj.getDeclaredMethods();
for(Method method : methods){
System.out.println(method);
}
System.out.println("输出所有的字段信息");
Field[] fields = obj.getFields();
for(Field field : fields){
System.out.println(field);
}
System.out.println("输出所有的构造器信息");
Constructor<?>[] cons = obj.getConstructors();
for(Constructor<?> con : cons){
System.out.println(con);
}
System.out.println("输出所有的接口信息");
Class<?>[] ins = obj.getInterfaces();
for(Class clazz: ins){
System.out.println(clazz);
}
}
public static void main(String[] args) {
testGetClassName();
testInstance();
testInstanceFromName();
testInstanceFromNameAndParam();
testInvokeMethod();
testGetProperty();
getAllMethods();
}
}
下面是一个用反射机制实现的赋值对象的例子:
package reflect;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class BeanCopy {
//一个拷贝java bean的方法,内部使用反射机制实现
public Object copy(Object obj){
Object copyObj= null;
//先取得的Class对象
Class<?> clazz = obj.getClass();
try {
copyObj = clazz.newInstance();
} catch (InstantiationException | IllegalAccessException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
//取得所有的属性,注意这里不要使用getFields方法,那样只能取得public的属性
Field[] fields = clazz.getDeclaredFields();
System.out.println("*********************************************");
System.out.println("类中的属性:");
for(Field field:fields){
//获取字段的名字,以便后面用来拼接get和set方法
String fieldName = field.getName();
//取得第一个字母,并转换为大写,否和普通java bean的名字
String firstLetter = fieldName.substring(0, 1).toUpperCase();
//拼接get和set方法名
String getMethodName = "get" + firstLetter + fieldName.substring(1);
String setMethodName = "set" + firstLetter + fieldName.substring(1);
try {
//取得get和set方法
Method getMethod = clazz.getMethod(getMethodName, null);//get方法没有参数
Method setMethod = clazz.getMethod(setMethodName, field.getType());
//调用get和set方法,完成赋值
Object value = getMethod.invoke(obj, null);
System.out.println(fieldName + " : "+ value);
setMethod.invoke(copyObj, value);
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("*********************************************");
return copyObj;
}
public static void main(String[] args){
User user = new User();
user.setAge(10);
user.setName("haha");
//BeanCopy beanCopy = new BeanCopy();
User newUser = (User)new BeanCopy().copy(user);
System.out.println(newUser);
}
}
这里有几个关于反射的多数功能的实例:
http://www.cnblogs.com/rollenholt/archive/2011/09/02/2163758.html
http://www.oschina.net/code/snippet_127825_6594