Android反射1-基本反射技术

news/2024/5/19 3:02:07 标签: Android, 反射

文章目录

  • 1 根据一个字符串得到一个类
    • 1.1 getClass
    • 1.2 Class.forName
    • 1.3 class属性
    • 1.4 TYPE属性
  • 2 获取类的成员
    • 2.1 类的构造函数
      • 2.1.1 获取类的所有构造函数。
      • 2.1.2 获取类的公共构造函数。
      • 2.1.3 获取类的某个构造函数
      • 2.1.4 调用构造函数
    • 2.2 获取类的私有实例方法并调用它
    • 2.3 获取类的静态的私有方法并调用它
    • 2.4 获取类的私有实例字段并修改它
    • 2.5 获取类的私有静态字段并修改它
  • 3 对泛型类的反射(模拟系统源码)

下面介绍基本反射技术。

反射包括以下技术:

  1. 根据一个字符串得到一个类的对象。
  2. 获取一个类的所有公用或私有、静态或实例的字段、方法、属性。
  3. 对泛型类的反射

1 根据一个字符串得到一个类

通过 Class 的 getName方法查看结果:

1.1 getClass

通过一个对象,获取它的类型。类型用Class表示:

//通过getClass,每个Class都有这个函数
String str = "abc";
Class c1 = str.getClass();

打印结果:
在这里插入图片描述


