小话java反射机制

写作原因:Java反射注解这一块一直是笔者的盲区,但是Java系开发者都知道这一块的重要性。以熟悉的Android开发为例,通过利用反射注解大神们创造了可以减少大量重复赘余代码和清晰逻辑结构的依赖注入框架。本文也是为日后的依赖注入剖析做预备。由于水平限制,一些地方可能存在疏漏,望指出。


本章先从反射开始吧。java是一门面向对象的语言,java中有一句话概括了这个特点:万物皆对象。平常我们创建的对象是通过某个类产生的,那么类本身呢?不是说万物皆对象吗?还有基本对象和静态成员变量呢?于是java中便引入定义:类本身也是Class类的实例对象。Class普通类是Class类的对象。一切操作都将使用Object完成,类、数组的引用都可以使用Object进行接收。这就真正实现了万物皆对象的思想。

定义

那么一个类作为一个对象这个怎么定义呢?有如下三种方法:

Person person = new Person();
Class c1 = Person.class;
//这个实例化说明每个类都有一个静态的成员变量Class;
class c2 = person.getClass();
//通过某个类的对象来获得这个对象的类类型;
Class c3 = Class.forName("类的具体名");
//通过类的具体名称(包括包名)来获得;

注意上面c1、c2、c3是相等的,他们都是指向Person这个Class类实例化的对象的。现在我们有了类这个对象(好绕口,其实我们现在要做的就是把所有都看成对象,包括类),我们能做什么呢?我们可以通过这个类对象来调用Class类的方法来实现一些功能。具体有哪些功能呢?

实现动态加载类的功能

由于反射是在编译之后进行的,所以可以通过反射来绕过编译期加载类,实现运行时动态加载类的效果。这样有什么好处呢?通过动态加载类,我们可以完成功能模块的更新而不需重新编译,在需要某个模块时可以动态添加。具体操作我们通过一个Demo来呈现。下面展示一下这个Demo,比如我们要写一个QQ,除了聊天功能(固定,这里我们使用静态加载作比较),我们还提供各种其他功能供用户选择安装使用,比如QQMusic和QQHealth,用户根据需要安装使用。如果使用静态加载类,要实现以上功能我们只能一次性全部把功能写到QQ这个程序里面,而且就算写进去了日后要升级还是得重新再源代码里面添加加载类,这样及其不合理。这时使用反射动态加载就十分必要了。下面我们写一下这个例子,该例子共有5个类,分别为QQ.java、QQChat.java、QQHealth.java、QQMusic.java、FuncAble.java:

