news 2026/10/18 15:29:37

Java进阶知识-反射

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java进阶知识-反射

获取Class对象

有三种方式获取Class对象:

  1. 根据类的完整包名获取Class

    Class clazz = Class.forName(“com.example.xjp.demo.reflect.PersonInfo”);

  2. 根据类名直接获取Class

    Class clazz = PersonInfo.class;

  3. 根据实例类的对象获取Class

    PersonInfo personInfo = new PersonInfo();
    Class clazz = personInfo.getClass();

创建实例

通过反射来生成对象主要有两种方式
1. 使用Class对象的newInstance()方法来创建Class对象对应类的实例

Class clazz = PersonInfo.class; PersonInfo personInfo = (PersonInfo) clazz.newInstance();

2.使用Class对象的构造器来创建实例

Constructor constructor = clazz.getConstructor(PersonInfo.class); //有构造器来创建实例,可以传参数给newInstance(Object ... initargs) PersonInfo personInfo = (PersonInfo) constructor.newInstance();

获取方法

通过反射,可以获取某个类中的所有方法,包括private,public,protect类型的方法
1. 获取类的所有申明的方法,包括public,private,protect类型的方法

Class clazz = PersonInfo.class; Method[] declaredMethods = clazz.getDeclaredMethods();

2.获取类中所有public方法

Class clazz = PersonInfo.class; Method[] methods = clazz.getMethods();

3.获取类中指定的public方法

Class clazz = PersonInfo.class; PersonInfo personInfo = (PersonInfo) clazz.newInstance(); //第一个参数是方法名,第二个参数是该方法参数的类型 Method method = clazz.getMethod("getName", String.class); //调用 PersonInfo 类中的 getName()方法 String name = (String) method.invoke(personInfo , "是最帅的");

4.获取指定的private方法

Class clazz = PersonInfo.class; PersonInfo personInfo = (PersonInfo) clazz.newInstance(); //第一个参数是方法名,第二个参数是该方法参数的类型 Method method = clazz.getDeclaredMethod("getAge", Integer.class); //由于该方法是private的,所以需要设置访问权限 method.setAccessible(true); //调用PersonInfo 类中的 getAge()方法 int age = (int) method.invoke(personInfo, 18);

获取类的成员变量

  1. 获取类中所有成员变量,包括public,private,protect类型

    Field[] declaredFields = clazz.getDeclaredFields();

2.获取类中所有public类型的成员变量

Field[] fields = clazz.getFields();

3.获取指定的成员变量,public类型

Field nameField = clazz.getField("mName"); //修改成员变量mName的值为Tom nameField.set(personInfo, "Tom"); //得到成员变量nName的值 String name = nameField.get(personInfo);

4.获取指定的成员变量,private类型

//得到私有的成员变量 mAge Field ageField = clazz.getDeclaredField("mAge"); //设置其访问权限 ageField.setAccessible(true); //修改成员变量 mAge 的值 ageField.set(test, 23); //获取该成员变量的值 int age = (int) ageField.get(test); public class PersonInfo { private int mAge = 18; public String mName = "xjp"; private int getAge(int age) { return mAge; } public String getName(String msg) { return mName + ":" + msg; } }

反射的应用场景

我们来做一个有意思的功能,在你的应用所有启动Activity之前的地方加一个打印,打印出一些相关信息。你可能会想到如下策略:
应用中所有的Activity都继承自一个BaseActivity基类,基类中实现一个startActivity方法,在该方法之前加上一句打印,那么所有startActivity的地方都调用基类中的方法。

但是有这么一种情况,项目已经进行了80%,大部分Activity已经写好了,好多Activity都不是继承自同一个BaseActivity基类,如果需要改的话,改动地方太多,改动大,不划算。那么有没有一种更好的办法,修改少,又能实现该功能的方法呢?

答案就是利用反射,在系统调用startActivity的地方换成我们自己的startActivity方法,这一招叫做偷梁换柱。那么怎么实现呢?

