Android-反射

一、什么是反射机制

在 Java 中,反射机制是在运行时对类进行分析和操作的一种能力。它允许程序在运行时获取类的信息,如类的名称、成员变量、方法等,并且可以动态地调用这些方法、访问和修改成员变量的值。例如,可以使用反射机制在运行时根据条件调用不同的方法、访问私有成员变量和方法、实现框架和插件化开发以及实现依赖注入等。反射机制虽然功能强大,但也有性能开销较大和可能降低代码可读性与可维护性的缺点。

二、反射机制主要提供的功能

一、获取类信息

  1. 在运行时获取类的名称。可以使用 Class.getName() 方法来获取类的全限定名。
    代码示例:
    Class<?> clazz = MyClass.class;
    String className = clazz.getName();
  2. 获取类的修饰符,如 public、private、protected 等。可以使用 Class.getModifiers() 方法来获取类的修饰符标志。
    代码示例:
    int modifiers = clazz.getModifiers();
    if (Modifier.isPublic(modifiers)) {
        // 类是 public 的
    }
  3. 获取类的父类信息。可以使用 Class.getSuperclass() 方法来获取类的父类。
    代码示例:
    Class<?> superclass = clazz.getSuperclass();

二、访问成员变量

  1. 获取类的所有成员变量,包括私有成员变量。可以使用 Class.getDeclaredFields() 方法来获取类声明的所有字段。
    代码示例:
    Field[] fields = clazz.getDeclaredFields();
    for (Field field : fields) {
        // 可以获取字段的名称、类型等信息
    }
  2. 设置和获取成员变量的值。可以使用 Field.set() 和 Field.get() 方法来设置和获取成员变量的值。
    代码示例:
    Field field = clazz.getDeclaredField("myField");
    field.setAccessible(true);
    Object value = field.get(instance);
    field.set(instance, newValue);

三、调用方法

  1. 获取类的所有方法,包括私有方法。可以使用 Class.getDeclaredMethods() 方法来获取类声明的所有方法。
    代码示例:
    Method[] methods = clazz.getDeclaredMethods();
    for (Method method : methods) {
        // 可以获取方法的名称、参数类型等信息
    }
  2. 调用方法。可以使用 Method.invoke() 方法来调用对象的方法。
    代码示例:
    Method method = clazz.getDeclaredMethod("myMethod", int.class);
    method.setAccessible(true);
    Object result = method.invoke(instance, argument);

四、创建对象

  1. 可以使用 Class.newInstance() 方法来创建类的实例,但这个方法只能调用无参的构造函数。
    代码示例:
    Class<?> clazz = MyClass.class;
    Object instance = clazz.newInstance();
  2. 如果需要调用有参数的构造函数,可以使用 Constructor.newInstance() 方法。
    代码示例:
    Constructor<?> constructor = clazz.getConstructor(int.class, String.class);
    Object instance = constructor.newInstance(10, "Hello");

三、java Reflection API简介

  • Class类:代表一个类,位于java.lang包下

  • Field类:代表类的成员变量(成员变量也称为类的属性)

  • Method类:代表类的方法

  • Constructor类:代表类的构造方法

  • Array类:提供了动态创建数组,以及访问数组的元素的静态方法

四、java中的Class介绍

Class 类十分特殊,它没有共有的构造方法,被jvm调用的(简单的理解:new对象或者被类加载器加载的时候),在Java中,每个class都有一个相应的Class对象。也就是说,当我们编写一个类,编译完成后,在生成的.class文件中,就会产生一个Class对象,用于表示这个类的类型信息。

五、java中的Class三种获取方式

  • 利用对象调用getClass()方法获取该对象的Class实例;

  • 使用Class类的静态方法forName(),用类的名字获取一个Class实例 ;

  • 运用.class的方式来获取Class实例,对于基本数据类型的封装类,还可以采用.TYPE来获取相对应的基本数据类型的Class实例;

       说明:在运行期间,如果我们要产生某个类的对象,Java虚拟机(JVM)会检查该类型的Class对象是否已被加载。如果没有被加载,JVM会根据类的名称找到.class文件并加载它。一旦某个类型的Class对象已被加载到内存,就可以用它来产生该类型的所有对象。​

    //方式一    Person person = new Person();    Class<? extends Person> personClazz01 = person.getClass();     //方式二    try {
   
           Class<?> personClazz02 = Class.forName("Person");    } catch (ClassNotFoundException e) {
   
           e.printStackTrace();    }     //方式三    Class<? extends Person> personClazz03 = Person.class;

六、java中的Class中一些重要的方法

  • public Annotation[] getAnnotations () 获取这个类中所有注解

  • getClassLoader() 获取加载这个类的类加载器

  • getDeclaredMethods() 获取这个类中的所有方法

  • getReturnType() 获取方法的返回类型

  • getParameterTypes() 获取方法的传入参数类型

  • isAnnotation() 测试这类是否是一个注解类

  • getDeclaredConstructors() 获取所有的构造方法

  • getDeclaredMethod(String name, Class… parameterTypes) 获取指定的构造方法(参数:参数类型.class)

  • getSuperclass() 获取这个类的父类

  • getInterfaces() 获取这个类实现的所有接口

  • getFields() 获取这个类中所有被public修饰的成员变量

  • getField(String name) 获取指定名字的被public修饰的成员变量

  • newInstance() 返回此Class所表示的类,通过调用默认的(即无参数)构造函数创建的一个新实例

