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); } }

打印如下:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1125481.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Gemini认证疑难解答会

Gemini认证疑难解答会通常是指围绕Google推出的Gemini大模型系列在使用、集成或开发过程中遇到的技术问题进行讨论与解决的会议或交流活动。这类会议可能由开发者社区、企业技术团队或Google官方组织&#xff0c;旨在帮助用户更好地理解Gemini API的认证机制、权限配置、访问控…

Django 视图基础

Django 视图基础Django 视图是处理用户请求并返回响应的核心组件。视图可以是函数或类&#xff0c;通常定义在 views.py 文件中。函数视图示例&#xff1a;from django.http import HttpResponsedef hello_world(request):return HttpResponse("Hello, World!")类视图…

Java 进阶:异常影响性能吗?

Java 进阶异常影响性能吗 catch 中不做任何事情catch 中输出异常到日志catch 中获取异常栈总结 Java 进阶&#xff1a;异常影响性能吗&#xff1f; 曾经在给一个业务系统增加限流功能&#xff0c;使用的限流组件在流量超过阈值时&#xff0c;会直接抛异常&#xff0c;异常导…

【Qt改变虚拟键盘的大小】

默认情况下qtvirtualkeyboard占据了半个屏幕 可以通过修改源码的方式来修改其大小。 1.找到desktopinputpanel.cpp,参考路径 2.修改show函数注释的代码为源文件原来的代码。 3.键盘样式修改需要修改对应的qml文件&#xff0c;路径为qtvirtualkeyboard/src/virtualkeyboard/cont…

java进阶训练营 极客,关于架构极客大学java进阶训练营

C语言中&#xff0c; 数组[2]属于结构数据类型。一个数组能够合成为多个数组元素&#xff0c;这些数组元素能够是根本数据类型或是构造类型。因而按数组元素的类型不同&#xff0c;数组又可分为数值数组、字符数组、指针数组、构造数组等各种类别。 对于可变长数组(VLA)的问题&…

Java进阶整理

对于一个程序员不能只是停留在满足平常的业务开发的水平&#xff0c;所以今天来整理一下Java的进阶知识。 通过以下几个方面来讲一讲Java的进阶知识&#xff1a; Jvm Jvm结构类加载对象的分配过程 、对象存储布局Java的内存模型、GC&#xff08;MinorGC&#xff08;新生代&a…

java头歌-数组进阶

第一关 public static void main(String[] args) {//动态构建arr1int[] arr1 new int[3];Scanner sc new Scanner(System.in);for(int i 0 ; i< arr1.length ; i){arr1[i] sc.nextInt();}/********** Begin **********///创建数组arr2int[] arr2 new int[3];for (int i…

java进阶知识点

java回收机制 浅谈java中的反射 依赖注入的简单理解 通过接口的引用和构造方法的表达&#xff0c;将一些事情整好了反过来传给需要用到的地方~ 这样做得好处&#xff1a;做到了单一职责&#xff0c;并且提高了复用性&#xff0c;解耦了之后&#xff0c;任你如何实现&#xf…

破解空间困局:看紧凑型ARM工控机如何一机多能

在智能工厂的角落、自动化产线的缝隙、或是移动设备的内部&#xff0c;工程师们常常面临一个经典难题&#xff1a;空间极其有限&#xff0c;但需要连接和控制的设备却一点不少。 摄像头、传感器、PLC、扫码枪、显示屏、机械臂……每一个都需要一个“对话”的接口。传统的工控机…

Java基础进阶-水仙花数

/* 功能&#xff1a;求水仙花数&#xff0c;打印并统计总个数。 思路&#xff1a; 水仙花数是定义范围100-999&#xff0c;满足每个位上的数子的3次方相加和等于这个数 第一步&#xff1a;循环遍历数据范围 第二步&#xff1b;取出当前数字的个位&#xff0c;十位&#xff0c;百…

Java进阶教程(二)代码块

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

提升Python AI模型训练速度:从入门到进阶的实战优化方案

你在训练AI模型时&#xff0c;最头疼的莫过于“等了几小时甚至几天&#xff0c;模型还没训练完”——不管是图片分类、文本分析还是更复杂的深度学习模型&#xff0c;训练速度直接影响开发效率。 一、硬件层面&#xff1a;先把“基础算力”用到位&#xff08;性价比最高的提速方…

【论文精读】-Graph-Grounded Pre-training and Prompting

家人们&#xff0c;主播又回来啦&#xff0c;这一周主播比较忙&#xff0c;所以就只认认真真精读了这一篇论文。主播这也是第一次使用提问法&#xff0c;讲这篇文章里面究极的一些问题给弄明白了&#xff0c;这也是很好的一点。话不多说&#xff0c;我们来一起看看这篇文章吧&a…

12 种 RAG(检索增强生成)的新型高级架构与方法,建议收藏!

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

java学习进阶之路,如果从一个菜鸟进阶成大神

二、下面是java工作之路&#xff0c;以供参考&#xff1a; 三、下面给出阶段性细化需要掌握的技能&#xff1a;1.第一阶段2.第二阶段3.第三阶段4.第四阶段5.第五阶段四、更加细化的细节如下&#xff1a;1&#xff1a; 编程基础 不管是C还是C&#xff0c;不管是Java还是PHP&…

Java_ElasticSearch(ES)——分布式搜索引擎

介绍&#xff1a; Elasticsearch是一个开源的分布式搜索和分析引擎&#xff0c;最初由Elastic公司开发。它构建在Apache Lucene搜索引擎库之上&#xff0c;提供了一个强大的全文搜索和分析引擎&#xff0c;它结合kibana、Logstash、Beats&#xff0c;是一整套技术栈&#xff0…

手写一个单例模式 (考虑线程安全)。

手写一个单例模式 (考虑线程安全) 关键词:单例模式, 线程安全, 设计模式, 双重检查锁定, 并发编程, 懒汉式, 饿汉式 摘要:单例模式是软件开发中最基础也最常用的设计模式之一,它保证一个类在整个系统中只有一个实例,并提供全局访问点。然而在多线程环境下,简单的单例实现可…

一个在使用方法上的低级错误(MySQL场景)

首先申明这个技术含量不高初始化问题数据。注意这里的t代表时间&#xff0c;数据类型是字符串。为什么用字符串来表示时间&#xff1f;那这就是问题所在。当初创建的人不懂。这个回答不知道满意吗&#xff1f;mysql> create table t1 (id int,t varchar(20)); Query OK, 0 r…

深度学习毕设选题推荐:基于python的CNN训练识别吃的美食基于深度学习的CNN训练识别吃的美食

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

深度学习毕设项目推荐-深度学习基于python的CNN训练识别吃的美食基于python的深度学习CNN训练识别吃的美食

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