我们先找到Android系统的startActivity方法是怎么实现的,该方法是Context类的方法,而Context只是一个借口,其实现类是ContextImpl类,代码如下:

@Override public void startActivity(Intent intent, Bundle options) { warnIfCallingFromSystemProcess(); // Calling start activity from outside an activity without FLAG_ACTIVITY_NEW_TASK is // generally not allowed, except if the caller specifies the task id the activity should // be launched in. if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0 && options != null && ActivityOptions.fromBundle(options).getLaunchTaskId() == -1) { throw new AndroidRuntimeException( "Calling startActivity() from outside of an Activity " + " context requires the FLAG_ACTIVITY_NEW_TASK flag." + " Is this really what you want?"); } mMainThread.getInstrumentation().execStartActivity( getOuterContext(), mMainThread.getApplicationThread(), null, (Activity) null, intent, -1, options); }

最终启动activity是有mMainThread对象的getInstrumentation()方法获取Instrumentation对象,然后由该对象调用execStartActivity()方法来启动activity。而mMainThread对象是ActivityThread类型,该类是我们的主线程类,里面有有一个mInstrumentation成员变量,该成员变量属于Instrumentation类型。

我们的思路是替换ActivityThread类总的mInstrumentation对象,使用我们自己的 Instrumentation对象。实现如下:

public class ProxyInstrumentation extends Instrumentation { private static final String TAG = "ProxyInstrumentation"; // ActivityThread中原始的对象, 保存起来 Instrumentation mBase; public ProxyInstrumentation(Instrumentation base) { mBase = base; } public ActivityResult execStartActivity( Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) { // Hook之前, XXX到此一游! Log.d(TAG, " 执行了startActivity, 参数如下: " + "who = [" + who + "], " + " contextThread = [" + contextThread + "], token = [" + token + "], " + " target = [" + target + "], intent = [" + intent + "], requestCode = [" + requestCode + "], options = [" + options + "]"); // 开始调用原始的方法, 调不调用随你,但是不调用的话, 所有的startActivity都失效了. // 由于这个方法是隐藏的,因此需要使用反射调用;首先找到这个方法 try { Method execStartActivity = Instrumentation.class.getDeclaredMethod( "execStartActivity", Context.class, IBinder.class, IBinder.class, Activity.class, Intent.class, int.class, Bundle.class); execStartActivity.setAccessible(true); return (Instrumentation.ActivityResult) execStartActivity.invoke(mBase, who, contextThread, token, target, intent, requestCode, options); } catch (Exception e) { // 某该死的rom修改了 需要手动适配 throw new RuntimeException("do not support!!! pls adapt it"); } }

然后通过反射拿到ActivityThread类中的mInstrumentation,代码如下:

public static void attachContext() throws Exception{ // 先获取到当前的ActivityThread对象 Class<?> activityThreadClass = Class.forName("android.app.ActivityThread"); Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread"); currentActivityThreadMethod.setAccessible(true); Object currentActivityThread = currentActivityThreadMethod.invoke(null); // 拿到原始的 mInstrumentation字段 Field mInstrumentationField = activityThreadClass.getDeclaredField("mInstrumentation"); mInstrumentationField.setAccessible(true); Instrumentation mInstrumentation = (Instrumentation) mInstrumentationField.get(currentActivityThread); // 创建代理对象 Instrumentation evilInstrumentation = new ProxyInstrumentation(mInstrumentation); // 偷梁换柱 mInstrumentationField.set(currentActivityThread, evilInstrumentation); }

然后在你应用的Application类中调用如上方法即可:

public class DemoApplication extends Application { private static final String TAG = "DemoApplication"; private static Context mContext; @Override public void onCreate() { super.onCreate(); mContext = DemoApplication.this.getApplicationContext(); String processName = mContext.getApplicationInfo().processName; Log.i(TAG, "onCreate: the processName=" + processName); } public static Context getContext(){ return mContext; } @Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); try { attachContext(); } catch (Exception e) { e.printStackTrace(); } } public static void attachContext() throws Exception{ // 先获取到当前的ActivityThread对象 Class<?> activityThreadClass = Class.forName("android.app.ActivityThread"); Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread"); currentActivityThreadMethod.setAccessible(true); Object currentActivityThread = currentActivityThreadMethod.invoke(null); // 拿到原始的 mInstrumentation字段 Field mInstrumentationField = activityThreadClass.getDeclaredField("mInstrumentation"); mInstrumentationField.setAccessible(true); Instrumentation mInstrumentation = (Instrumentation) mInstrumentationField.get(currentActivityThread); // 创建代理对象 Instrumentation evilInstrumentation = new ProxyInstrumentation(mInstrumentation); // 偷梁换柱 mInstrumentationField.set(currentActivityThread, evilInstrumentation); } }

打印如下:

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/17 7:58:45

IP地址、子网掩码(NETMASK)和网关(Gateway)

IP: 192.168.123.1NETMASK: 255.255.255.0Gateway: 192.168.123.254 IP地址、子网掩码&#xff08;NETMASK&#xff09;和网关&#xff08;Gateway&#xff09;是计算机网络中用于定位和通信的关键元素。针对给出的IP地址192.168.123.1、子网掩码255.255.255.0和网关192.168.12…

作者头像 李华
网站建设 2026/10/17 7:58:45

Java自学路线图之Java进阶自学

Java进阶自学–面向对象进阶面向对象特性之继承继承的好处和弊端super关键字的作用继承时子父类的访问特点super的内存分析方法的重写(Override)package关键字的作用import关键字的作用final关键字的作用final修饰局部变量的注意事项static关键字的作用面向对象特性之多态多态的…

作者头像 李华
网站建设 2026/10/17 7:23:42

2026最新12 种 RAG(检索增强生成)的新型高级架构与方法

RAG&#xff08;检索增强生成&#xff09; 曾是极其热门的话题之一。而本周非常幸运地看到了一些关于 RAG 的真正令人兴奋的新研究 让我们一起来看看近期出现的 12 种 RAG 高级架构与方法&#xff1a; 1. Mindscape-Aware RAG (MiA-RAG) 全局感知 RAG MiA-RAG 通过首先构建整个…

作者头像 李华
网站建设 2026/10/16 19:01:18

救命神器!9个AI论文网站测评:本科生毕业论文全攻略

救命神器&#xff01;9个AI论文网站测评&#xff1a;本科生毕业论文全攻略 学术写作新选择&#xff1a;AI论文网站测评全解析 在当前高校教育日益重视科研能力的背景下&#xff0c;本科生在撰写毕业论文时面临诸多挑战&#xff0c;如选题困难、资料查找繁琐、格式规范不熟悉等。…

作者头像 李华
网站建设 2026/10/14 17:45:09

通信协议仿真:蓝牙协议仿真_(6).蓝牙网络拓扑

蓝牙网络拓扑 1. 蓝牙网络基础 蓝牙网络拓扑是指蓝牙设备在无线通信网络中的组织和连接方式。理解蓝牙网络的基本概念和拓扑结构对于进行蓝牙协议仿真至关重要。蓝牙网络可以分为两种主要类型&#xff1a;基础模式&#xff08;Basic Rate/Enhanced Data Rate, BR/EDR&#xff0…

作者头像 李华
网站建设 2026/10/13 13:14:55

通信协议仿真:蓝牙协议仿真_(9).蓝牙仿真案例分析

蓝牙仿真案例分析 在上一节中&#xff0c;我们已经介绍了蓝牙协议的基本概念和结构。本节将通过具体的案例来分析蓝牙协议的仿真过程&#xff0c;帮助读者更好地理解如何在实际开发中应用蓝牙仿真技术。我们将从以下几个方面进行详细的分析&#xff1a; 蓝牙设备连接仿真蓝牙数…

作者头像 李华