Android基础-Java反射

一般情况下,我们使用某个类时必须知道它是什么类,是用来做什么的,并且能够获得此类的引用。于是我们直接对这个类进行实例化,之后使用这个类对象进行操作。
反射则是一开始并不知道我们要初始化的类的对象是什么,自然也无法使用 new 关键字来创建对象了。这时候,我们使用JDK提供的反射API进行反射调用。反射就是在运行状态中,对于任意一个类,都能够知道这个类的所有方法和属性。对于任意一个对象都能够调用它的任意方法和属性,并且能改变它的属性。是 Java 被视为动态语言的关键。
Java 的反射主要提供了以下功能:

  • 在运行时构造任意一个类的对象
  • 在运行时获取或者修改任意一个类所具有的成员变量和方法
  • 在运行时调用任意一个对象的方法(属性)
Class

反射始于Class,Class是一个类,封装了当前对象所对应的类的信息。一个类中有属性,方法,构造器等。比如说有一个 Student 类,一个 Animal 类,这些都是不同的类。现在需要一个类,它的作用是用来描述这些类,比如一个普通的类应该有构造方法,成员属性,方法等。那么这个能描述类的类就是Class。
Class类是一个对象照镜子的结果,对象可以看到自己有哪些方法,属性,构造器,实现了哪些接口等等。对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象。一个 Class 对象包含了特定某个类的有关信息。对象只能由系统建立对象,一个类(而不是一个对象)在JVM中只会有一个 Class 实例,不管这个类被 new 了多少次。

