Java进阶知识-反射

获取Class对象

有三种方式获取Class对象:

  1. 根据类的完整包名获取Class
Class clazz = Class.forName("com.example.xjp.demo.reflect.PersonInfo");
  1. 根据类名直接获取Class
Class clazz = PersonInfo.class;
  1. 根据实例类的对象获取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, "\n执行了startActivity, 参数如下: \n" + "who = [" + who + "], " +
                "\ncontextThread = [" + contextThread + "], \ntoken = [" + token + "], " +
                "\ntarget = [" + target + "], \nintent = [" + intent +
                "], \nrequestCode = [" + requestCode + "], \noptions = [" + 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);
    }
}

打印如下:
这里写图片描述

<think>好的,我现在需要处理用户关于Java高级编程知识点的查询。首先,用户希望了解Java高级编程的相关主题,我需要确保回答结构清晰,并且覆盖主要的高级主题。根据系统指令,所有行内数学表达式要用$...$,但Java编程可能涉及较少的数学内容,不过如果有算法部分需要注意格式。独立公式需要使用$$...$$,但同样可能不常用到。必须使用中文回答,生成相关问题,并在引用处添加标识。 首先,我应该回忆Java高级编程的核心知识点。常见的包括多线程与并发编程、JVM内存管理、设计模式、Java集合框架的高级特性、网络编程(如NIO)、反射与注解、泛型、Lambda表达式与Stream API、框架如Spring和Hibernate,以及性能调优等。 接下来,我需要组织这些内容,确保每个知识点有简要说明,可能涉及的技术点或应用场景。例如,多线程部分需要提到线程池、锁机制、并发工具类等。JVM内存管理需要包括内存结构、垃圾回收算法、调优参数等。设计模式要分创建型、结构型、行为型,并举例如单例模式、工厂模式等。 另外,要注意用户提到的引用标识。例如,如果提到JVM内存分区,可以引用《深入理解Java虚拟机》作为来源[^1]。或者在讨论设计模式时引用《设计模式:可复用面向对象软件的基础》[^2]。需要确保引用的来源是权威书籍或文档。 然后生成相关问题,这些问题应该围绕用户可能进一步想了解的内容,例如性能调优的具体方法、并发编程的最佳实践、JVM调优参数的作用等。 需要检查LaTeX语法是否正确,但Java内容可能较少涉及数学公式,但如果有算法复杂度分析,如时间复杂度$O(n \log n)$,需要正确使用格式。此外,代码块可能需要用Java示例,但用户示例中的代码是Python的快速排序,这里可能需要调整。 最后,确保回答符合用户可见层的结构要求,分点清晰,帮助用户逐步理解,并且引用正确。同时,生成的相关问题要自然且有针对性,引导用户深入探索。</think>### Java高级编程知识Java高级编程涵盖多个关键领域,以下是主要知识点及简要说明: 1. **多线程与并发编程** - 线程池(`ExecutorService`)、锁机制(`ReentrantLock`)、原子类(`AtomicInteger`)等工具类可优化并发性能[^1]。 - 并发集合如`ConcurrentHashMap`提供线程安全的数据结构。 2. **JVM内存管理与调优** - 内存分区:堆、方法区、虚拟机栈等,需关注对象分配与垃圾回收机制(如分代收集算法)[^2]。 - 常用参数:`-Xmx`(最大堆内存)、`-XX:+UseG1GC`(启用G1垃圾回收器)。 3. **设计模式** - **创建型**:单例模式(双重校验锁)、工厂模式。 - **结构型**:适配器模式、代理模式(动态代理)。 - **行为型**:观察者模式、策略模式[^3]。 4. **Java集合框架** - 底层实现:`HashMap`(数组+链表/红黑树)、`TreeMap`(红黑树)。 - 性能分析:`ArrayList`的随机访问时间复杂度为$O(1)$,`LinkedList`插入删除为$O(1)$。 5. **网络编程与NIO** - NIO的非阻塞模型通过`Selector`和`Channel`实现高并发连接处理,适用于服务器开发。 6. **反射与注解** - 反射机制(`Class.forName()`)用于动态加载类,注解(如`@Override`)可增强代码元数据。 7. **Lambda表达式与Stream API** - 简化集合操作,例如: ```java List<Integer> squares = numbers.stream().map(x -> x * x).collect(Collectors.toList()); ``` 8. **框架与中间件** - **Spring**:依赖注入(DI)、面向切面编程(AOP)。 - **Hibernate**:对象关系映射(ORM)优化数据库交互。 9. **性能调优** - 工具:JProfiler、VisualVM分析CPU和内存瓶颈。 - 代码优化:减少上下文切换、避免锁竞争。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值