七、如何通过反射获取私有成员变量和私有方法

/**
 * Created by yuanyc on 2024/1/28.
 */
public class Person {
    private String name = "zhangsan";
    private String age;
​
    public String getName() {
        return name;
    }
​
    public void setName(String name) {
        this.name = name;
    }
}
​
class ReflectionUtil {
    /**
     * 通过反射获取私有的成员变量
     *
     * @param obj        目标对象
     * @param fieldName  成员变量名称
     * @param <T>        对象类型
     * @return 成员变量的值
     */
    public static <T> Object getPrivateValue(T obj, String fieldName) {
        try {
            // 获取对象的类
            Class<?> clazz = obj.getClass();
            // 获取指定名称的私有字段
            Field field = clazz.getDeclaredField(fieldName);
            // 设置可访问私有字段
            field.setAccessible(true);
            return field.get(obj);
        } catch (NoSuchFieldException e) {
            System.err.println("找不到指定的字段:" + fieldName);
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            System.err.println("无法访问指定的字段:" + fieldName);
            e.printStackTrace();
        }
        return null;
    }
}
​
public class Main {
    public static void main(String[] args) {
        Person person = new Person();
        // 打印没有改变属性之前的 name 值
        System.out.println("before:" + ReflectionUtil.getPrivateValue(person, "name"));
        person.setName("lisi");
        // 打印修改之后的 name 值
        System.out.println("after:" + ReflectionUtil.getPrivateValue(person, "name"));
    }
}

八、案例演示反射

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

class Person {
    private String name;
    private int age;

    public Person() {
    }

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

    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;
    }
}

class ReflectionUtils {
    /**
     * Demo1: 通过 Java 反射机制得到类的包名和类名
     */
    public static void test1(Person person) {
        System.out.println("Test1: 包名: " + person.getClass().getPackage().getName() + "," + "完整类名: " + person.getClass().getName());
    }

    /**
     * Demo2: 验证所有的类都是 Class 类的实例对象
     */
    public static void test2() throws ClassNotFoundException {
        Class<?> class1 = null;
        Class<?> class2 = null;

        // 写法 1, 可能抛出 ClassNotFoundException [多用这个写法]
        class1 = Class.forName("com.example.Person");
        System.out.println("Test2:(写法 1) 包名: " + class1.getPackage().getName() + "," + "完整类名: " + class1.getName());

        // 写法 2
        class2 = Person.class;
        System.out.println("Test2:(写法 2) 包名: " + class2.getPackage().getName() + "," + "完整类名: " + class2.getName());
    }

    /**
     * Demo3: 通过 Java 反射机制,用 Class 创建类对象[这也就是反射存在的意义所在]
     */
    public static void test3() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        Class<?> class1 = Class.forName("com.example.Person");
        // 由于这里不能带参数,所以你要实例化的这个类 Person,一定要有无参构造函数
        Person person = (Person) class1.newInstance();
        person.setAge(26);
        person.setName("kaiven");
        System.out.println("Test3: " + person.getName() + " : " + person.getAge());
    }

    /**
     * Demo4: 通过 Java 反射机制得到一个类的构造函数,并实现创建带参实例对象
     */
    public static void test4() throws ClassNotFoundException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException {
        Class<?> class1 = Class.forName("com.example.Person");
        // 得到一系列构造函数集合
        Constructor<?>[] constructors = class1.getConstructors();

        Person person1 = null;
        Person person2 = null;

        try {
            person1 = (Person) constructors[0].newInstance();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        person1.setAge(28);
        person1.setName("zhuk");

        person2 = (Person) constructors[1].newInstance(29, "zhuk");

        System.out.println("Test4: " + person1.getName() + " : " + person1.getAge() + ",   " + person2.getName() + " : " + person2.getAge());
    }

    /**
     * Demo5: 通过 Java 反射机制操作成员变量, set 和 get
     */
    public static void test5() throws IllegalArgumentException, IllegalAccessException, SecurityException, NoSuchFieldException, InstantiationException, ClassNotFoundException {
        Class<?> class1 = Class.forName("com.example.Person");
        Object obj = class1.newInstance();

        Field nameField = class1.getDeclaredField("name");
        nameField.setAccessible(true);
        nameField.set(obj, "cyy");

        System.out.println("Test5: 修改属性之后得到属性变量的值:" + nameField.get(obj));
    }

    /**
     * Demo6: 通过 Java 反射机制得到类的一些属性: 继承的接口,父类,函数信息,成员信息,类型等
     */
    public static void test6() throws ClassNotFoundException {
        Class<?> class1 = Class.forName("com.example.Person");

        // 取得父类名称
        Class<?> superClass = class1.getSuperclass();
        System.out.println("Test6:  Person 类的父类名: " + superClass.getName());

        System.out.println("===============================================");

        Field[] fields = class1.getDeclaredFields();
        for (int i = 0; i < fields.length; i++) {
            System.out.println("类中的成员: " + fields[i]);
        }
        System.out.println("===============================================");

        // 取得类方法
        Method[] methods = class1.getDeclaredMethods();
        for (int i = 0; i < methods.length; i++) {
            System.out.println("Test6,取得 Person 类的方法:");
            System.out.println("函数名:" + methods[i].getName());
            System.out.println("函数返回类型:" + methods[i].getReturnType());
            System.out.println("函数访问修饰符:" + java.lang.reflect.Modifier.toString(methods[i].getModifiers()));
            System.out.println("函数代码写法: " + methods[i]);
        }

        System.out.println("===============================================");

        Class<?> interfaces[] = class1.getInterfaces();
        for (int i = 0; i < interfaces.length; i++) {
            System.out.println("实现的接口类名: " + interfaces[i].getName());
        }
    }

    /**
     * Demo7: 通过 Java 反射机制调用类方法
     */
    public static void test7() throws ClassNotFoundException, SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class<?> class1 = Class.forName("com.example.Person");

        System.out.println("Test7: \n调用无参方法(如果有):");
        Method method = class1.getMethod("yourMethodWithoutParams");
        Object result = method.invoke(class1.newInstance());
        System.out.println("无参方法调用结果:" + result);

        System.out.println("调用有参方法(如果有):");
        method = class1.getMethod("yourMethodWithParams", int.class);
        result = method.invoke(class1.newInstance(), 100);
        System.out.println("有参方法调用结果:" + result);
    }

    /**
     * Demo8: 通过 Java 反射机制得到类加载器信息
     */
    public static void test8() throws ClassNotFoundException {
        Class<?> class1 = Class.forName("com.example.Person");
        String name = class1.getClassLoader().getClass().getName();

        System.out.println("Test8: 类加载器类名: " + name);
    }
}

