Java中的反射

什么是反射?

反射,是指在运行状态下,获取类中的属性和方法,以及调用其中方法的的一种机制。

这种机制的作用在于获取运行时才知道的类(Class)及其中的属性(Field)、方法(Method)以及调用其中的方法,也可以设置其中的属性值。

Java反射机制提供了一下几个功能:

  • 在运行时判断任意一个对象所属的类
  • 在运行时构造任意一个类的对象
  • 在运行时判断任意一个类所具有的成员变量和方法
  • 在运行时调用任意一个对象的方法

利用反射可以 :

  • 操作因访问控制权限限制的属性和方法
  • 实现自定义注解
  • 生成动态代理
  • 根据需求加载类

反射的工作原理

Class 对象 :每一个Java类文件都会被编译成一个.class字节码文件,这些文件会在程序运行时被类加载器读入内存,将其放在运行时数据区的方法区内,并在堆中创建一个java.lang.Class 对象,用来封装方法区内的数据结构,这些Class对象包含了这个类的所有信息,包括父类,接口,构造方法,方法,属性等。

我们通过new的形式创建对象实际上就是通过这些Class来创建,只是这个过程对于我们是不透明的而已。

反射的工作原理就是借助Class.java、Constructor.java、Method.java、Field.java这四个类在程序运行时动态访问和修改任何类的行为和状态。

反射的用法

Person类声明如下:

class Person {
    private String name;
    private int age;
    private long phoneNumber;

    public Person() {

    }

    public Person(String name, int age, long phoneNumber) {
        this.name = name;
        this.age = age;
        this.phoneNumber = phoneNumber;
    }

    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 long getPhoneNumber() {
        return phoneNumber;
    }

    public void setPhoneNumber(long phoneNumber) {
        this.phoneNumber = phoneNumber;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", phoneNumber=" + phoneNumber +
                '}';
    }
}

实现反射的三种方式(获取Class对象)

Person person = new Person();
//①通过调用实例对象的getClass()方法
Class clazz = person.getClass();
//②通过类名调用class属性
Class clazz = Person.class;
//③通过Class.forName()方法,以全限定类名作为参数
Class clazz = Class.forName("cn.echo0.reflect.Person");//需要处理ClassNotFoundException

通过Class对象创建实例

具有无参构造函数时
Object obj =  clazz.newInstance();//,需要处理IllegalAccessException, InstantiationException
构造器需要参数时
//如果需要参数则需要先获取到该类构造方法,并通过构造方法类获取实例:
Constructor constructor = clazz.getConstructor(String.class);//需要处理NoSuchMethodException
Object obj = constructor.newInstance("Echo0",20,15197401470l);//需要处理InvocationTargetException

获取属性

//        获取该类中的所有属性(包括私有属性)
Field[]  fields = clazz.getDeclaredFields();
//        遍历输出所有属性

for(Field field : fields){
    System.out.println("Modifier : \t" + Modifier.toString(field.getModifiers()));
    System.out.println("Type : \t"+field.getType());
    System.out.println("FieldName : \t"+field.getName());

}
修改私有属性的值
// 获取名为name的私有属性

Field field = clazz.getDeclaredField("name");

field.setAccessible(true);//对于私有属性需要将其设置为可访问

Object name=field.get(object);

System.out.println(name);

// 修改属性

field.set(object,"Echo009");

获取方法

Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
    System.out.println(method);
    System.out.println("Modifier : \t" + Modifier.toString(method.getModifiers()));
    System.out.println("MethodName : \t" + method.getName());
    System.out.println("ReturnType : \t" + method.getReturnType());

    // 获取参数类型
    Class[] clazzs = method.getParameterTypes();
    for (Class temp : clazzs) {
        System.out.println("ParameterType \t: " + temp);
    }
    System.out.println("___________________________________________");
}
调用方法
// 获取名为getPhoneNumber的方法
Method method = clazz.getDeclaredMethod("getPhoneNumber");
Object phoneNumber = method.invoke(object);
System.out.println(phoneNumber);

总结

虽然反射非常的灵活,可以不受访问控制修饰符的限制,但同时也需要注意以下几个问题:

  • 性能问题: 通过反射访问、修改类的属性和方法时会远慢于直接操作,但性能问题的严重程度取决于在程序中是如何使用反射的。
  • 安全性问题: 反射可以随意访问和修改类的所有状态和行为,破坏了类的封装性,如果不熟悉被反射类的实现原理,随意修改可能导致潜在的逻辑问题;
  • 兼容性问题: 因为反射会涉及到直接访问类的方法名和实例名,不同版本的API如果有变动,反射时找不到对应的属性和方法时会报异常;

完整示例代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值