获得 Class 对象
  • 通过类名获取:类名.class
        Class<Fruit> fruitClazz = Fruit.class;
  • 通过对象获取:对象.getClass()
        Fruit fruit = new Fruit();
        Class<? extends Fruit> fruitClazz = fruit.getClass();
  • 通过全类名获取:Class.forName(全类名),classLoader.loadClass(全类名)
        // 通过静态方法
        try {
            Class<?> fruitClazz = Class.forName("com.example.Fruit");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        // 通过ClassLoader
        try {
            ClassLoader classLoader = getClassLoader();
            Class<?> fruitClazz = classLoader.loadClass("com.example.Fruit");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
判断是否为某个类的实例

一般的,我们使用 instanceof 关键字来判断是否为某个类的实例。同时为我们也可以借助反射中 Class 对象的 isInstance() 方法来判断是否为某个类的实例,它是一个 native 方法。

    public boolean isInstance(Object obj);

判断是否为某个类的类型:

    public boolean isAssignableFrom(Class<?> cls);
创建实例

通过反射来生成对象实例主要有两种方式:

  • 使用 Class 对象的 newInstance() 方法来创建 Class 对象对应类的实例。
        Class<Fruit> fruitClazz = Fruit.class;
        Fruit fruit = fruitClazz.newInstance();
  • 先通过 Class 对象获取指定的 Constructor 对象,再调用 Constructor 对象的 newInstance()方法来创建实例。这种方法可以用指定的构造器来构造类的实例。
        Class<Fruit> fruitClazz = Fruit.class;
        //   获取构造参数为 String 类型的构造器
        Constructor<Fruit> constructor = fruitClazz.getConstructor(String.class);
        // 使用构造器创建实例
        Fruit fruit = constructor.newInstance("name");
获取构造器信息
  • 获得类的所有公共构造函数
    public Constructor<?>[] getConstructors()
  • 获得使用特殊的参数类型的public构造函数(包括父类)
    public Constructor<T> getConstructor(Class<?>... parameterTypes)
  • 获得类的所有构造函数(与接入级别无关)
    public Constructor<?>[] getDeclaredConstructors()
  • 获得使用特定参数类型的构造函数(包括私有)
    public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
获取类的成员变量
  • 获得命名的公共字段
    public Field getField(String name)
  • 获得类的所有公共字段
    public Field[] getFields() 
  • 获得类声明的命名的字段
    public native Field getDeclaredField(String name)
  • 获得类声明的所有字段
    public native Field[] getDeclaredFields();
调用方法
  • 获得类的所有公共方法
    public Method[] getMethods()
  • 使用特定的参数类型,获得命名的公共方法
    public Method getMethod(String name, Class<?>... parameterTypes)
  • 获得类声明的所有方法
    public Method[] getDeclaredMethods() 
  • 使用特定的参数类型,获得类声明的命名的方法
    public Method getDeclaredMethod(String name, Class<?>... parameterTypes) 

当我们从类中获取了一个方法后,我们就可以使用 invoke() 方法来调用这个方法:

    public native Object invoke( Object obj, Object... args)
利用反射创建数组

数组在 Java 里是比较特殊的一种类型,它可以赋值给一个 Object Reference 其中的 Array 类为 java.lang.reflect.Array 类。我们通过 Array.newInstance()创建数组对象,它的原型是:

public static Object newInstance(Class<?> componentType,int length);
反射获取真实类型

当我们对一个泛型类进行反射时,需要得到泛型中的真实数据类型,来完成如json反序列化的操作。此时需要通过 Type 体系来完成。Type 接口包含了一个实现类(Class) 和四个实现接口,它们分别是:

  • TypeVariable 泛型类型变量,可以获得泛型上下限等信息
public class TypeStudy<K extends Comparable & Serializable, V> {

    public K mKey;
    public V mValue;

    public static void main(String[] args) throws NoSuchFieldException {
        // 通过反射获取字段
        Field keyField = TypeStudy.class.getDeclaredField("mKey");
        Field valueField = TypeStudy.class.getDeclaredField("mValue");
        // 强转为TypeVariable
        TypeVariable keyType = (TypeVariable) keyField.getGenericType();
        TypeVariable valueType = (TypeVariable) valueField.getGenericType();
        // 获取泛型变量的名称
        String keyName = keyType.getName();
        String valueName = valueType.getName();

        System.out.println("KeyName:" + keyName);
        System.out.println("ValueName:" + valueName +"\n");
        // 获取泛型声明信息
        GenericDeclaration keyDeclaration = keyType.getGenericDeclaration();
        GenericDeclaration valueDeclaration = valueType.getGenericDeclaration();

        System.out.println("KeyDeclaration:" + keyDeclaration);
        System.out.println("ValueDeclaration:" + valueDeclaration +"\n");
        // 获取边界
        Type[] keyBounds = keyType.getBounds();
        Type[] valueBounds = valueType.getBounds();

        for (Type keyBound : keyBounds) {
            System.out.println("KeyBound:" + keyBound);
        }

        for (Type valueBound : valueBounds) {
            System.out.println("ValueBound:" + valueBound);
        }
    }
}
输出结果
  • ParameterizedType 具体的泛型类型,可以获得元数据中泛型签名类型(泛型真实类型)
public class TypeStudy{

    public Map<String,Integer> map;

    public static void main(String[] args) throws NoSuchFieldException {

        Field mapField = TypeStudy.class.getDeclaredField("map");

        Type type = mapField.getGenericType();
        System.out.println("Type: " + type + "\n");

        ParameterizedType parameterizedType = (ParameterizedType) mapField.getGenericType();

        Type ownerType = parameterizedType.getOwnerType();
        System.out.println("OwnerType: " + ownerType+ "\n");
        Type rawType = parameterizedType.getRawType();
        System.out.println("RawType: " + rawType+ "\n");
        Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
        for (Type actualTypeArgument : actualTypeArguments) {
            System.out.println("ActualTypeArgument: " + actualTypeArgument);
        }
}
输出结果
  • GenericArrayType 当需要描述的是泛型类的数组时,比如List[],Map[],此接口会作为 Type 的实现
public class TypeStudy{

    List<String>[] lists;
    public static void main(String[] args) throws NoSuchFieldException {

        Field field = TypeStudy.class.getDeclaredField("lists");
        GenericArrayType genericType = (GenericArrayType) field.getGenericType();
        Type genericComponentType = genericType.getGenericComponentType();
        System.out.println(genericComponentType);
}
输出结果
  • WildcardType 通配符泛型,获取上下限信息
public class TypeStudy{

    List<? extends Number> numbers;
    List<? super String> strs;
    public static void main(String[] args) throws NoSuchFieldException {

        Field numbersField = TypeStudy.class.getDeclaredField("numbers");
        Field strsField = TypeStudy.class.getDeclaredField("strs");
        // 先拿到泛型类型
        ParameterizedType numbersType = (ParameterizedType) numbersField.getGenericType();
        ParameterizedType strsType = (ParameterizedType) strsField.getGenericType();
        // 再从泛型里拿到通配符类型
        WildcardType numbersWildcardType = (WildcardType) numbersType.getActualTypeArguments()[0];
        WildcardType strsWildcardType = (WildcardType) strsType.getActualTypeArguments()[0];

        System.out.println(numbersWildcardType.getUpperBounds()[0]);
        System.out.println(strsWildcardType.getLowerBounds()[0] + "\n");

        System.out.println(numbersWildcardType);
        System.out.println(strsWildcardType);
}
输出结果
Gson反序列化
public class Response<T> {
    T data;
    int code;
    String msg;

    public Response(T data, int code, String msg) {
        this.data = data;
        this.code = code;
        this.msg = msg;
    }

    @Override
    public String toString() {
        return "Response{" +
                "data=" + data +
                ", code=" + code +
                ", msg='" + msg + '\'' +
                '}';
    }
}
public class Data {
    String result;

    public Data(String result) {
        this.result = result;
    }

    @Override
    public String toString() {
        return "Data{" +
                "result='" + result + '\'' +
                '}';
    }
}
public class TypeStudy {

    public static void main(String[] args) {

        Gson gson = new Gson();

        Data data = new Data("i am result");

        Response<Data> response = new Response<>(data, 200, "ok");
        // 序列化数据
        String json = gson.toJson(response);

        System.out.println("json: " + json);
        // 反序列化数据
        Response<Data> resp = gson.fromJson(json, new TypeToken<Response<Data>>() {
        }.getType());

        System.out.println(resp.data.result);
}

对于经常进行网络请求的 coder 来说,上面的代码肯定不会陌生。可是 TypeToken 为什么要被定义称抽象类呢?这是因为只有抽象类或者接口在使用时需要创建对应的实现类,此时确定泛型类型,编译器才能将泛型 signature 信息记录到 Class 元数据中。

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

推荐阅读更多精彩内容