美文网首页
Android基础-Java反射

Android基础-Java反射

作者: echoSuny | 来源:发表于2021-03-04 15:14 被阅读0次

    一般情况下,我们使用某个类时必须知道它是什么类,是用来做什么的,并且能够获得此类的引用。于是我们直接对这个类进行实例化,之后使用这个类对象进行操作。
    反射则是一开始并不知道我们要初始化的类的对象是什么,自然也无法使用 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 元数据中。

    相关文章

      网友评论

          本文标题:Android基础-Java反射

          本文链接:https://www.haomeiwen.com/subject/jlfcqltx.html