1.2 Class.forName

        //这种方式最常见,通过静态方法Class.forName()
        Class c2 = null;
        Class c3 = null;
        Class c5 = null;
        //这种方式最常见,通过静态方法Class.forName()
        try {
            c2 = Class.forName("java.lang.String");
            c3 = Class.forName("android.widget.Button");
            //通过getSuperClass,每个Class都有这个函数
            c5 = c3.getSuperclass();  //得到TextView
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

打印结果:
在这里插入图片描述


1.3 class属性

每个类都有class属性,可以得到这个类的类型:

 		//通过 .class属性
        Class c6 = String.class;
        Class c7 = java.lang.String.class;
        Class c8 = MainActivity.InnerClass.class;
        Class c9 = int.class;
        Class c10 = int[].class;

打印结果:
在这里插入图片描述


1.4 TYPE属性

基本类型都有TYPE属性,可以得到这个基本类型的类型

   		//基本类型包装类的TYPE语法
        Class c11 = Boolean.TYPE;
        Class c12 = Byte.TYPE;
        Class c13 = Character.TYPE;
        Class c14 = Short.TYPE;
        Class c15 = Integer.TYPE;
        Class c16 = Long.TYPE;
        Class c17 = Float.TYPE;
        Class c18 = Double.TYPE;
        Class c19 = Void.TYPE;

打印结果:
在这里插入图片描述


2 获取类的成员

2.1 类的构造函数

获取类的构造函数,包括privatepublic两种,也支持无参数有参数两种类型的构造函数。

获取类的构造函数由以下两个方法完成:

  • getDeclaredConstructors:获取所有构造函数
  • getConstructors:获取公共构造函数

新建TestClassCtor 添加一些构造函数,如下:

public class TestClassCtor {
    private int id;
    private String name;
    private static String address = "abc";
    public TestClassCtor() {
        id = 0;
        name = "hongxue";
    }
    public TestClassCtor(int a) {
        id = a;
    }
    public TestClassCtor(int a, String b) {
        id = a;
        name = b;
    }
    private TestClassCtor(int a, double c) {

    }
    private String doSomething(String d) {
        Log.v("hongxue", "TestClassCtor, doSOmething");
        return "abcd";
    }
    private static void work() {
        Log.v("hongxue", "TestClassCtor, work");
    }
    public String getName() {
        return name;
    }
    public static void printAddress() {
        Log.v("hongxue", address);
    }
    public int getId() {
        return id;
    }
}

使用printCtor方法打印获取到的构造函数的 修饰域、方法名称、方法的参数,如下:

 void printCtor(Constructor theConstructor) {
        int mod = theConstructor.getModifiers();// 输出修饰域和方法名称
        Log.v("hongxue", Modifier.toString(mod)  + "(");
        Class[] parameterTypes = theConstructor.getParameterTypes(); // 获取指定构造方法的参数的集合
        for (int j = 0; j < parameterTypes.length; j++) {    // 输出打印参数列表
            Log.v("hongxue", parameterTypes[j].getName());
            if (parameterTypes.length > j + 1) {
                Log.v("hongxue", ", ");
            }
        }
        Log.v("hongxue", ")");
    }

使用printCtors打印多个构造方法,代码如下:

public  static void printCtors(Constructor[] theConstructors,String className){
        try {
            for (int i = 0; i < theConstructors.length; i++) {
                int mod = theConstructors[i].getModifiers();// 输出修饰域和方法名称
                Log.v("hongxue", Modifier.toString(mod) + " " + className + "(");
                Class[] parameterTypes = theConstructors[i].getParameterTypes();// 获取指定构造方法的参数的集合
                for (int j = 0; j < parameterTypes.length; j++) {    // 输出打印参数列表
                    Log.v("hongxue", parameterTypes[j].getName());
                    if (parameterTypes.length > j + 1) {
                        Log.v("hongxue", ", ");
                    }
                }
                Log.v("hongxue", ")");
            }
        }catch (Exception e) {
            e.printStackTrace();
        }
    }

2.1.1 获取类的所有构造函数。

使用Class的getDeclaredConstructors方法获取类的所有ctor,不分public还是private:

    	TestClassCtor r = new TestClassCtor();
        Class temp = r.getClass();
        String className = temp.getName(); // 获取指定类的类名
        Log.v("hongxue", "获取类的所有ctor,不分public还是private----------------------------------------------");
         Constructor[] theCtorsAll = temp.getDeclaredConstructors(); // 获取指定类的所有构造方法
        Util.printCtors(theCtorsAll,className);

打印结果如下:
在这里插入图片描述
从结果中可以看出:getDeclaredConstructors确实可以获取所有构造方法。


2.1.2 获取类的公共构造函数。

 Log.v("hongxue", "获取类的public ctor----------------------------------------------");
 Constructor[] theCtorsPublic = temp.getConstructors();//获取指定类的公共构造方法    
 Util.printCtors(theCtorsPublic,className);

打印结果:
在这里插入图片描述

2.1.3 获取类的某个构造函数

			TestClassCtor r = new TestClassCtor();
        	Class temp = r.getClass();
        	
			//获取类的ctor:TestClassCtor()
            Constructor c1 = temp.getDeclaredConstructor();
            Util.printCtor(c1);

            //获取类的ctor:TestClassCtor(int a)
            Class[] p2 = {int.class};
            Constructor c2 = temp.getDeclaredConstructor(p2);
            Util.printCtor(c2);

            //获取类的ctor:TestClassCtor(int a, String b)
            Class[] p3 = {int.class, String.class};
            Constructor c3 = temp.getDeclaredConstructor(p3);
            Util.printCtor(c3);

            //获取类的ctor:TestClassCtor(int a, double c)
            Class[] p4 = {int.class, double.class};
            Constructor c4 = temp.getDeclaredConstructor(p4);
            Util.printCtor(c4);

查看打印结果:
在这里插入图片描述

反射到类的构造函数很重要:通过字符串反射出一个类,然后通过反射获取到类的构造函数,执行构造函数就得到了类的实例。有了实例就可以通过反射进一步得到实例的所有字段和方法。

2.1.4 调用构造函数

通过反射调用构造函数,得到类的实例,这里借助于Constructor的newInstance方法:

	    //通过反射,获取一个类的ctor,然后调用它
        try {
            Class r = Class.forName("com.hongx.reflection.TestClassCtor");
            //含参
            Class[] p3 = {int.class, String.class};
            Constructor ctor = r.getDeclaredConstructor(p3);
            Object obj = ctor.newInstance(1, "fhx");//修改了id和name的值
            TestClassCtor t = (TestClassCtor)obj;
            Log.w(TAG,"id = " + t.getId() +  " name = "+ t.getName());

            //无参
            Constructor ctor2 = r.getDeclaredConstructor();
            Object obj2 = ctor2.newInstance();
            TestClassCtor t2 = (TestClassCtor)obj2;
            Log.w(TAG,"id = " + t2.getId() +  " name = "+ t2.getName());//得到的id和name都是默认值

            //也可以使用Class的newInstance方法,但Class仅提供默认无参的实例化方法
            Object obj4 = r.newInstance();
            TestClassCtor t4 = (TestClassCtor)obj4;
            Log.w(TAG,"id = " + t4.getId() +  " name = "+ t4.getName());
        } catch (Exception e) {
            e.printStackTrace();
        }

打印结果:
在这里插入图片描述


2.2 获取类的私有实例方法并调用它

在TestClassCtor中,有一个私有方法doSomething:

    private String doSomething(String d) {
        Log.v("hongxue", "TestClassCtor, doSomething");
        return d;
    }
			//以下4句话,创建一个对象
            Class r = Class.forName("com.hongx.reflection.TestClassCtor");

            Constructor ctor2 = r.getDeclaredConstructor();
            Object obj = ctor2.newInstance();
            
            //以下4句话,调用一个private方法
            Class[] p4 = {String.class};
            Method method = r.getDeclaredMethod("doSomething", p4); //在指定类中获取指定的方法
            method.setAccessible(true);

            Object argList[] = {"hongxue123"}; //这里写死,下面有个通用的函数getMethodParamObject
            Object result = method.invoke(obj, argList);

            Log.v(TAG,result.toString());

打印:
在这里插入图片描述

2.3 获取类的静态的私有方法并调用它

在TestClassCtor中,有一个静态的私有方法work:

/**
 * 在TestClassCtor中有一个私有的方法work
 */
  private static void work() {
        Log.v("hongxue", "TestClassCtor, work");
  }
    		//以下4句话,创建一个对象
            Class r = Class.forName("com.hongx.reflection.TestClassCtor");

            //以下3句话,调用一个private静态方法
            Method method = r.getDeclaredMethod("work"); //在指定类中获取指定的方法
            method.setAccessible(true);
            method.invoke(null);

打印结果:

V/hongxue: TestClassCtor, work

2.4 获取类的私有实例字段并修改它

在TestClassCtor中,有一个私有的实例字段name:

public class TestClassCtor {
    private String name;
    public String getName() {
        return name;
    }
 }
			//以下4句话,创建一个对象
            Class r = Class.forName("com.hongx.reflection.TestClassCtor");
            Class[] p3 = {int.class, String.class};
            Constructor ctor = r.getDeclaredConstructor(p3);
            Object obj = ctor.newInstance(10, "fhx");

            //获取name字段,private
            Field field = r.getDeclaredField("name");
            field.setAccessible(true);

            Object fieldObject = field.get(obj);
            Log.w(TAG,"name = " + fieldObject.toString() );

            //只对obj有效
            field.set(obj, "hongxue1988");//修改
            Log.w(TAG,"name = " + field.get(obj).toString() );

            //这次修改仅对当前这个对象有效,如果接下来我们再次创建一个TestClassCtor 对象,它的name字段的值为空
            TestClassCtor testClassCtor = new TestClassCtor(100);
            Log.w(TAG,"name = " + testClassCtor.getName() + " ; id = " + testClassCtor.getId()); //仍然返回null,并没有修改

打印结果:
在这里插入图片描述

2.5 获取类的私有静态字段并修改它

在TestClassCtor中,有一个静态的私有字段address:

 			//以下4句话,创建一个对象
            Class r = Class.forName("com.hongx.reflection.TestClassCtor");

            //获取address静态字段,private
            Field field = r.getDeclaredField("address");
            field.setAccessible(true);

            Object fieldObject = field.get(null);
            field.set(fieldObject, "ABCD");

            //静态变量,一次修改,终生受用
            TestClassCtor.printAddress();

打印结果:
在这里插入图片描述

与前面介绍的实例字段不同,静态字段的值被修改了,下次再使用,这个字段的值是修改后的值。所谓“一次修改,终生受用”。


3 对泛型类的反射(模拟系统源码)

Android系统源码中存在大量泛型,所以插件化技术离不开对泛型进行反射,比如单例模式(Singleton)。

以下代码是从Android源码中找出来的:

public abstract class Singleton<T> {
	private T mInstance;
	protected abstract T create();
	public final T get() {
		synchronized (this) {
			if(mInstance == null) {
				mInstance = create();
			}
			return mInstance;
		}
	}
}

Singleton 是一个泛型类,我们可以通过以下代码,取出Singleton中的mInstance字段 :

Class<?> singleton = Class.forName("com.hongx.reflection.Singleton");
Field mInstanceField = singleton.getDeclaredField("mInstance");
mInstanceField.setAccessible(true);

同时, Singleton也是一个抽象类,在实例化Singleton的时候,一定要实现 create 这个抽象方法 。

public interface ClassB2Interface {
    void doSomething();
}

public class AMN {

    private static final Singleton<ClassB2Interface> gDefault = new Singleton<ClassB2Interface>() {
        protected ClassB2Interface create() {
            ClassB2 b2 = new ClassB2();
            b2.id = 2;
            return b2;
        }
    };

    static public ClassB2Interface getDefault() {
        return gDefault.get();
    }
}

class ClassB2 implements ClassB2Interface{
    public int id;

    public void doSomething() {
        Log.v("hongxue", "ClassB2 doSomething");
    }
}
  ActivityManager am;
        try {
            // gDefault是一个 android.util.Singleton对象; 我们取出这个单例里面的字段
            Class<?> singleton = Class.forName("com.hongx.reflection.Singleton");
            Field mInstanceField = singleton.getDeclaredField("mInstance");
            mInstanceField.setAccessible(true);

            //获取AMN的gDefault单例gDefault,gDefault是静态的
            Class<?> activityManagerNativeClass = Class.forName("com.hongx.reflection.AMN");
            Field gDefaultField = activityManagerNativeClass.getDeclaredField("gDefault");
            gDefaultField.setAccessible(true);
            Object gDefault = gDefaultField.get(null);

            // AMN的gDefault对象里面原始的 B2对象
            Object rawB2Object = mInstanceField.get(gDefault);

            // 创建一个这个对象的代理对象ClassB2Mock, 然后替换这个字段, 让我们的代理对象帮忙干活
            Class<?> classB2Interface = Class.forName("com.hongx.reflection.ClassB2Interface");
            Object proxy = Proxy.newProxyInstance(
                    Thread.currentThread().getContextClassLoader(),
                    new Class<?>[] { classB2Interface },
                    new ClassB2Mock(rawB2Object));
            mInstanceField.set(gDefault, proxy);

        } catch (Exception e) {
            e.printStackTrace();
        }


http://www.niftyadmin.cn/n/1838859.html

相关文章

Java论坛系统 JForum

JForum 是采用Java开发的功能强大且稳定的论坛系统。它提供了抽象的接口、高效的论坛引擎以及易于使用的管理界面&#xff0c;同时具有完全的权限控制、多语言支持&#xff08;包括中文&#xff09;、高性能、可自定义的用户接口、安全、支持多数据库等等特性。 JForum 采用 Fr…

昨天完成了从平文件转换为XML文件的通用程序

方法参考了XSLT技术,与我以前完成了XML文件转其它文件的程序写法,通过一个中间文件(XML)来定义平文件的各种字段及长度,并且这个中间文件的结构与目标XML文件类似.期间遇到了一个"截取指定长度的中文字符串"的问题,后来虽然不知就里,但总算解决了."截取指定长度…

云上印尼:“数”影婆娑的千岛之国

编辑 | 阿冒 设计 | 沐由有些朋友可能不知道&#xff0c;隔海相望的印度尼西亚与中国之间&#xff0c;早在2000年前就有中国商船蹈海连接&#xff1b;或许也不是特别清楚&#xff0c;在明朝期间著名航海家郑和七下南洋的伟大航路里&#xff0c;其中多达六次到达过印尼群岛。…

[Linux.Kde] CentOS界面上找不到音量调节

笔记本上安装了CentOS 5, KDE下在任务栏和菜单中都没有找到调节音量的地方。解决&#xff1a;在命令行中安装&#xff0c;并且执行kmix.转载于:https://www.cnblogs.com/livepencil/archive/2010/02/11/1667546.html

3款好用的多媒体播放器

来自韩国的影音全能播放器&#xff0c;与Mplayer一样从linux平台移植而来的Kmplayer(简称KMP)几乎可以播放您系统上所有的影音文件。通过各种插件扩展KMP可以支持层出不穷的新格式。强大的插件功能,直接从Winamp继承的插件功能&#xff0c;能够直接使用winamp的音频 &#xff0…

类库发布成DLL文件之后,怎么在调用时显示注释。

今天把DBUtility类封装成了DLL文件&#xff0c;建了个测试项目使了一下&#xff0c;绿灯&#xff0c;可是有点让偶郁闷了&#xff0c;DBUtility里面是写了很多注释的&#xff0c;可是在调用封装之后DLL文件之后却看不到任何注释。 解决方案&#xff1a; 在类库中生成DLL文件的时…

RookeyFrame 线上 添加Model

线上添加好了模块&#xff0c;会在本地生成几个文件 类文件&#xff1a;Rookey.Frame.Web\Config\TempModel\Order_File.code DLL文件&#xff1a;Rookey.Frame.Web\bin\TempModel\Order_File.dll转载于:https://www.cnblogs.com/guxingy/p/10912686.html

宏杉科技助力西安交大二附院:突破存储旧瓶颈、实现业务新成长

编辑 | 阿冒 设计 | 沐由“时间短&#xff0c;任务重。”同样的短短六个字&#xff0c;从西安交通大学第二附属医院&#xff08;以下简称&#xff1a;西安交大二附院&#xff09;信息网络部主任陈鹏岗口中说出来&#xff0c;分量自然与众不同。作为国内领先、西北顶尖的国家…