Android中反射机制解析 API介绍 创建private构造方法类实例 反射内部类 使用demo

版权声明:本文为博主原创文章,转载请标明出处 https://blog.csdn.net/qq_30993595/article/details/83900658

前言

在前面写一些Android源码分析的文章中,比如fork进程,SystemServer启动服务,ActivityThread加载等都会涉及到反射的大量运用,让我觉得很有必要针对反射机制写一篇博客进行总结

相关知识点

在讲解反射前先说明一些知识点以做铺垫

编译型语言和解释型语言

  • 编译型语言是指先将源代码编译生成机器码,然后再由机器运行机器码

  • 解释型语言是指先将源代码编译成中间介质,运行时再由解释器对中间介质进行解释运行

对于这两种定义,Java处于两种之间,同时具有编译型型和解释型的特点,要知道我们的Android项目运行过程是:

编译器先将项目源代码即Java文件编译成字节码即class文件,应用安装到手机,以后每次运行应用时,Java虚拟机再将字节码解释成机器码由机器运行,从这个过程看Java是解释型语

因为每次运行都要重新解释,其执行速度必然会比可执行的二进制字节码程序慢很多。为了提高执行速度,引入了 JIT 技术;在运行时, JIT 会把运行频率很高的字节码翻译成机器码保存起来,以后每次运行直接运行机器码,从这点看,又是编译型语言

动态类型语言和静态类型语言

  • 静态类型语言是指在编译时数据类型即可确定的语言,要求在变量定义时必须声明其数据类型,例如C#,C++,Java
  • 动态类型语言是指在运行时才确定数据类型的语言,变量在定义或者使用时无需声明类型,例如PHP,Ruby,Python,JavaScript,Shell等等

尽管在这样的定义与分类下Java不是动态语言,它却有着一个非常突出的动态相关机制:Reflection(反射);Reflection 是Java被视为动态(或准动态)语言的一个关键性质。这个机制允许程序在运行时透过Reflection API取得任何一个已知名称的class的内部信息,包括其modifiers(诸如public, static 等等)、superclass(例如Object)、实现之interfaces(例如Serializable),也包括fields和methods的所有信息,并可于运行时改变fields内容或调用methods

反射(Reflection)

JAVA反射机制是指在运行态可直接操作任意类或对象的所有属性和方法的功能,它只是提供给Java一个动态修改功能,但是无法修改程序结构或变量类型

反射机制的用途

  • 在运行时获取任意一个对象所属的类:Class<?> clazz = Class.forName(String className)
  • 在运行时构造任意一个类的对象:Object obj = clazz.newInstance()
  • 在运行时获取任意一个类所具有的成员变量和方法:field.set(Object obj, Object value); field.get(Object obj)
  • 在运行时调用任意一个对象的方法,这应该是需求最大的一个功能了,特别是这个方法是private的或者hide的:method.invoke(Object obj, Object… args)

反射还可以获取类的其他信息,包含modifiers,superclass, 实现的interfaces等

实现反射相关的类

  • Class:代表一个类
  • Constructor:代表类的构造方法
  • Method:代表类的方法
  • Field:代表类的属性或者成员变量
  • Array:提供动态创建数组能力及访问数组的元素的静态方法
  • Modifier:代表类,方法,属性的描述修饰符

注意:Modifier的取值有public,protected,private,abstract,static,final,transient,volatile,synchronized,native, strictfp,interface;Constructor, Field, Method这三个类都继承AccessibleObject,该对象有一个非常重要的方法setAccessible(boolean flag), 借助该方法,能直接调用非Public的属性与方法

Class

我们知道在Java体系中,所有类直接或间接继承与Object类,而Object类声明了很多重要的需要被子类重写的方法,其中一个就是getClass,该方法返回要给Class类对象,所以对于任何一个Java对象,都可以通过此方法获得对象的类型

要知道Java程序在运行时,系统对每一个对象都有一个类型标识,用于记录对象所属的类,虚拟机使用这个类型来选择相应方法去执行;而保存所有对象类型信息的类就是Class类;实际过程是运行程序时,虚拟机首先会检测所要加载的类对象的Class对象是否已经加载,如果没有加载,虚拟机就会根据类名查找class文件,并将其Class对象载入,这样每个类都有一个Class对象,它们由虚拟机进行管理

要知道Class类没有公共的构造方法,所以不能通过new的方法去构造一个Class对象,虚拟机通常是调用ClassLoader的defineClass方法构造

