一、反射机制是什么
反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;
对于任意一个对象,都能够调用它的任意一个方法和属性;
这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
二、反射机制能做什么
1.在运行时判断任意一个对象所属的类;
2.在运行时构造任意一个类的对象;
3.在运行时判断任意一个类所具有的成员变量和方法;
4.在运行时调用任意一个对象的方法;
5.生成动态代理。
三、反射机制的相关API
图片来源于 http://c.biancheng.net/view/1103.html
1.通过一个对象获得完整的包名和类名:
对象名.getClass().getName();
2.实例化Class类对象:
①clazz1 = Class.forName("完整的包名和类名");
②clazz2 = new 构造方法.getClass();
= 对象名.getClass();
③clazz3 = 类名.class;
3.获取一个对象的父类与实现的接口:
获得父类:clazz.getSuperclass();
获得所有的接口:clazz.getInterfaces();
4.获取某个类中的全部构造函数,实例化一个类的对象
①第一种方法,实例化默认构造方法:clazz.newInstance();
②第二种方法,取得全部的构造函数,可先查看每个构造函数的参数类型,再使用构造函数赋值:
clazz.getConstructors()[i].getParameterTypes()[j].getName();
clazz.getConstructors()[i].newInstance(参数);
5.获取某个类的全部属性:
本类的所有属性:
clazz.getDeclaredFields();
父类或实现的接口的所有属性:
clazz.getFields();
属性名:
fields[i].getName();
属性权限修饰符:
int mo = field[i].getModifiers();
String priv = Modifier.toString(mo);
属性类型:
field[i].getType();
属性类型名:
field[i].getType().getName();
6.获取某个类的全部方法
全部方法:
clazz.getMethods();
方法的返回类型:
methods[i].getReturnType();
某个方法的所有参数类型:
methods[i].getParameterTypes();
7.调用某个类的方法
Method method = clazz.getMethod("方法名",new Class[]{参数类型1,参数类型2,...});
method.invoke(对象名,new Object[]{参数1,参数2,...});
8.操作某个类的属性
可以直接对 private 的属性赋值:
Field field = clazz.getDeclaredField("属性名");
field.setAccessible(true);
field.set(对象名,"属性值");
四、反射机制的应用实例
1.在泛型为Integer的ArrayList中存放一个String类型的对象。
2.通过反射取得并修改数组的信息
3.通过反射机制修改数组的大小
4.将反射机制应用于工厂模式
实例1:利用反射来审查一个对象的内部
Main.java
package reflecttest; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) { // 打开连接 // 准备语句,设置参数 // 发送语句,执行并处理结果集(ResultSet) // 遍历结果集,把每一行封装成对象 /* List<Student> students = new ArrayList<>(); for (row : resultSet) { Student student = new Student(); // 要素1:要new的是哪个类 student.setId(row.getInt(1)); // 要素2:这个类中有哪些属性,并且要能调用其setter student.setName(row.getString(2)); student.setAge(row.getInt(3)); students.add(student); } */ // Java中类似镜子的机制叫做反射,可以用来审查某个对象或者某个类的内部(哪些字段,哪些方法,调用之) // 这面镜子叫做元对象(meta object),元数据 Student student = new Student(1, "zhangan", 20); inspect(student); inspect(new Teacher(1, "lisi", "软件工程")); } /** * 审查一个对象(打印类名,打印声明的字段列表,打印定义的方法列表) * @param o */ private static void inspect(Object o) { Class clazz = o.getClass(); // 获取描述对象的元数据 System.out.println("限定类名:" + clazz.getName()); System.out.println("简单类名:" + clazz.getSimpleName()); Field[] fields = clazz.getDeclaredFields(); // 包括public、private等,但排除集成的字段 // System.out.println(Arrays.toString(fields)); // 打印字段列表 for (int i = 0; i < fields.length; i++) { Field field = fields[i]; // field等元信息是跟类走的 Object value; try { // value = field.get(o); // 实际取值或者调用时得传入目标对象(与对象挂钩的) // 默认是不能访问私有字段的,改为调用其getter String getterName = "get" + StringUtils.cap(field.getName()); // getName, getAge Method getter = clazz.getDeclaredMethod(getterName); value = getter.invoke(o); } catch (Exception e) { e.printStackTrace(); value = "--- 取值错误 ---"; } System.out.println("字段:" + field.getName() + ", " + field.getType().getSimpleName() + " = " + value); } // 打印方法列表 Method[] methods = clazz.getDeclaredMethods(); for (int i = 0; i < methods.length; i++) { Method method = methods[i]; System.out.println("方法:" + method.getName() + ", " + method.getReturnType().getSimpleName()); } } }
StringUtils.java
package reflecttest; public class StringUtils { /** * 将首字母大写 * @param name * @return */ public static String cap(String name) { return name.substring(0, 1).toUpperCase() + name.substring(1); } }
Student.java
package reflecttest; public class Student { private Integer id; private String name; private int age; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } 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; } public Student(Integer id, String name, int age) { this.id = id; this.name = name; this.age = age; } public Student() { } @Override public String toString() { return "Student [id=" + id + ", name=" + name + ", age=" + age + "]"; } }
Teacher.java
package reflecttest; public class Teacher { private Integer id; private String name; private String major; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getMajor() { return major; } public void setMajor(String major) { this.major = major; } public Teacher(Integer id, String name, String major) { this.id = id; this.name = name; this.major = major; } public Teacher() { } }
实例2:利用反射给对象属性赋值
Mapper.java
package reflecttest; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; /** * 作为MyBatis的一个最原始的原型 * @author Administrator * */ public class Mapper { /** * 将row中的每一列set到目标类的对象上 * @param clazz * @param row * @return */ public static Object map(Class clazz, Map<String, Object> row) { // new一个目标对象 // new Student() try { // 获取无参构造器 Constructor constructor = clazz.getDeclaredConstructor(); Object obj = constructor.newInstance(); // 将各列set到obj的属性上,如id列就setId(?) for (String columnName : row.keySet()) { // System.out.println(columnName + " -> " + row.get(columnName)); Object value = row.get(columnName); String setterName = "set" + StringUtils.cap(columnName); // 此处忽略一个细节:如果列名对应的setter不存在应该忽略该列 Field field = clazz.getDeclaredField(columnName); Method setter = clazz.getDeclaredMethod(setterName, field.getType()); setter.invoke(obj, value); } return obj; } catch (Exception e) { throw new RuntimeException(e); } } public static void main(String[] args) { Map<String, Object> row = new HashMap<String, Object>(); row.put("id", 3); row.put("name", "王五"); row.put("age", 25); Object obj = map(Student.class, row); System.out.println(obj); } }
实例3:利用Apache Commons工具来复制对象(属性)
BeanUtilsTest.java
package reflecttest; import java.lang.reflect.InvocationTargetException; import org.apache.commons.beanutils.PropertyUtils; public class BeanUtilsTest { public static void main(String[] args) throws Exception { // 如何获取bean属性 // id, name, age Student o = new Student(5, "xyz", 30); String name = (String) PropertyUtils.getProperty(o, "name"); System.out.println("name: " + name); // 如何设置bean属性 PropertyUtils.setProperty(o, "name", "张三"); System.out.println(o.getName()); // 应该是张三 // 如何复制所有属性 Student o2 = new Student(); PropertyUtils.copyProperties(o2, o); System.out.println(o2); } }
案例4:利用反射完成javabean之间的复制
package com.myreflect; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import com.pojo.User; public class ReflectDemo { //写一个类,写一个方法完成两个数求幂值 x的n次方值 //用反射来调用此方法 //写一个工具类,完成功能是javabean之间的复制 方法的原型:copyObject(Object obj1,Object obj2); //从obj1到obj2的复制功能 //思路:Student对象 有属性:id,name,age,sex,birthday 有方法:Set和get方法 //1.创建一个学员类给其属性赋值,然后再创建一个新的学员类,将原来的学员对象的值复制到新的学员对象中 public void copy(Object obj1,Object obj2) throws Exception{ //1.拿到obj1和obj2的类型 Class c1 = obj1.getClass(); Class c2 = obj2.getClass(); //2.通过类型中的获取obj1的get方法取值。obj2的set方法来完成复制功能 Field[] fs1 = c1.getDeclaredFields(); //获取c1中以get开头属性名结尾的所有方法 for (int i = 0; i < fs1.length; i++) { String getmethodName = "get"+cap(fs1[i].getName()); Method getMethod = c1.getDeclaredMethod(getmethodName,null); String setmethodName = "set"+cap(fs1[i].getName()); Method setMethod = c2.getDeclaredMethod(setmethodName, new Class[]{fs1[i].getType()}); Object result = getMethod.invoke(obj1, null); setMethod.invoke(obj2, new Object[]{result}); } } public String cap(String name) { return name.substring(0, 1).toUpperCase() + name.substring(1); } public static void main(String[] args) throws Exception { User user1 = new User(); user1.setUsername("张三"); user1.setPwd("q2w"); User user2 = new User(); ReflectDemo rd = new ReflectDemo(); rd.copy(user1, user2); System.out.println(user2.getUsername()+" "+user2.getPwd()); } }