移动架构<第十篇>:反射机制

Java反射机制是移动架构的重要知识体系中的一部分,可以说想要成为合格的移动架构师必须了解Java的反射机制

(1)概念

Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。反射被视为动态语言的关键。

以上定义是在科学百科中找到的。

(2)本质

所谓反射,其实就是获取字节码文件,即.class文件,最后生成Class对象,拿到这个对象之后就可以对这个对象为所欲为了。

(3)获取Class对象的三种方式

上面说到,反射机制的本质是获取.class文件,生成Class对象,那么该如何获取Class对象呢?

[第一种方式]

如果我们可以拿到某对象的实例如对象Test,那么获取Test的Class对象的代码如下:

Class<?> cls = new Test().getClass();

[第二种方式]

在知道类名的情况下:

Class<?> cls = Test.class;

[第三种方式]

前两种获取Class对象的方式比较简单,但是必须保证Test对象能直接访问,但是,在很多时候,我们想要获取的Class对象不能直接访问,这个时候必须采用第三种获取Class对象的方式,如下:

    Class<?> cls = null;
    try {
        cls = Class.forName("com.juexing.mytest.Test");
        System.out.println(cls.getName());
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }

forName()方法里传递的参数是Class的Name,所以,如果Test对象可以直接访问,也可以写成:

    Class<?> cls = null;
    try {
        cls = Class.forName(Test.class.getName());
        System.out.println(cls.getName());
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }

[三种方式如何选择]

第一种方式显然已经获取到对象了,所以没必要使用复杂化的反射机制;
第二种方式会导致模块与模块之间的耦合性,也不建议使用;
第三种方式是反射机制常用的方法,建议使用。

(4)获取构造方法
// 1.获取参数列表是parameterTypes,访问控制符是public的构造函数
public Constructor getConstructor(Class[] parameterTypes)

// 2.获取所有访问控制符是public的构造函数
public Constructor[] getConstructors()

// 3.获取参数列表是parameterTypes,并且是类自身声明的构造函数,访问控制符包含public、protected和private的函数。
public Constructor getDeclaredConstructor(Class[] parameterTypes)

//4.获取类自身声明的全部的构造函数,访问控制符包含public、protected和private的函数。
public Constructor[] getDeclaredConstructors()

//5.如果类声明在其它类的构造函数中,返回该类所在的构造函数,如果存在则返回,不存在返回null
public Constructor getEnclosingConstructor()

举例:

Constructor<?> constructor = cls.getDeclaredConstructor();//没有参数
Constructor<?> constructor = cls.getDeclaredConstructor(int.class);//一个参数
Constructor<?> constructor = cls.getDeclaredConstructor(String.class, String.class);//两个参数

拿到constructor对象之后可以新建当前对象的一个实例:

constructor.newInstance();

这个方法在实际运用中会经常用到。

(5)获取成员函数
// 1.获取函数名是name,参数是parameterTypes的public的函数(包括从基类继承的、从接口实现的所有public函数)
 public Method getMethod(String name, Class[] parameterTypes)

 //2.获取全部的public的函数(包括从基类继承的、从接口实现的所有public函数)
 public Method[] getMethods()

 //3.获取函数名name,参数是parameterTypes,并且是类自身声明的函数,包含public、protected和private方法。
 public Method getDeclaredMethod(String name, Class[] parameterTypes)

 //4.获取全部的类自身声明的函数,包含public、protected和private方法。
 public Method[] getDeclaredMethods()

 //5.如果这个类是其它类中某个方法的内部类,调用getEnclosingMethod()就是这个类所在的方法;若不存在,返回null。
 public Method getEnclosingMethod()

举例:

        Class<?> cls = Class.forName("com.juexing.mytest.ATest");
        Method methodA = cls.getDeclaredMethod("testA");
        System.out.println(methodA.getName());
        Method methodB = cls.getDeclaredMethod("testB", String.class);
        System.out.println(methodB.getName());
        Method[] methods = cls.getDeclaredMethods();
        for(Method method : methods){
            System.out.println(method.getName());
        }

那么,得到Method对象之后又可以做什么操作呢?

[获取方法名]

methodA.getName();

[调用方法]

使用invoke方法调用指定方法,如:

methodA.invoke(constructor.newInstance());

invoke方法第一个参数传递指定类的实例(如果能获取到对应类的实例最好,如果获取不到,只用使用constructor.newInstance()代替了),后面几个参数传递方法的参数。

假设有这样一个类

public class ATest {

    public ATest(){

    }

    public void testA(){
        System.out.println("调用了testA");
    }

    public String testB(String a1, int b, float c){
        System.out.println(a1);
        return a1;
    }

    private void testC(){
        System.out.println("调用了testC");
    }
}

那么,该如何使用反射机制调用testA、testB、testC这三个方法呢?

调用testA(最简单的调用方式)

        Class<?> cls = Class.forName("com.juexing.mytest.ATest");
        Constructor constructor = cls.getDeclaredConstructor();
        Method methodA = cls.getDeclaredMethod("testA");
        methodA.invoke(constructor.newInstance());

调用testB(注意返回值和参数)

        Class<?> cls = Class.forName("com.juexing.mytest.ATest");
        Constructor constructor = cls.getDeclaredConstructor();
        Method methodB = cls.getDeclaredMethod("testB", String.class, int.class, float.class);
        String aa = (String) methodB.invoke(constructor.newInstance(), "调用了testB", 2, 2.0f);
        System.out.println(aa);

invoke方法除了第一个参数外,剩下的几个参数都是testB方法的参数传值,aa是返回值。

调用testC(注意private修饰符)

        Class<?> cls = Class.forName("com.juexing.mytest.ATest");
        Constructor constructor = cls.getDeclaredConstructor();
        Method methodC = cls.getDeclaredMethod("testC");
        methodC.invoke(constructor.newInstance());

我们都知道,Java是无法访问其他类中的私有方法,如果使用以上代码调用testC方法会直接报错,如下:

图片.png

那么,反射机制能否调用私有方法?答案是可以的。

在代码中加入以下代码:

        methodC.setAccessible(true);

可以解决访问受限的问题。

(6)获取成员变量
//1.获取“名称是name”的public的成员变量(包括从基类继承的、从接口实现的所有public成员变量)
public Field getField(String name)

//2.获取全部的public成员变量(包括从基类继承的、从接口实现的所有public成员变量)
public Field[] getFields()

//3.获取“名称是name”,并且是类自身声明的成员变量,包含public、protected和private成员变量。
public Field getDeclaredField(String name)

//4.获取全部的类自身声明的成员变量,包含public、protected和private成员变量。
public Field[] getDeclaredFields()

拿到字段信息就可以访问这些字段了,如:

        field.get(obj);
        field.getBoolean(obj);
        field.getByte(obj);
        field.getChar(obj);
        field.getFloat(obj);
        field.getDouble(obj);
        field.getLong(obj);

当然,如果字段的修饰否是private,需要添加以下代码:

        field.setAccessible(true);

既然可以获取字段,当然也可以修改字段:

        field.set(obj, null);
        field.setBoolean(obj, false);
        field.setByte(obj, (byte)1);
        field.setChar(obj, 'C');
        field.setFloat(obj, 1.0f);
        field.setDouble(obj, 1.0);
        field.setLong(obj, 1);
(7)总结

反射机制的相关方法还有很多,我想,了解以上提到的内容应该就差不多了,在一些主流框架中,反射机制运用非常的广泛,了解反射机制是成为架构师的必备功课之一。

[本章完]

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