zoukankan      html  css  js  c++  java
  • 反射

    一、反射

      在Java运行环境中,对于任意一个类,都能够知道它的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性。这种动态获取类信息以及动态调用对象方法的功能被称为Java语言的反射(Reflection)机制。反射机制可以赋予Jvm动态编译的能力,这是Java被视为动态(或准动态)语言的一个关键性质。Java中有两种编译类型:

    • 静态编译:在编译时确定类型,绑定对象
    • 动态编译:在运行时确定类型,绑定对象

    所谓编译期就是编译器帮你把源代码翻译成机器能识别的代码,比如编译器把Java代码编译成Jvm能识别的字节码文件;而运行期指的是将可执行文件交给操作系统去执行。一般情况下,在Java中声明一个变量时,必须知道它的类型,变量的类型信息在编译时就保存到了class文件中,程序在运行时是固定不变的;而反射机制允许静态语言在运行时检查、修改程序的结构与行为。动态编译最大限度地发挥了Java的灵活性,体现了多态的应用,降低了类之间的耦合。


    二、常用方法

     如果说类是对具有相同特征实体的抽象,那Class类就是对类的抽象,毕竟所有类都由属性、方法和构造函数等组成,也可以说Class是类的类。抛开类的具体功能,单从类的结构看,一个类可以拆分成属性、方法和构造函数。利用反射可以将类解剖,并将各个组成部分映射成一个个Java对象,通过这些对象最终达到调用类方法和属性的目的。

    与反射机制相关的类有:

    • Class——代表类的实体,在运行的Java应用程序中表现为类和接口
    • Field——代表类的成员变量
    • Method——代表类的方法
    • Constructor——代表类的构造方法

    各个类的常用方法汇总如下:

    方法族方法用途
    Class 获取类相关的方法 asSubclass(Class<U> clazz) 把传递的类的对象转换成代表其子类的对象
    Cast 把对象转换成代表类或是接口的对象
    getClassLoader() 获得类的加载器
    getClasses() 返回一个数组,数组中包含该类中所有公共类和接口类的对象
    getDeclaredClasses() 返回一个数组,数组中包含该类中所有类和接口类的对象
    forName(String className) 根据类名返回类的对象
    getName() 获得类的完整路径名字
    newInstance() 创建类的实例
    getPackage() 获得类的包
    getSimpleName() 获得类的名字
    getSuperclass() 获得当前类继承的父类的名字
    getInterfaces() 获得当前类实现的类或是接口
    获得类中属性相关的方法 getField(String name) 获得某个公有的属性对象
    getFields() 获得所有公有的属性对象
    getDeclaredField(String name) 获得某个属性对象
    getDeclaredFields() 获得所有属性对象
    获得类中注解相关的方法 getAnnotation(Class<A> annotationClass) 返回该类中与参数类型匹配的公有注解对象
    getAnnotations() 返回该类所有的公有注解对象
    getDeclaredAnnotation(Class<A> annotationClass) 返回该类中与参数类型匹配的所有注解对象
    getDeclaredAnnotations() 返回该类所有的注解对象
    获取类中构造器相关的方法 getConstructor(Class...<?> parameterTypes) 获得该类中与参数类型匹配的公有构造方法
    getConstructors() 获得该类的所有公有构造方法
    getDeclaredConstructor(Class...<?> parameterTypes) 获得该类中与参数类型匹配的构造方法
    getDeclaredConstructors() 获得该类所有构造方法
    获取类中方法相关的方法 getMethod(String name, Class...<?> parameterTypes) 获得该类某个公有的方法
    getMethods() 获得该类所有公有的方法
    getDeclaredMethod(String name, Class...<?> parameterTypes) 获得该类某个方法
    getDeclaredMethods() 获得该类所有方法
    获取类状态相关的方法 isAnnotation() 如果是注解类型则返回true
    isAnnotationPresent(Class<? extends Annotation> annotationClass) 如果是指定类型注解类型则返回true
    isAnonymousClass() 如果是匿名类则返回true
    isArray() 如果是一个数组类则返回true
    isEnum() 如果是枚举类则返回true
    isInstance(Object obj) 如果obj是该类的实例则返回true
    isInterface() 如果是接口类则返回true
    isLocalClass() 如果是局部类则返回true
    isMemberClass() 如果是内部类则返回true
    Field   equals(Object obj) 属性与obj相等则返回true
    get(Object obj) 获得obj中对应的属性值
    set(Object obj, Object value) 设置obj中对应属性值
    Method   invoke(Object obj, Object... args) 传递object对象及参数调用该对象对应的方法
    Constructor   newInstance(Object... initargs) 根据传递的参数创建类的对象

    三、实战应用

    先看一个Android源码中运用反射的例子:

        boolean mbHasLoadSettingClass = false;
        Method mmgetIntForUser = null;
        String  mStrFPS_ONENAV_ENABLED="";
        Class<?> mclzModeSettingsSecure = null;
        boolean hasNavBar(Context context) {
            try {
                if (!mbHasLoadSettingClass) {
                    mclzModeSettingsSecure = Class.forName("com.motorola.android.provider.MotorolaSettings$Secure");  //获取Secure类的Class实例
                    Field fFPS_ONENAV_ENABLED = mclzModeSettingsSecure.getField("FPS_ONENAV_ENABLED");  //获取FPS_ONENAV_ENABLED属性对象
                    String strFPS_ONENAV_ENABLED = "";
                    if (fFPS_ONENAV_ENABLED != null) {
                        mStrFPS_ONENAV_ENABLED = (String) fFPS_ONENAV_ENABLED.get(null);  //读取fFPS_ONENAV_ENABLED属性值
                    }
                    mmgetIntForUser = mclzModeSettingsSecure.getDeclaredMethod("getIntForUser", ContentResolver.class,
                            String.class, int.class, int.class);  //获取getIntForUser方法对象
                }
                if (mmgetIntForUser != null && mclzModeSettingsSecure != null) {
                    int iRet = (int) mmgetIntForUser.invoke(mclzModeSettingsSecure, context.getContentResolver(),
                            mStrFPS_ONENAV_ENABLED, -1, ActivityManager.getCurrentUser());  //调用getIntForUser方法
                    if (iRet != -1) {
                        return false;
                    }
                }
            } catch (ClassNotFoundException e) {
            } catch (NoSuchMethodException e) {
            } catch (NoSuchFieldException e) {
            } catch (IllegalAccessException e) {
            } catch (InvocationTargetException e) {
            }
        }

    反射的用法相对固定,主要流程如下:

     

    在反射调用过程中,需要注意:

    • 静态方法和变量在加载类时已经分配内存,通过Class实例就可以直接访问;调用时可以直接传入null或Class
    • 非静态方法和变量取决于类的实例,必须创建类实例后才能访问;
    • 在反射中无法直接调用private修饰的方法或变量,需要先通过如下方法取消访问限制
    • 引用静态常量的地方在编译时已经替换为常量,所以修改静态变量并不会改变已经编译的对象逻辑
    method.invoke(null);  //调用静态方法
    field.set(null,false);  //设置静态变量
    
    method.invoke(clazz.newInstance());  //调用非静态方法
    field.set(clazz.newInstance(),false);  //设置非静态变量
    
    method.setAccessible(true);  //取消访问限制

    日常开发中反射并不常用,主要有如下四个场景会用到反射:

    • 注解,注解底层实现就是反射
    • 编写框架,反射机制是很多Java框架的基石,如xml或properties配置文件
    • 其他编译期无法确定类名,需要在运行期从配置文件动态读取的情况,比如加载数据库驱动程序
    • 访问系统内部方法,如果方法内部有权限检查,反射无效

    总结:反射机制最大限度地发挥了Java的灵活性,使Java具有动态语言的特性。但是除非可以大大简化业务代码,反射并不被鼓励使用,原因之一是造成代码可读性降低;原因之二是反射需要在运行期重新解析,降低运行效率。 

  • 相关阅读:
    hdu 3652 【数位dp】
    02 -body标签中相关标签
    01-html介绍和head标签
    python 核心编程第九章文件
    python核心编程 第七章 字典,集合 练习
    常用链接
    python核心编程 第七章 字典
    python核心编程 第六章 字符串,元组,列表 字符串元组只读不可变。列表可变。
    python核心编程 第五章 数字
    python读取文件中的路径内容,保存到另外的路径中
  • 原文地址:https://www.cnblogs.com/not2/p/11121845.html
Copyright © 2011-2022 走看看