美文网首页
【Java】反射

【Java】反射

作者: littlefogcat | 来源:发表于2021-03-16 19:49 被阅读0次

    一、反射的目的是什么?

    Java的权限机制限制了对一个类的私有成员的访问。如果想要访问或修改一个对象的私有成员或者方法,则可以通过反射来实现。
    通过反射机制,可以在运行中获取到一个类的属性和方法并调用之
    反射相关的类在java.lang.reflect包中。

    二、反射的具体使用示例

    反射主要用到四个类,Class、Field、Method、Constructor,分别代表类、属性、方法、构造方法。

    现有类Animal如下,本章以Animal类为例:

    类图
    class Animal {
        private String name;
    
        private Animal() {
        }
    
        public Animal(String name) {
            this.name = name;
        }
    
        public String getName() {
            return name;
        }
    
        public void run() {
            System.out.println(name + " is running");
        }
    
        private void die() {
            System.out.println(name + " died");
        }
    }
    

    加载类

    对于一个非公开的类,在其他包无法访问。这时需要使用反射,通过Class.forName()来获取到类。

        Class animalClass = Class.forName("com.example.Animal");
    

    Class.forName本质上是通过当前类加载器加载类。

    获取并修改对象成员的值

    对于Animal类型的变量animal,现在需要修改其name属性。

        // 通过反射修改name
        private static void modifyName(Animal animal) throws NoSuchFieldException, IllegalAccessException {
            Class cls = Animal.class; // 获取Class对象
            Field nameField = cls.getDeclaredField("name"); // 获取name属性的Field对象
            nameField.setAccessible(true); // 设置name权限为可获取
            nameField.set(animal, "pig"); // 修改animal的名称为"pig"
            System.out.println(animal.getName()); // 打印名称
        }
    

    调用私有方法

    对于Animal类的die()方法,由于其是私有的,所以需要通过反射调用。

        // 通过反射调用私有方法
        private static void invokePrivateMethod(Animal animal) throws Exception {
            Class cls = Animal.class; // 获取Class对象
            Method method = cls.getDeclaredMethod("die"); // 获取die()方法的Method对象
            method.setAccessible(true); // 设置方法可以调用
            method.invoke(animal); // 调用方法
        }
    

    通过私有构造方法创建对象

    由于Animal类的无参构造方法是私有的,如果要使用这个方式构建对象,需要使用反射。

        // 通过私有的无参构造方法创建Animal对象
        private static Animal constructInstance() throws Exception {
            Class cls = Animal.class; // 获取Class对象
            Constructor constructor = cls.getDeclaredConstructor(); // 获取无参构造方法
            constructor.setAccessible(true); // 设置该构造方法可调用
            return (Animal) constructor.newInstance(); // 创建实例并返回 
        }
    

    三、反射常用方法

    1. Class类

    Class.forName(String)

    通过类的全名加载类。与直接通过类加载器加载类的区别是,Class.forName会初始化类的static内容,而ClassLoader不会。

    getDeclaredMethod / getMethod / getDeclaredMethods / getMethods

    获取该类的方法。
    其中getDeclaredMethod(methodName, paramTypes)获取的是类自身声明的方法,包含public、protected和private方法;getMethod(methodName, paramTypes)获取的是类的public方法,包括自身的和从父类、接口继承的。
    与此类似的,getDeclaredMethods()获取所有该类声明的方法,而getMethods()获取该类声明的和继承、实现的所有public方法

    getDeclaredField / getField / getDeclaredFields / getFields

    获取该类的字段(成员变量、静态变量),其间的区别与获取方法相同。

    getConstructor / getConstructors / getDeclaredConstructor / getDeclaredConstructors

    获取该类的构造方法,其间的区别与获取方法相同。

    2. Field类

    get(obj)

    获取实例obj中该字段的值。如果是静态变量,则obj传入null。

    set(obj, value)

    设置实例obj中该字段的值。如果是静态变量,则obj传入null。

    setAccessible(boolean)

    设置该字段是否默认可访问。如果设置为true,则这个字段会忽略Java访问修饰符。

    3. Method类

    invoke(obj)

    调用obj对象的该方法。如果是静态方法,则传入null。

    setAccessible(boolean)

    设置该方法是否默认可访问。如果设置为true,则这个字段会忽略Java访问修饰符。

    4. Constructor类

    newInstance(params)

    通过该构造方法创建对象。

    setAccessible(boolean)

    设置该构造方法是否默认可访问。如果设置为true,则这个字段会忽略Java访问修饰符。

    四、为什么反射效率低?如何提高反射效率?

    反射效率低的原因:

    1. 会做一系列的权限判断、包装参数、检查参数等额外工作;
    2. JIT无法优化;

    提高反射效率的方式:

    1. 对反射得到的Class、Method、Field、Constructor等对象进行缓存;
    2. 使用MethodHandle;
    3. 使用第三方库如ASM;

    相关文章

      网友评论

          本文标题:【Java】反射

          本文链接:https://www.haomeiwen.com/subject/nbjsqltx.html