栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Java反射机制分析

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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 aClass0 = String.class;

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

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

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

TargetObject o = new TargetObject();
Class 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 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。通过这些接口我们就可以在运行时获取泛型相关的信息。

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

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

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/877006.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号