news 2026/3/10 4:30:36

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/3/9 10:16:04

Java进阶教程(二)代码块

Java进阶教程&#xff08;二&#xff09; 代码块 构造代码块&#xff1a;给所有的对象进行统一的初始化。对象一建立就运行并且优先于构造函数。 静态代码块&#xff1a;随着类的加载而加载。只执行一次&#xff0c;用于给类进行初始化。public class Demo {public static void…

作者头像 李华
网站建设 2026/3/10 2:06:53

【毕业设计】深度学习基于CNN卷积神经网络对猫狗数据集训练识别

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/3/8 0:08:36

从入门到精通:AI Agent六大核心模块深度解析

AI Agent通过六大核心模块构成智能闭环&#xff1a;感知交互模块负责接收解析需求&#xff1b;任务规划模块拆解任务序列&#xff1b;记忆管理模块存储短期记忆和长期知识&#xff1b;工具调用模块连接外部资源&#xff1b;执行反馈模块监控过程和校验结果&#xff1b;自主优化…

作者头像 李华
网站建设 2026/3/6 6:44:45

Flutter 列表 rebuild 的真正边界在哪里

子玥酱 &#xff08;掘金 / 知乎 / CSDN / 简书 同名&#xff09; 大家好&#xff0c;我是 子玥酱&#xff0c;一名长期深耕在一线的前端程序媛 &#x1f469;‍&#x1f4bb;。曾就职于多家知名互联网大厂&#xff0c;目前在某国企负责前端软件研发相关工作&#xff0c;主要聚…

作者头像 李华
网站建设 2026/3/3 23:06:20

AI创业已变天!Manus首席科学家带你重新理解Agent与产品思维

本文基于Manus首席科学家季逸超的访谈&#xff0c;探讨了AI创业与Agent设计的核心原则。AI时代更像传统制造业&#xff0c;创业者需具备经营思维而非艺术情怀。产品设计应遵循"有所不为"的理念&#xff0c;解决好"最后一公里"问题。Agent设计不应模仿人类分…

作者头像 李华