深入理解Java反射

Java反射机制

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

反射的本质就是:在运行时,把 Java 类中的各种成分映射成一个个的 Java 对象。

反射机制很重要的一点就是“运行时”,其使得我们可以在程序运行时加载、探索以及使用编译期间完全未知的 .class 文件。换句话说,Java 程序可以加载一个运行时才得知名称的 .class 文件,然后获悉其完整构造,并生成其对象实体、或对其 fields(变量)设值、或调用其 methods(方法)。

Class类将一个类的组成封装成各个属性,并实现了各个getXxx()方法。

    public static void getInfo(Class cls) throws NoSuchMethodException {
        //构造方法
        cls.getConstructor();

        //获取某个方法
        cls.getMethod("");

        //包含的方法
        cls.getMethods();

        //获取某个属性
        cls.getField("");

        //包含的属性
        cls.getFields();

        //实现的接口
        cls.getInterfaces();

        //包含的Annotation
        cls.getAnnotations();

        //内部类
        cls.getDeclaredClasses();

        //外部类
        cls.getDeclaringClass();

        //获取类名
        cls.getName();

        //获取包名
        cls.getPackage();

        //获取修饰符
        cls.getModifiers();
    }
创建使用类
public class FatherClass {

    protected String mFatherName;
    protected int mFatherAge;

    public FatherClass() {
    }
}

public class SonClass extends FatherClass {

    private String mSonName;
    protected int mSonAge;
    public String mSonBirthday;

    @GET("https:\\www.baidu.com")
    private <T,K> String toStr(T t, K k) {
        return t.toString() + k.toString();
    }

    public String getmSonName() {
        return mSonName;
    }

    public void setmSonName(String mSonName) {
        this.mSonName = mSonName;
    }

}

各种操作实现:

  • 通过反射获取类的三种方式
    /**
     * 通过反射获取类的三种方式
     * @throws ClassNotFoundException
     */
    private void getClassWay() throws ClassNotFoundException {
        Class cls = null;
        cls = FatherClass.class;
        cls = new FatherClass().getClass();
        cls = Class.forName("com.example.genericannotaionreflect.reflactdemo.FatherClass");
    }
  • 反射创建实例
    public static void getConstructor() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {

        Class<?> cls = StringBuilder.class;
        StringBuilder sb = (StringBuilder) cls.newInstance();
        sb.append("hello");
        System.out.println(sb.toString());

        Class<?> cls2 = String.class;
        //获取String类带一个String参数的构造器
        Constructor constructor = cls2.getConstructor(String.class);
        //根据构造器constructor创建实例
        String str = (String) constructor.newInstance("hello");
        System.out.println(str);
    }
获取类的所有变量信息
    /**
     * 通过反射获取类的所有变量
     */
    public static void printFileds(Class cls) {
        System.out.println("类名:" + cls.getName());

        // 获取所有 public 访问权限的变量
        // 包括本类声明的和从父类继承的
        Field[] fields = cls.getFields();

        // 获取所有本类声明的变量,包含各种访问权限
        Field[] declaredFields = cls.getDeclaredFields();

        for (Field field: declaredFields) {
            //获取访问权限并输出
            field.setAccessible(true);
            int modifier = field.getModifiers();
            System.out.println("属性访问权限是否是 PROTECTED: " + Modifier.isProtected(modifier));
            //输出变量的类型及变量名
            System.out.println("属性类型:" + field.getType().getName() + "属性名:" + field.getName());
        }
    }
    
     //调用
     ClassReflact.printFileds(FatherClass::class.java)

打印:

类名:com.example.genericannotaionreflect.reflactdemo.FatherClass
属性访问权限是否是 PROTECTED: true
属性类型:int属性名:mFatherAge
属性访问权限是否是 PROTECTED: true
属性类型:java.lang.String属性名:mFatherName

