进击的Android工程师之Java基础: 反射

反射机制呢就是在程序运行时,动态的获取类(class),类的方法(method)属性(field)等。主要的注意点就是程序运行时动态的获取。
这里主要是从代码的角度来讲解Java反射。在使用中我们用的较多的几个类有ClassMethodFieldConstructor,Annotation等。
下面我们分别介绍下。

获取Class

获取class对象有如下三种方式:

  • cat.getClass
  • Cat.class
  • Class.forName("xyz.magicer.Cat")
        //方式1 调用对象的getClass()方式,该方法属于Object类
        Class<? extends Cat> aClass = cat.getClass();
        //方式2 直接类.class获取。
        Class<Cat> catClass = Cat.class;
        //方式3 通过Class类的静态方法forName获取,参数为想要获取的类的全名(含包名,不然会报ClassNotFoundException)
        try {
            Class<?> cat2 = Class.forName("xyz.magicer.Cat");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

在使用中根据实际情况选择,获取Class对象是我们使用反射的第一步。不过还是很简单的。获取了Class对象后,怎么创建一个实体呢?catClass.newInstance()
不过改方法会抛出两个异常:InstantiationExceptionIllegalAccessException

  • InstantiationException :当该Class不能被实例化的时候抛出该异常,例如,为抽象类、接口、数组类、基本类、Void等时。
  • IllegalAccessException:当无参构造为私有时
    还有一种创建实体的方式是使用Constructor,在下边在介绍。

Method

获取Method

类或接口的方法对应这Method这个类。我们通过Class对象来获取。主要方法有

  • getMethods(): 返回Method[] 返回所有public的方法。包含父类或父接口的方法。
  • getDeclaredMethods(): 返回Method[] 返回所有的方法。包括 private public defaultprotected的。不包含父类或父接口的方法
  • getMethod(String name, Class<?>... parameterTypes) : 返回Method, 根据方法名和类型获取Method。类或接口的及父类父接口公共成员方法。
  • getDeclaredMethod(String name, Class<?>... parameterTypes): 返回Method。根据方法名和类型返回Method。类和接口的所有方法。
        //sleep是private的
        Method[] methods = catClass.getMethods();
        for (Method method : methods) {
            //method.getName() 返回方法名,不包括修饰符,参数和返回值。
            System.out.printf(method.getName()+" ");
        } 
        // 打印toString getName setName setColor eat eat getAge setAge getColor wait wait wait equals hashCode getClass notify notifyAll 

        System.out.println();

        Method[] declaredMethods = catClass.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            System.out.printf(declaredMethod.getName()+" ");
        } 
        //打印 toString getName setName sleep setColor eat eat getAge setAge getColor 

        //抛出NoSuchMethodException 因为sleep的访问权限为private
       //Method sleep1 = catClass.getMethod("sleep", null);
        Method hashCode = catClass.getMethod("hashCode", null);
        //抛出NoSuchMethodException,因为hashCode是父类的方法。
       //Method hashCode1 = catClass.getDeclaredMethod("hashCode", null);
        Method eat2 = catClass.getMethod("eat",null);
        Method sleep1 = catClass.getDeclaredMethod("sleep", null);

通过上面的代码我们能看到,getMethods()可以获取父类的方法,但是不能获取私有方法,而getDeclaredMethod()方法不可以获取父类的方法,但是可以获取私有的方法。getMethod也是一样,可以获取父类父接口方法,但是无法获取私有方法,而getDeclaredMethod可以获取私有方法不能获取父类父接口方法。
而带s和不带s的区别是带s返回值为Method[],不带返回的是Method

调用Method

既然我们获取到了方法(Method)当然是想调用它,那么怎么调用呢?Method有个方法invoke(Object obj, Object... args).
invoke接收两个参数,第一个是调用该方法的实体,第二个是方法的参数。参数类型多个时顺序要跟方法参数相同。

        Method eat1 = catClass.getMethod("eat");
        eat1.invoke(catInstance,null); //打印:我只吃小鱼干。
        Method eat = catClass.getDeclaredMethod("eat");
        eat.invoke(catInstance,null);  //打印: 我只吃小鱼干。

        Method sleep = catClass.getDeclaredMethod("sleep");
        //IllegalAccessException
        sleep.invoke(catInstance,null);

我们会发现当私有方法invoke调用时会抛出IllegalAccessException,不是可以获取么为什么不能调用?因为方法有权限修饰符,我们需要设置成我们可以调用的。如下:

        sleep.setAccessible(true);
        sleep.invoke(catInstance,null);

在调用前设置为可以调用就解决了。
在前面我们接触到了两个Method类的方法了(getName,和invoke)。

Method常用方法

  • getModifiers(): 返回方法修饰符
  • getAnnotations(): 可以获取父类的注解
  • getDeclaredAnnotations(): 不可以返回父类的注解
  • getAnnotation(Class<T> annotationClass)
  • getDeclaredAnnotation(Class<T> annotationClass)
  • getParameters(): 返回Parameter[] (java1.8加入)

Field

获取Field对象的方式跟Method一样,用法和规律都一样。无非是现在方法改为getFields()、getDeclaredFields()、getField(String)、getDeclaredField(String)。

设置Field

可以通过set方法来设置值 public void set(Object obj, Object value) 每种基本数据类型都有的setxxx()方法。

        //name 为私有
        Field name = catClass.getDeclaredField("name");
        name.setAccessible(true);
        name.set(cat,"啦啦啦");
        System.out.println("\n"+cat.toString());

在使用反射设置属性的时候一定要注意,可能在代码中用到该属性的地方较多,改变了值之后引起一些意想不到的效果。

Constructor

获取Constructor对象的方式跟Field和Method一样。有四个方法:
getConstructors(),getDeclaredConstructors,getConstructor(Class<?>... parameterTypes), getDeclaredConstructor(Class<?>... parameterTypes)。

得到了Constructor对象后我们就可以调用newInstance(Object ... initargs)方法进行初始化了,注意参数的顺序。

        Constructor<Cat> constructor1 = catClass.getDeclaredConstructor(String.class, int.class, String.class);
        Cat cat1 = constructor1.newInstance("喵喵2", 3, "white");
        System.out.println(cat1.toString());

到这里我们看到,通过反射创建一个对象有两种方式:(1)class.newInstance()和(2)consturctor.newInstance(Object ... initargs)。那么他们有什么区别呢?
方式(1)只能调用无参构造创建对象,并且无参构造不能为私有,而方式(2)可以调用所有

        Constructor<Cat> constructor1 = catClass.getDeclaredConstructor(String.class, int.class, String.class);
        Cat cat1 = constructor1.newInstance("喵喵2", 3, "white");
        System.out.println(cat1.toString());

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

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,100评论 18 139
  • 一:java概述: 1,JDK:Java Development Kit,java的开发和运行环境,java的开发...
    慕容小伟阅读 1,734评论 0 10
  • 一:java概述:1,JDK:Java Development Kit,java的开发和运行环境,java的开发工...
    ZaneInTheSun阅读 2,582评论 0 11
  • Scala与Java的关系 Scala与Java的关系是非常紧密的!! 因为Scala是基于Java虚拟机,也就是...
    灯火gg阅读 3,343评论 1 24
  • 当你老了。 白发苍苍。 坐在椅子上休息。 回忆过去。 的点点滴滴的往事 是否是种幸福 回忆过去的酸甜苦辣 现在想起...
    迷之尴尬阅读 99评论 0 0