反射

一、反射

  在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具有动态语言的特性。但是除非可以大大简化业务代码,反射并不被鼓励使用,原因之一是造成代码可读性降低;原因之二是反射需要在运行期重新解析,降低运行效率。 

 

转载于:https://www.cnblogs.com/not2/p/11121845.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值