基本概念
java的反射机制是动态获取类的信息以及动态调用对象的方法。这种机制允许程序在运行时通过reflection apis获取一个已知名称的class的内部信息。包括:modifiers(如public,static等),superclass(如Object),实现了的Interfaces(如Serializable),也包括其fields和methods的所有信息,并可以在运行时动态改变fields或调用methods。
- 主要功能
- 在运行时判断任意一个对象所属的类。
- 在运行时构造任意一个类的对象。
- 在运行时判断任意一个类所具有的成员变量和方法。
- 在运行时调用任意一个对象的方法。
Java Reflection API简介
在JDK中,主要由以下类来实现Java反射机制,这些类(除了第一个)都位于java.lang.reflect包中。
- Class类:代表一个类,位于java.lang包下。
- Field类:代表类的成员变量(及类的属性)。
- Method:代表类的方法。
- Constructor类:代表类的构造方法。
- Array类:提供了动态创建数组,以及访问数据的元素的静态方法。
Class对象
要想使用放射,首先需要获得带操作的类所对应的Class对象。java中,无论生成某个类的多少对象,这些对象都会对应于同一个Class对象。这个Class对象是有JVM生成的,通过它能够获取整个类的结构。
获取Class对象的3中方法
- 使用Class类的静态方法
Class<?> stringClass = Class.forName("java.lang.String");
- 使用类的.class方法
Class<?> stringClass2 = String.class;
- 使用对象的getClass方法
String str = "aa";
Class<?> stringClass3 = str.getClass();
getClass方法定义在Object类中,不是静态方法,声明为final,不能被子类覆写。
使用方法
- 打印类所有方法信息
public static void main(String[] args) throws ClassNotFoundException {
// 获取class对象
Class<?> classType = Class.forName("java.lang.String");
// 返回class对象所对应类或接口中声明德尔所有方法的数组【包括私有方法】
Method[] methods = classType.getDeclaredMethods();
// 输出所有方法的声明
for (Method method : methods) {
System.out.println(method);
}
}
通过反射调用方法
public class Reflect {
public int add(int param1, int param2) {
return param1 + param2;
}
public String echo(String message) {
return "Hello " + message;
}
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
// 正常的执行手段
Reflect reflect = new Reflect();
System.out.println(reflect.add(1, 2));
System.out.println(reflect.echo("suys"));
System.out.println("================================");
// 反射的方法
// 获取class对象
Class<?> classType = Reflect.class;
// 生成新的对象
Reflect reflect1 = (Reflect) classType.newInstance();
System.out.println(reflect1 instanceof Reflect); // 判断是否是Reflect类的对象
// 通过反射调用方法
// 首先需要获得与该方法对应的Method对象,第一个参数是方法名,第二个参数是所需要的参数Class对象的数组
Method addMethod = classType.getMethod("add", new Class[]{int.class, int.class});
// 调用目标方法
Object result = addMethod.invoke(reflect1, new Object[]{1, 2});
System.out.println(result); //此时返回的是Integer类型
// 第二个方法调用采用类似的方法
}
}
两种用法
// 接口
public interface CarInterface {
public String getName();
public String getSpeed();
}
// 实现类1
public class SmallCar implements CarInterface {
@Override
public String getName() {
return "small car";
}
@Override
public String getSpeed() {
return "100-200";
}
}
// 实现类2
public class BigCar implements CarInterface{
@Override
public String getName() {
return "big car";
}
@Override
public String getSpeed() {
return "10-50";
}
}
// 测试类
public class Test {
public static void main(String[] args) throws Exception {
List<Class<?>> carInterfaces = Lists.newArrayList();
carInterfaces.add(SmallCar.class);
carInterfaces.add(BigCar.class);
for(Class<?> carInterface : carInterfaces) {
if (carInterface instanceof Class) {
Object obj = carInterface.newInstance();
System.out.println(carInterface.getName());
Method method= carInterface.getMethod("getName");
Object result = method.invoke(obj);
System.out.println(result);
}
}
}
public static void main2(String[] args) throws Exception {
List<CarInterface> carInterfaces = Lists.newArrayList();
carInterfaces.add(new BigCar());
carInterfaces.add(new SmallCar());
for(CarInterface carInterface : carInterfaces) {
if (carInterface instanceof CarInterface) {
Class<?> cc = carInterface.getClass();
System.out.println(cc.getName());
Method method= cc.getMethod("getName");
Object result = method.invoke(carInterface);
System.out.println(result);
}
}
}
}
生成对象
如果像拖过类的不带参数的构造方法来生成对象,有两种方式:
- 先获得Class对象,然后通过该Class对象的newInstance()方法直接生成
Class<?> classType = String.class;
Object obj = classType.newInstance;
- 先获得Class对象,然后通过改对象获得对应的Constructor对象,在通过Constructor对象的newInstance()方法生成(其中Customer是一个自定义的类,有一个无参数的构造函数,也有带参数的构造方法)
Class<?> classType = Customer.class;
Constructor con = classType.getConstructor(new Class[]{});
Object obj = con.newInstance(new Object[]{});
Object result = classType.getMethod("getName").invoke(obj);
如果想通过类的带参数的构造方法生成对象,只能使用上面的第二种方法。
可以看出调用构造方法生成对象的方法和调用一般方法的类似,不同的是从Constructor对象时不需要指定名字,而获取Method对象时需要指定名字。
利用反射实现对象拷贝的例子
// customer类
public class Customer {
private String name;
private String email;
public Customer() {
}
public Customer(String a, String b) {
name = a;
email = b;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
// 类copy以及测试方法
public Object copy(Object object) throws Exception {
Class<?> classType = object.getClass();
Object objectCopy = classType.getConstructor(new Class[]{}).newInstance(new Object[]{});
// 获得对象的所有成员变量
Field[] fields = classType.getDeclaredFields();
for (Field field : fields) {
// 获取成员变量的名字
String name = field.getName();
// 获取get和set方法
String firstLetter = name.substring(0, 1).toUpperCase(); // 将属性的首字母转换为大写
String getMethodName = "get" + firstLetter + name.substring(1);
String setMethodName = "set" + firstLetter + name.substring(1);
// 获取方法对象
Method getMethod = classType.getMethod(getMethodName, new Class[]{});
Method setMethod = classType.getMethod(setMethodName, new Class[]{field.getType()}); //注意set方法需要传入参数类型
// 调用get方法获取旧的对象的值
Object value = getMethod.invoke(object, new Object[]{});
// 调用set方法将这个值赋值到新的对象中去
setMethod.invoke(objectCopy, new Object[]{value});
}
return objectCopy;
}
public static void main(String[] args) throws Exception {
Customer customer = new Customer("Tom", "tom@163.com");
Reflect reflect = new Reflect();
Customer customer1 = (Customer)reflect.copy(customer);
System.out.println(customer1.getEmail() + "," + customer1.getName());
}
反射与数组
java.lang.Array类提供了动态创建和访问数组元素的各种静态方法。
下面例子创建了一个长度为10的字符串数组,接着把索引位置为5的元素设为Hello,然后在读取索引位置为5的元素的值。
public static void main(String[] args) throws Exception {
Class<?> classType = Class.forName("java.lang.String");
// 生成数组,指定元素类型和数组长度
Object array = Array.newInstance(classType, 10);
Array.set(array, 5, "hello");
System.out.println((String)Array.get(array, 5));
}
多维数组
首先区别一下下面两者
System.out.println(Integer.TYPE); ==> int
System.out.println(Integer.class); ==>java.lang.Integer
public static void main(String[] args) {
int[] dims = new int[]{5, 10, 15};
// 注意区分以下两种
System.out.println(Integer.TYPE); // int
System.out.println(Integer.class); // Integer
// 创建一个三维数组,这个数组的三个维度分别是5,10,15
Object array = Array.newInstance(Integer.TYPE, dims);
// 可变参数,也可以这样写
//Object array = Array.newInstance(Integer.TYPE, 5, 10, 15);
System.out.println(array instanceof int[][][]);
Class<?> classType = array.getClass().getComponentType(); //返回数组元素类型
System.out.println(classType); // 三维数组的元素为二维数组,输出:CLass[[
// 获得第一层的索引为3的数组,返回的是一个二维数组
Object arrayObject = Array.get(array, 3);
Class<?> classType1 = arrayObject.getClass().getComponentType(); // 返回数组的元素类型
System.out.println(classType1); // 二维数组的元素为一维数组,输出:class [
// 此处返回的是一个一维数组
arrayObject = Array.get(arrayObject, 5);
Class<?> classType2 = arrayObject.getClass().getComponentType(); //返回数组元素类型
System.out.println(classType2); // 一维数组的元素为int
// 给一维数组下标为10的位置设置值为37
Array.setInt(arrayObject, 10, 37);
int[][][] arrayCast = (int[][][]) array;
System.out.println(arrayCast[3][5][10]);
}
利用反射调用私有方法、访问私有属性
利用反射,首先是Class对象的获取,之后是Method和Field的udixiang的获取。
以Method为例,可以看到getMethod()返回的是public的Method对象,而getDeclaredMethod()返回Method对象可以是非public的。
Field的方法同理。
访问私有属性和方法,在使用前要通过AccessibleObject类(Constructor、Field和Method类的基类)中的setAccesible()方法来抑制Java访问权限的检查。
实例1.调用私有方法
// 有私有方法的类
public class PrivateClass {
private String sayHello(String name) {
return "Hello: " + name;
}
}
// 利用反射机制访问该方法
public class TestPrivate {
public static void main(String[] args) throws Exception {
PrivateClass privateClass = new PrivateClass();
Class<?> classType = privateClass.getClass();
// 获取Method对象
Method method = classType.getDeclaredMethod("sayHello", new Class[]{String.class});
// 抑制Java的访问控制检查
method.setAccessible(true);
// 如果不加上面语句,报错
// Class com.suys.java.reflect.TestPrivate can not access a member of class com.suys.java.reflect.PrivateClass with modifiers "private"
String str = (String)method.invoke(privateClass, new Object[]{"suys"});
System.out.println(str);
}
}
实例2.访问私有属性
// 有私有属性的类
public class PrivateClass2 {
private String name = "zhangsan";
public String getName() {
return name;
}
}
//测试方法
public static void main(String[] args) throws Exception {
PrivateClass2 privateClass2 = new PrivateClass2();
Class<?> classType = privateClass2.getClass();
Field field = classType.getDeclaredField("name");
field.setAccessible(true);
field.set(privateClass2, "lisi");
System.out.println(privateClass2.getName());
}
Class类
java中的Object是所有类的继承根源,其中getClass()方法返回一个Class Object。所有的类都有这个方法。
Class类十分特殊,它和其他类一样继承自Object,其实体用以表达java程序运行时的classes和interfaces,也用来表达enum、array、primitive Java types(boolean,byte,char,short,int,long,float,double)以及关键词void。
当一个class被加载,或当加载器(class loader)的defineClass()被JVM调用,JVM便自动产生一个Class对象(Class object)。
如果想借由修改Java标准库源码来观察Class对象的实际生成时机,例如在Class的constructor内添加println,那是不可以的,因为Class没有public constructor。
Class是Reflection的起源,针对任何类,唯有先为它产生一个Class object,接下来才能经由后者唤起为数十多个的Reflection APIs。