基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也都对应一个 Class 对象

Class类API

  • Class<?> forName(String className):返回一个与指定参数相同的Class对象(要求JVM查找并加载指定的类,也就是说JVM会执行该类的静态代码段)
  • Class<? super T> getSuperclass():获取调用对象的父类
  • Constructor<?>[] getConstructors():获得类的public类型的构造方法
  • Constructor getConstructor(Class<?>… parameterTypes):获得类的特定构造方法,parameterTypes 参数指定构造方法的参数类型
  • Constructor<?>[] getDeclaredConstructors():获取所有构造方法
  • Method[] getMethods():获得类的public类型的方法
  • Method[] getDeclaredMethods():获得类的所有方法
  • Method getDeclaredMethod(String name, Class<?>… parameterTypes):根据方法名和参数类型获取指定方法
  • Field[] getFields():获得类的public类型的属性
  • Field getField(String name):获取指定名称的public类型的属性
  • Field[] getDeclaredFields():获得类的所有属性
  • Field getDeclaredField(String name):获取指定名称属性
  • String getName():获得一个实体的名称(实体可能是类, 接口, 数组,基本类型, void修饰符),就是forName方法的参数
  • T newInstance():通过类的不带参数且public的构造方法创建这个类的一个对象
  • String getInnerClassName():获取内部类名称

Constructor类 API

  • getModifiers():获取构造方法的修饰符,比如private,public等
  • getParameterTypes():获取构造方法中参数的类型
  • newInstance(Object … initargs):传递参数,创建实例化对象
  • setAccessible(true):设置允许访问,禁止Java修饰符访问检查

Method类API

  • setAccessible(boolean flag):设置允许访问,禁止Java修饰符访问检查
  • Class<?>[] getParameterTypes():获取方法参数类型
  • Class<?> getReturnType():获取方法返回值类型
  • Object invoke(Object receiver, Object… args):执行方法,这也是最重要的一个方法了,接收两个参数,第一个参数是拥有该方法的对象实例,第二个参数是参数类型

Field类API

  • Object get(Object object):获取对象object的指定属性值,还有一堆getXXX方法,是用来获取基本数据类型的属性的值
  • void set(Object object, Object value):设置对象object的指定属性的值为value,还有一堆setXXX方法,用来设置基本数据类型的属性的值
  • setAccessible(boolean flag):设置允许访问,禁止Java修饰符访问检查

反射的使用

API的使用样例

