一、 什么是反射
反射是指程序在运行时能够获取其自身结构(如类、接口、字段和方法)的能力。它提供了一种机制,使程序能够在运行时获得关于类及其成员的详细信息,并能直接操作任意对象的内部状态。
二、反射的使用
Java的反射机制主要通过java.lang.reflect包提供的类来实现,其中包括:
- Class:代表类和接口的元数据
- FieId:表示类的成员变量(包括静态变量)
- Method:表示类的方法
- Constructor:表示类的构造器
获取Class的方式
获取Class方式有三种
- 通过Class.forName("全类名")
- 通过类名.class
- 通过对象名.getClass()
public class ReflectDemo {
public static void main(String[] args)throws Exception {
// 1、Class.forName("全类名")
// 常用
Class class1 = Class.forName("com.reflect.Student");
// 2、类名.class
//当做参数传递
Class class2 = Student.class;
// 3、对象名.getClass()
// 当我们已经有了这个类的对象时,才可以使用
Student student = new Student();
Class class3 = student.getClass();
}
}
获取构造方法
Studeng类
public class Student {
private String name;
private Integer age;
private Student(String name, Integer age) {
this.name = name;
this.age = age;
}
public Student() {
}
private Student(String name) {
this.name = name;
}
public Student(Integer age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
获取方式
public class ReflectDemo1 {
public static void main(String[] args)throws Exception {
Class class1 = Class.forName("com.reflect.Student");
// 获取所有构造方法---公共
Constructor[] cons = class1.getConstructors();
for(Constructor constructor : cons){
System.out.println("公共---" + constructor);
}
// 获取所有构造方法---所有
Constructor[] cons2 = class1.getDeclaredConstructors();
for (Constructor constructor : cons2){
System.out.println("所有---" +constructor);
}
// 获取单个构造方法---公共
Constructor con = class1.getConstructor();
System.out.println("单个---" +con);
// 获取单个构造方法---所有---带参数
Constructor con1 = class1.getDeclaredConstructor(String.class);
System.out.println("带String参数---" +con1);
// 获取权限修饰符--- 0-没有 1-public 2-private 4-protected
Constructor con2 = class1.getDeclaredConstructor(String.class, Integer.class);
int modifiers = con2.getModifiers();
System.out.println("权限修饰符---" +modifiers);
// 获取构造方法的参数
Parameter[] parameters = con2.getParameters();
for(Parameter parameter : parameters){
System.out.println("获取构造方法的参数---" +parameter);
}
}
}
以上代码运行结果
获取成员变量
public class ReflectDemo2 {
public static void main(String[] args) throws Exception {
Class class1 = Class.forName("com.reflect.Student");
// 获取所有成员变量---所有
Field[] fields = class1.getDeclaredFields();
for(Field field : fields){
System.out.println("所有---" + field);
}
// 获取单个成员变量---所有
Field name = class1.getDeclaredField("name");
System.out.println("单个---" + name);
// 获取权限修饰符
int modifiers = name.getModifiers();
System.out.println("权限修饰符---" + modifiers);
// 获取成员变量的名字
String n = name.getName();
System.out.println("成员变量名---" + n);
// 获取成员变量的类型
Class<?> type = name.getType();
System.out.println("类型---" + type);
// 获取成员变量记录的值
Student student = new Student("zhangsan",18);
// 由于name是private的,所以需要暴力反射,临时取消权限校验
name.setAccessible(true);
String o = (String) name.get(student);
System.out.println("成员变量值---" + o);
// 修改对象里面记录的值
name.set(student,"lisi");
System.out.println(student);
}
以上代码执行结果
获取成员方法
public class ReflectDemo3 {
public static void main(String[] args) throws Exception{
Class class1 = Class.forName("com.reflect.Student");
// 获取所有的方法对象(包含父类中所有的公共方法)
Method[] methods = class1.getMethods();
for(Method method : methods){
// 输出结果:
// public boolean java.lang.Object.equals(java.lang.Object) 该方法是Object类的
// ...
System.out.println(method);
}
// 获取所有的方法对象(不获取父类的方法,可以获取本类的私有方法)
Method[] methods1 = class1.getDeclaredMethods();
for(Method method : methods1){
// 输出结果:
// public java.lang.Integer com.reflect.Student.getAge()
// public void com.reflect.Student.setAge(java.lang.Integer)
// public java.lang.String com.reflect.Student.getName()
// public java.lang.String com.reflect.Student.toString()
// public void com.reflect.Student.setName(java.lang.String)
System.out.println("本类" + method);
}
// 获取指定的单一方法
Method setName = class1.getDeclaredMethod("setName", String.class);
Method getName = class1.getDeclaredMethod("getName");
// 输出:public void com.reflect.Student.setName(java.lang.String)
System.out.println(setName);
// 获取方法的修饰符
int modifiers = setName.getModifiers();
System.out.println(modifiers); // 输出:1
// 获取方法的形参
Parameter[] parameters = setName.getParameters();
for(Parameter parameter : parameters){
System.out.println(parameter); // 输出:java.lang.String arg0
}
// 获取方法的返回值
Student student = new Student();
setName.setAccessible(true);
// 参数一:表示方法的调用者
// 参数二:"张三"表示在调用方法的时候传递的实际参数
setName.invoke(student, "张三");
String result = (String) getName.invoke(student);
System.out.println(result); // 张三
}
三、反射的使用场景
- 框架开发:许多Java框架(如Spring)都使用了反射机制来简化配置和初始化过程,例如Spring通过反射来自动装配bean。
- 动态代理:Java的动态代理机制也是基于反射实现的。通过反射,可以在运行时创建接口的代理实现,如AOP(面向切面编程)等功能。
- 工具类库:如Apache Commons BeanUtils使用反射来简化JavaBean的操作、如属性赋值、获取等。
四、反射的优缺点
优点:
- 灵活性:反射使得程序可以在运行时动态修改和擦好像对象的行为,增强了程序的灵活性。
- 简化配置:减少了硬编码,使配置更加灵活,便于维护和拓展。
缺点:
- 安全性问题:比如上面可以使用暴力反射的方式直接获取private修饰的成员,即破坏了面向对象的封装性。
- 可读性问题:过度使用反射可能导致代码难以理解和维护,适度使用反射为最佳。