public class MainActivityExtended extends android.app.Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tests();
    }

    private void tests() {
        try {
            // 通过 Java 反射机制得到类的包名和类名
            Person person = new Person();
            ReflectionUtils.test1(person);
            System.out.println("===============================================");

            // 验证所有的类都是 Class 类的实例对象
            ReflectionUtils.test2();
            System.out.println("===============================================");

            // 通过 Java 反射机制,用 Class 创建类对象[这也就是反射存在的意义所在],无参构造
            ReflectionUtils.test3();
            System.out.println("===============================================");

            // 通过 Java 反射机制得到一个类的构造函数,并实现构造带参实例对象
            ReflectionUtils.test4();
            System.out.println("===============================================");

            // 通过 Java 反射机制操作成员变量, set 和 get
            ReflectionUtils.test5();
            System.out.println("===============================================");

            // 通过 Java 反射机制得到类的一些属性: 继承的接口,父类,函数信息,成员信息,类型等
            ReflectionUtils.test6();
            System.out.println("===============================================");

            // 通过 Java 反射机制调用类中方法
            ReflectionUtils.test7();
            System.out.println("===============================================");

            // 通过 Java 反射机制获得类加载器
            ReflectionUtils.test8();
            System.out.println("===============================================");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

九、反射机制的运用场景

一、动态调用方法

  1. 当需要在运行时根据条件调用不同的方法时,可以使用反射。例如,根据用户的操作或配置文件中的设置来决定调用哪个特定的方法。

  2. 代码示例:
     

  3. try {
         
             Class<?> clazz = Class.forName("com.example.MyClass");    Object instance = clazz.newInstance();    Method method = clazz.getMethod("myMethod", int.class);    method.invoke(instance, 10);} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
         
             e.printStackTrace();}

二、访问私有成员变量和方法

  1. 在某些情况下,可能需要访问一个类的私有成员变量或方法,例如在进行单元测试或实现一些特殊的功能时。反射可以突破访问限制。

  2. 代码示例:
     

  3. try {
         
             Class<?> clazz = Class.forName("com.example.MyClass");    Object instance = clazz.newInstance();    Field field = clazz.getDeclaredField("privateField");    field.setAccessible(true);    int value = field.getInt(instance);} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchFieldException e) {
         
             e.printStackTrace();}

三、框架和插件化开发

  1. 许多 Android 框架和插件化开发中会使用反射机制。例如,在实现热修复、动态加载插件等功能时,反射可以用来加载和实例化未知的类,调用其方法。

  2. 以热修复为例,通过反射可以替换有问题的类或方法,实现运行时的修复。

四、实现依赖注入

  1. 可以使用反射来实现依赖注入框架。在运行时自动查找和注入所需的对象,减少手动初始化的代码。

  2. 例如,一个依赖注入框架可以通过反射查找标记了特定注解的字段,并自动将对应的对象实例注入到这些字段中。

摘选:Android反射机制:手把手教你实现反射_android invoke-CSDN博客

猜你喜欢

转载自blog.csdn.net/LIUCHANGSHUO/article/details/143492015