我们这里以String类为例进行API的介绍

		Class c;
		try {
            c = Class.forName("java.lang.String");
            Class sup = c.getSuperclass();
            Log.e(TAG,"onCreate sup="+sup);
            Log.e(TAG,"onCreate c="+c);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

看看打印结果

E: onCreate sup=class java.lang.Object
E: onCreate c=class java.lang.String

获取构造方法

public void getConstructor(){

        //获取所有public的构造方法
        Constructor[] constructors = c.getConstructors();
        for (int i=0; i<constructors.length; i++) {
            Log.e(TAG,"getConstructor constructors="+constructors[i]);
        }
        Log.e(TAG,"getConstructor ===============");
        //获取所有构造方法
        Constructor[] constructors2 = c.getDeclaredConstructors();
        for (int i=0; i<constructors2.length; i++) {
            Log.e(TAG,"getConstructor constructors="+constructors2[i]);
        }
        Log.e(TAG,"getConstructor ===============");
        //获取指定参数类型的构造方法
        Class cs[] = {java.lang.String.class};
        Constructor constructors3 ;
        try {
            constructors3 = c.getConstructor(cs);
            Log.e(TAG,"getConstructor constructors3="+constructors3);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }


    }
E: getConstructor constructors=public java.lang.String()
E: getConstructor constructors=public java.lang.String(java.lang.String)
E: getConstructor constructors=public java.lang.String(java.lang.StringBuffer)
E: getConstructor constructors=public java.lang.String(java.lang.StringBuilder)
E: getConstructor constructors=public java.lang.String(byte[])
E: getConstructor constructors=public java.lang.String(byte[],int)
E: getConstructor constructors=public java.lang.String(byte[],int,int)
E: getConstructor constructors=public java.lang.String(byte[],int,int,int)
E: getConstructor constructors=public java.lang.String(byte[],int,int,java.lang.String) throws java.io.UnsupportedEncodingException
E: getConstructor constructors=public java.lang.String(byte[],int,int,java.nio.charset.Charset)
E: getConstructor constructors=public java.lang.String(byte[],java.lang.String) throws java.io.UnsupportedEncodingException
E: getConstructor constructors=public java.lang.String(byte[],java.nio.charset.Charset)
E: getConstructor constructors=public java.lang.String(char[])
E: getConstructor constructors=public java.lang.String(char[],int,int)
E: getConstructor constructors=public java.lang.String(int[],int,int)
E: getConstructor ===============
E: getConstructor constructors=public java.lang.String()
E: getConstructor constructors=java.lang.String(int,int,char[])
E: getConstructor constructors=public java.lang.String(java.lang.String)
E: getConstructor constructors=public java.lang.String(java.lang.StringBuffer)
E: getConstructor constructors=public java.lang.String(java.lang.StringBuilder)
E: getConstructor constructors=public java.lang.String(byte[])
E: getConstructor constructors=public java.lang.String(byte[],int)
E: getConstructor constructors=public java.lang.String(byte[],int,int)
E: getConstructor constructors=public java.lang.String(byte[],int,int,int)
E: getConstructor constructors=public java.lang.String(byte[],int,int,java.lang.String) throws java.io.UnsupportedEncodingException
E: getConstructor constructors=public java.lang.String(byte[],int,int,java.nio.charset.Charset)
E: getConstructor constructors=public java.lang.String(byte[],java.lang.String) throws java.io.UnsupportedEncodingException
E: getConstructor constructors=public java.lang.String(byte[],java.nio.charset.Charset)
E: getConstructor constructors=public java.lang.String(char[])
E: getConstructor constructors=public java.lang.String(char[],int,int)
E: getConstructor constructors=public java.lang.String(int[],int,int)
E: getConstructor ===============
E: getConstructor constructors3=public java.lang.String(java.lang.String)

可以看到getDeclaredConstructors比getConstructors返回结果多了一个非public的构造方法

获取方法

public void getMethods(){

        //获取所有public方法
        Method[] m = c.getMethods();
        for (int i=0; i<m.length; i++) {
            Log.e(TAG,"getMethods m="+m[i]);
        }
        Log.e(TAG,"getMethods ===============");

        //获取所有方法
        Method[] m2 = c.getDeclaredMethods();
        for (int i=0; i<m2.length; i++) {
            Log.e(TAG,"getMethods m2="+m2[i]);
        }
        Log.e(TAG,"getMethods ===============");

        //获取指定方法名和参数类型的方法
        Class cs[] = {java.lang.String.class};
        try {
            Method m3 = c.getDeclaredMethod("getBytes",cs);
            Log.e(TAG,"getMethods m3="+m3);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }

这个打印结果太多就不列举了,指列举 获取指定方法名和参数类型的方法 结果

E: getMethods m3=public byte[] java.lang.String.getBytes(java.lang.String) throws java.io.UnsupportedEncodingException

获取属性

public void getField(){
        //获取所有public属性
        Field[] f = c.getFields();
        for (int i=0; i< (f == null ? 0:f.length); i++) {
            Log.e(TAG,"getField f="+f[i]);
        }
        Log.e(TAG,"getField ===============");

        //获取所有属性
        Field[] f2 = c.getDeclaredFields();
        for (int i=0; i<f2.length; i++) {
            Log.e(TAG,"getField f2="+f2[i]);
        }
        Log.e(TAG,"getField ===============");

        try {
            //获取指定名称的public属性
            Field f3 = c.getField("CASE_INSENSITIVE_ORDER");
            Log.e(TAG,"getField f3="+f3);
            Log.e(TAG,"getField ===============");
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }

        try {
            //获取指定名称的属性
            Field f4 = c.getDeclaredField("count");
            Log.e(TAG,"getField f4="+f4);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }

看结果

E: getField f=public static final java.util.Comparator java.lang.String.CASE_INSENSITIVE_ORDER
E: getField ===============
E: getField f2=private final int java.lang.String.count
E: getField f2=private int java.lang.String.hash
E: getField f2=public static final java.util.Comparator java.lang.String.CASE_INSENSITIVE_ORDER
E: getField f2=private static final java.io.ObjectStreamField[] java.lang.String.serialPersistentFields
E: getField f2=private static final long java.lang.String.serialVersionUID
E: getField ===============
E: getField f3=public static final java.util.Comparator java.lang.String.CASE_INSENSITIVE_ORDER
E: getField ===============
E: getField f4=private final int java.lang.String.count

Demo使用样例

这里通过自己写的demo来进行反射使用练习

场景是定义一个密码加密类,有两个构造方法且都是private修饰,有两个加密方法,一个私有一个公有;现在通过反射获取私有构造方法来创建类实例,并且调用两个方法

先定义加密类

/**
 * @Description TODO(反射使用演示)
 * @author cxy
 * @Date 2018/11/12 9:14
 */
public class PasswordEncrypt {

    public String TAG = PasswordEncrypt.class.getSimpleName();

    private int version = 1;

    private PasswordEncrypt(){
        Log.e(TAG,"PasswordEncrypt");
    }

    private PasswordEncrypt(int version){
        this.version = version;
        Log.e(TAG,"PasswordEncrypt version="+version);
    }

    private String encodeStr(String pw){
        return Base64.encodeToString(pw.getBytes(),Base64.NO_WRAP);
    }

    public String encryptMD5(String pw){

        if (TextUtils.isEmpty(pw)) throw new NullPointerException("password not be null");
        String result = "";
        try {

            MessageDigest md5 = MessageDigest.getInstance("MD5");
            byte[] buff = md5.digest(pw.getBytes());
            for (byte b : buff) {
                String temp = Integer.toHexString(b & 0xff);
                if (temp.length() == 1) {
                    temp = "0" + temp;
                }
                result += temp;
            }
        } catch (NoSuchAlgorithmException e) {
            Log.e(TAG,"encryptMD5 NoSuchAlgorithmException="+e);
            e.printStackTrace();
        }
        return result;
    }

}

正常来讲,这个类是没办法构建出实例的,里面的方法也没法调用,但是通过反射可以破解私有构造方法来创建实例,如下:

获取Class对象

Class<PasswordEncrypt> classType = null;
try {
    classType = (Class<PasswordEncrypt>) Class.forName("com.mango.reflex.PasswordEncrypt");
} catch (ClassNotFoundException e) {
    e.printStackTrace();
}
Log.e(TAG,"classType="+classType);

通过 Class.forName 拿到PasswordEncrypt的Class对象,看日志

E/MainActivity: classType=class com.mango.reflex.PasswordEncrypt

成功拿到

获取构造方法

接下来就要通过这个Class对象构建PasswordEncrypt实例了,因为PasswordEncrypt两个构造方法都是private的,所以不能通过如下方法构建实例

		//这种方法只能构建拥有公有构造方法的对象
		try {
            PasswordEncrypt encry = classType.newInstance();
            String result = encry.encryptMD5("123456");
            Log.e(TAG,"result="+result);
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

这样我们必须通过Class拿到PasswordEncrypt的构造方法,然后设置它的访问属性,最后再构建实例

		//拿到所有构造方法,包括private的
        Constructor[] data = classType.getDeclaredConstructors();
        for (Constructor con : data) {
            con.setAccessible(true);
            Log.e(TAG,"con=" + con);
        }

        //根据参数类型拿到指定构造方法
        Constructor constructor = null;
        try {
            constructor = classType.getDeclaredConstructor(int.class);
            constructor.setAccessible(true);
            Log.e(TAG,"constructor=" + constructor);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }

看日志

E/MainActivity: con=private com.mango.reflex.PasswordEncrypt()
E/MainActivity: con=private com.mango.reflex.PasswordEncrypt(int)
E/MainActivity: con=com.mango.reflex.PasswordEncrypt(java.lang.Object[],com.android.tools.fd.runtime.InstantReloadException)
E/MainActivity: constructor=private com.mango.reflex.PasswordEncrypt(int)

可以看到能成功拿到构造方法,接下来通过构造方法来构建实例,然后调用encryptMD5方法

构造实例 调用public 方法及属性

	try {
			//通过无参构造方法构建
            PasswordEncrypt encry = (PasswordEncrypt) data[0].newInstance();
            String result = encry.encryptMD5("123456");
            String tag = encry.TAG;
            Log.e(TAG,"result = " + result);
			//通过带参构造方法构建
            PasswordEncrypt encry2 = (PasswordEncrypt) constructor.newInstance(2);
            String result2 = encry2.encryptMD5("123456");
            Log.e(TAG,"result2 = " + result2);

        } catch (InstantiationException e) {
            Log.e(TAG,"InstantiationException = " + e);
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            Log.e(TAG,"IllegalAccessException = " + e);
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

newInstance方法的参数是一个Object数组,表示构造方法的参数,有几个就传几个,没有就不用传;看日志结果

11-12 11:09:20.088 2605-2605/com.mango.reflex E/PasswordEncrypt: PasswordEncrypt
11-12 11:09:20.094 2605-2605/com.mango.reflex E/MainActivity: result = e10adc3949ba59abbe56e057f20f883e
11-12 11:09:20.095 2605-2605/com.mango.reflex E/PasswordEncrypt: PasswordEncrypt version=2
11-12 11:09:20.095 2605-2605/com.mango.reflex E/MainActivity: result2 = e10adc3949ba59abbe56e057f20f883e

可以看到构造方法被调用,encryptMD5方法也被调用了

访问修改private属性

构造出的对象只能调用public修饰的方法和变量,其它修饰词修饰的需要通过别的方法调用,如下

//根据属性名获取指定属性
Field field = classType.getDeclaredField("version");
//设置对private修饰的属性的访问
field.setAccessible(true);
//获取属性值
int value = field.getInt(encry);
Log.e(TAG,"value = " + value);

E/MainActivity: value = 1

可以看到获取到默认值1

接下来我们修改这个值

field.setInt(encry,10);
int valueNew = field.getInt(encry);
Log.e(TAG,"valueNew = " + valueNew);

E/MainActivity: valueNew = 10

调用private方法

接下来进行private修饰的方法的访问(PasswordEncrypt类的encodeStr方法)

Method m = classType.getDeclaredMethod("encodeStr",String.class);
m.setAccessible(true);
String result = (String) m.invoke(encry,"123456");
Log.e(TAG,"result = " + result);

E/MainActivity: result = MTIzNDU2

从这个例子可以看到通过反射你可以做到平时学的Java规则做不到的事,总结下反射的使用步骤

反射获取内部类

这里内部类有成员内部类 ,静态内部类,匿名内部类

public class InnerClass {   

    public InnerClass () { }

    private class InnerA {
        private String f = InnerA.class.getSimpleName();
        public InnerA() { }
    }

    private static class InnerB {
        private String f = InnerB.class.getSimpleName();
        public InnerB() {}
    }

    private Runnable r = new Runnable() {       
        @Override
        public void run() {
            
        }
    };

}  

		Class clazz = InnerClass.class;
        InnerClass container = (InnerClass ) clazz.newInstance();
        Class innerClazz[] = clazz.getDeclaredClasses();
        for (Class cls : innerClazz) {
            int mod = cls.getModifiers();
            String modifier = Modifier.toString(mod);
            if (modifier.contains("static")) {
                //构造静态内部类实例
                Object obj1 = cls.newInstance();
                Field field1 = cls.getDeclaredField("f");
                field1.setAccessible(true);
            } else {
                // 构造成员内部类实例
                Constructor con2 = cls.getDeclaredConstructor(clazz);
                con2.setAccessible(true);
                Object obj2 = con2.newInstance(container);
                Field field2 = cls.getDeclaredField("f");
                field2.setAccessible(true);
            }
        }
        // 获取匿名内部类实例
        Field field = clazz.getDeclaredField("r");
        field.setAccessible(true);
        Runnable r = (Runnable) field.get(container);
        r.run();

使用总结

  • 通过Class.forName拿到类的Class对象
  • 如果有公有构造方法,直接通过Class.newInstance()构造实例
  • 如果没有公有构造方法,通过Class.getConstructor(Class<?>… parameterTypes)获取指定构造方法对象Constructor,调用setAccessible设置运行访问,再使用Constructor.newInstance(Object … initargs)方法构造对象实例
  • 如果是公有方法和属性,可直接通过构建的实例访问
  • 如果是私有属性,先通过Class类的getDeclaredField(String name)方法获取属性对象Field,调用setAccessible设置运行访问,通过Field类的get相关方法获取属性值,通过set相关方法修改属性值
  • 如果是私有方法,先通过Class类的getDeclaredMethod(String name, Class<?>… parameterTypes)方法获取指定方法对象Method,调用setAccessible设置运行访问,最后调用invoke(Object receiver, Object… args)方法执行方法

猜你喜欢

转载自blog.csdn.net/qq_30993595/article/details/83900658