public class QQ {
    public static void main(String args[]){
        QQChat chat = new QQChat();
        chat.run();
        //
        System.out.println("Please input the application you want to open:");
        Scanner s = new Scanner(System.in);
        String name = s.next();
        try {
            Class health = Class.forName(name);
            FuncAble fa = (FuncAble) health.newInstance();
            fa.run();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

QQ.java中实现了静态加载聊天功能和动态加载其它功能的逻辑。我们根据用户输入的类名来加载类类型并实例化为对象然后调用对象的方法,注意我们使用了FuncAble这个接口。为什么要使用接口呢?这里利用了多态性,通过使用接口来兼容各个类对象的实例化。这样下次在增加新的类功能模块时就不用重新去写QQ类的内部逻辑了,只需实现FuncAble接口然后写我们需要的类的逻辑就行了。

获取类内部信息

大家一定很好奇IDE的只能提示是怎么实现的吧?没错,它们也是利用反射机制完成的。下面我展示一下利用反射来模拟IDE的智能提示功能。这里我们写了一个类名叫ClassInfo,用该类来获取类的内部信息,详细方法见代码中的注释。

public class ClassInfo {
    //获取方法信息    
    public static void getMethodInfo(Object obj){
        Class c = obj.getClass();
        //获取类类型
        Method[] ms = c.getDeclaredMethods();
        //获取方法对象数组
        for(Method m : ms){
            String modifierName = Modifier.toString(m.getModifiers());
            //获取修饰符
            String methodName = m.getName();
            //获取方法名
            Class[] paraTypes = m.getParameterTypes();
            //获取形参类类型
            String paraTypeName = "";
            for(Class para : paraTypes){
                paraTypeName= paraTypeName + " "+para.getSimpleName();
            }
            Class returnType = m.getReturnType();
            String returnTypeName = returnType.getSimpleName();
            //获取返回类型
            //也可以使用getName(),不过会包含包名
            System.out.println(modifierName +" "+returnTypeName+" "+methodName+"("+paraTypeName+")");
        }
    }
    //获取成员信息 
    public static void getFieldInfo(Object obj){
        Class c = obj.getClass();
        Field[] fields = c.getDeclaredFields();
        //获取成员对象数组
        for(Field f : fields){
            String fieldName = f.getName();
            //获取成员名
            Class fieldType = f.getType();
            //获取成员类类型
            int modifier = f.getModifiers();
            //获取修饰符
            String modifierName = Modifier.toString(modifier);
            String fieldTypeName = fieldType.getSimpleName();
            System.out.println(modifierName+" " + fieldTypeName + " "+fieldName);
        }
    }
    //获取构造函数信息 
    public static void getConstructorInfo(Object obj){
        Class c = obj.getClass();
        Constructor[] constructors = c.getDeclaredConstructors();
        //获取构造函数对象数组
        for(Constructor constructor : constructors){
            String constructorName = constructor.getName();
            //获取构造函数名
            String paraTypeName = "";
            Class[] paraTypes = constructor.getParameterTypes();
            //获取形参类类型
            for(Class p : paraTypes){
                paraTypeName = paraTypeName+" "+p.getSimpleName()+" ";
                //获取形参类型名
            }
            String modifierName = Modifier.toString(c.getModifiers());
            System.out.println(modifierName+" "+constructorName+"("+paraTypeName+")");
        }
    }
}

有了这个类,我们就可以根据某个类的实例对象查看该类的方法,成员及构造函数了。

通过反射操作类内部

这里讲述通过反射操作方法的例子,修改成员变量类似。还记得以前我剖析过鸿洋大神对SP的封装工具类SPUtils。他就是通过反射将SP.Editor里面的apply()方法取出然后调用实现将IO操作异步进行的工作(commit是同步的)。首先应该知道:方法名称和方法的参数列表唯一决定方法。所以我们可以通过类类型的getMethod(name,parameterTypes)获得唯一方法,然后通过方法对象的invoke(对象,参数列表)来实现方法间接操作对象从而调用方法。代码片段见下:

获取方法:名称参数列表
//getDeclaredMethod()
a1 = c.getMethod(name,parameterTypes)
parameterTypes = new Class[]{int.class,int class};//int.class,int.class
Object o = a1.invoke(对象,new Object[]{10,20})//方法操作对象(或者对象,10,20)
方法如果没有返回值返回null,有返回值返回具体的值

总结

最后回答一下前面那个问题:基本数据类型跟void关键字也是类类型,数组也一样。就是说它们也可以是对象,如int.class等。博主写这篇文章真是几经周折,先是eclipse崩溃,然后使用Atom写文章,文章写完后Atom挂掉了……又重写了一遍。不说了,都是泪。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,596评论 18 139
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,442评论 25 707
  • 星期六总有着一种奇特的魅力,不用像星期五下班那样劳累,也不用像星期日那样担心星期一的工作,所以星期六是不用上发条...
    灰白印记阅读 420评论 0 2
  • 鱼儿与鸟相恋 每天在池塘相见相谈 鱼儿向鸟儿求婚,说: “嫁给我吧,我要让你成为世上最幸福的妻子” 鸟儿羞涩点头 ...
    沉朽阅读 152评论 0 0