Java反射机制分析

1、反射机制的概念

指在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个对象,都能操作它的任意一个方法和属性。这种动态获取信息,以及动态调用对象方法的功能叫Java语言的反射机制。

本质是JVM得到class对象之后,再通过class对象进行反编译,从而获取对象的各种信息。

2、反射的原理

为了彻底理解反射的原理,可以先理解一下虚拟机的工作机制。

通常,java在编译之后,会将Java代码生成为class源文件,JVM启动时,将会载入所有的源文件,并将类型信息存放到方法区中;将所有对象实例存放在Java堆中,同时也会保存指向类型信息的引用。

反射首先需要获取到这个对象存放在方法区的类型信息,获取到类型信息后,我们就知道这个类的构造器、属性、方法、注解、子类、父类等等信息了,这个时候,我们就可以通过这些类型信息来回调处理对象,来完成自己想要的操作了。

3、反射的优缺点

(1)优点:在运行时获得类的各种内容,进行反编译,对于Java这种先编译再运行的语言,能够让我们很方便的创建灵活的代码,这些代码可以在运行时装配,无需在组件之间进行源代码的链接,更加容易实现面向对象。

(2)缺点:

  • 反射会消耗一定的系统资源,因此,如果不需要动态地创建一个对象,那么就不需要用反射;
  • 反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题。

4、反射的用途

反射功能通常用于检查或修改Java虚拟机运行中(runtime)的应用程序的行为,这一句话就精准的描述了反射的全部功能,更详细来说可以分为以下几点:

        1. 在运行中分析类的能力,可以通过完全限定类名创建类的对象实例。

        2. 在运行中查看和操作对象,可以遍历类的成员变量。

        3. 反射允许代码执行非反射代码中非法的操作,可以检索和访问类的私有成员变量,包括私有属性、方法等。

注意:要有选择的使用反射功能,如果可以直接执行操作,那么最好不要使用反射。

 5、在Java中应用反射

1. 获取Class对象

 (1)知道具体类的情况下可以使用

Class<String> aClass0 = String.class;

(2)通过 Class.forName()传入类的全路径获取

Class aClass1 = Class.forName("cn.dailyTest.TargetObject");

(3)通过对象实例instance.getClass()获取

TargetObject o = new TargetObject();
Class<? extends TargetObject> aClass1 = o.getClass();

(4)通过类加载器xxxClassLoader.loadClass()传入类路径获取

Class clazz = ClassLoader.loadClass("cn.dailyTest.TargetObject");

通过类加载器获取 Class 对象不会进行初始化,意味着不进行包括初始化等一系列步骤,静态代码块和静态对象不会得到执行

2. 通过反射机制去获取类的信息

首先创建一个类

public class TargetObject {
    private String value;
    public String name;

    public TargetObject() {
        value = "java";
    }

    public void publicMethod(String s) {
        System.out.println("I love " + s);
    }

    private void privateMethod() {
        System.out.println("value is " + value);
    }
}

 使用反射操作这个类的方法以及参数

public class 反射 {
    public static void main(String[] args) throws Exception {

        // 获取 TargetObject 类的 Class 对象并且创建 TargetObject 类实例
        Class<?> targetClass = Class.forName("com.dailyTest.TargetObject");
        System.out.println(targetClass);
        TargetObject targetObject = (TargetObject) targetClass.newInstance();

        // 获取 TargetObject 类中定义的所有方法
        Method[] methods = targetClass.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println(method.getName());
        }

        // 获取指定方法并调用
        Method publicMethod = targetClass.getDeclaredMethod("publicMethod", String.class);
        publicMethod.invoke(targetObject, "反射");

        // 获取指定参数并对参数进行修改
        Field field = targetClass.getDeclaredField("value");

        // 为了对类中的参数进行修改我们取消安全检查
        field.setAccessible(true);
        field.set(targetObject, "fanShe!");

        // 调用 private 方法
        Method privateMethod = targetClass.getDeclaredMethod("privateMethod");

        // 为了调用private方法我们取消安全检查
        privateMethod.setAccessible(true);
        privateMethod.invoke(targetObject);
    }
}

输出:

class com.dailyTest.TargetObject
privateMethod
publicMethod
I love 反射
value is fanShe!

3. 反射可获取类的所有信息

包括:modifier、constructor、field、method

4. 反射方法的其他使用--通过反射越过泛型检查

泛型用在编译期,编译过后泛型擦除(消失掉),所以是可以通过反射越过泛型检查的。


public static void main(String[] args) throws Exception {
    ArrayList<String> stringList = new ArrayList<>();
    stringList.add("aaa");
    stringList.add("bbb");
//        stringList.add(100); // 这里报错,需要的类型是String,提供的类型是int
    // 但是使用反射机制就能把100添加进去
    Class<?> listClass = stringList.getClass();
    Method add = listClass.getMethod("add", Object.class);
    add.invoke(stringList, 100);

    System.out.println(stringList);
    for(Object o : stringList) {
        System.out.println(o.getClass());
    }
}

输出:

[aaa, bbb, 100]
class java.lang.String
class java.lang.String
class java.lang.Integer

思考:泛型是会擦除的,那为什么反射能够获取到泛型的信息呢?

分析:泛型的信息只存在编译阶段,在class字节码就看不到泛型的信息了。其实,可以理解为泛型擦除是有范围的,定义在类上的泛型信息是不会被擦除的。Java编译器仍在class文件以 Signature属性的方式保留了泛型信息。Type作为顶级接口,Type下还有几种类型,比如TypeVariable、ParameterizedType、WildCardType、GenericArrayType、以及Class。通过这些接口我们就可以在运行时获取泛型相关的信息。

思考:都说反射会影响性能,有什么方式可以减低它的性能影响吗?

分析:可以使用缓存把反射的元数据存储起来,下一次使用的时候就可以直接从内存获取了。尽可能使用高性能的反射框架。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值