getFields() :获取所有 public 访问权限的变量,非public的获取不到
getDeclaredFields():获取所有本类声明的变量,包含各种访问权限

  • 获取类的所有方法的所有元素
    @RequiresApi(api = Build.VERSION_CODES.P)
    public static void printMethods(Class cls) {
        System.out.println("类名:" + cls.getName());

        // 获取所有 public 访问权限的方法
        // 包括本类声明的和从父类继承的
        Method[] methods = cls.getMethods();

        // 获取所有本类声明的方法,包含各种访问权限
        Method[] declaredMethods = cls.getDeclaredMethods();

        for (Method method : declaredMethods) {
            method.setAccessible(true);

            System.out.println("方法名: " + method.getName());

            System.out.println("属性访问权限是否是 public: " + Modifier.isProtected(method.getModifiers()));

            //获取方法返回值类型
            Class<?> returnType = method.getReturnType();
            System.out.println( "返回类型: " + returnType.getName());

            //获取方法所有参数
            Parameter[] parameters = method.getParameters();
            for (Parameter parameter: parameters) {
                System.out.println("拥有参数:" + parameter.getName() + "--" + parameter.getType().getName());
            }

            //获取方法抛出的异常
            Class<?>[] exceptionTypes = method.getExceptionTypes();

            //获取方法注解
            Annotation[] annotations = method.getAnnotations();

            //方法是否有某个注解
            if (method.isAnnotationPresent(GET.class)) {
                GET annotation = method.getAnnotation(GET.class);
                System.out.println(annotation.value());
            }

            //获取方法参数类型的泛型参数
            Type[] genericParameterTypes = method.getGenericParameterTypes();
            for (Type type: genericParameterTypes) {
                System.out.println(type.getTypeName());
            }

        }
    }

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface GET {
    String value();
}

//调用
ClassReflact.printMethods(SonClass::class.java)

打印:

类名:com.example.genericannotaionreflect.reflactdemo.SonClass
方法名: toStr
属性访问权限是否是 public: false
返回类型: java.lang.String
拥有参数:arg0--java.lang.Object
拥有参数:arg1--java.lang.Object
https:\www.baidu.com
T
K
类名:com.example.genericannotaionreflect.reflactdemo.SonClass
方法名: toStr
属性访问权限是否是 public: false
返回类型: java.lang.String
拥有参数:arg0--java.lang.Object
拥有参数:arg1--java.lang.Object
https:\www.baidu.com
T
K
方法名: getmSonName
属性访问权限是否是 public: false
返回类型: java.lang.String
方法名: setmSonName
属性访问权限是否是 public: false
返回类型: void
拥有参数:arg0--java.lang.String
java.lang.String
  • 反射执行某个对象的私有方法
    public static void invokeMethod() throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        Class cls = SonClass.class;
        SonClass sonClass = (SonClass) cls.newInstance();
        Method method = cls.getDeclaredMethod("setmSonName", String.class); //获取setmSonName方法
        method.setAccessible(true); //可以访问对象的私有方法
        method.invoke(sonClass, "lala"); //使用 invoke 反射调用私有方法,传需要操作的对象和方法的各个参数
        System.out.println(sonClass.getmSonName());
    }
  • 反射操作对象属性值
    public static void modifyField() throws NoSuchFieldException, IllegalAccessException {
        Class cls = SonClass.class;

        Field field = cls.getDeclaredField("mSonAge");
        field.setAccessible(true);
        SonClass sonClass = new SonClass();
        field.set(sonClass, 18);
        System.out.println("age: " + sonClass.mSonAge);
    }
java 反射为什么会耗性能

1.反射调用过程中会产生大量的临时对象,这些对象会占用内存,可能会导致频繁 gc,从而影响性能。
2.反射调用方法时会从方法数组中遍历查找,并且会检查可见性等操作会耗时。
3.反射在达到一定次数时,会动态编写字节码并加载到内存中,这个字节码没有经过编译器优化,也不能享受JIT优化。
4.反射一般会涉及自动装箱/拆箱和类型转换,都会带来一定的资源开销。
参考:
https://juejin.cn/post/6844904098207105038

参考:https://juejin.cn/post/6844904005294882830

Github demo地址:

https://github.com/running-libo/GenericAnnotationReflect

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容