Java中的反射

这篇具有很好参考价值的文章主要介绍了Java中的反射。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

反射介绍

正常情况下,我们知晓我们要操作的类和对象是什么,可以直接操作这些对象中的变量和方法,比如一个User类:

User user=new User();
user.setName("Bob");

复制

但是有的场景,我们无法正常去操作:

  • 只知道类路径,无法直接实例化的对象。
  • 无法直接操作某个对象的变量和方法,比如私有方法,私有变量。
  • 需要hook系统逻辑,比如修改某个实例的参数。

等等情况。

所以我们就需要一种机制能让我们去操作任意的类和对象。

这种机制,就是反射。简单的说,反射就是:

对于任意一个,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性。

常用API举例

先设定一个User类:

package com.example.testapplication.reflection;
public class User {
    private int age;
    public String name;

    public User() {
        System.out.println("调用了User()");
    }

    private User(int age, String name) {
        this.name = name;
        this.age = age;
        System.out.println("调用了User(age,name)"+"__age:"+age+"__name:"+name);
    }

    public User(String name) {
        this.name = name;
        System.out.println("调用了User(name)"+"__name:"+name);
    }

    private String getName() {
        System.out.println("调用了getName()");
        return this.name;
    }

    private String setName(String name) {
     this.name = name;
        System.out.println("调用了setName(name)__"+name);
        return this.name;
    }

    public int getAge() {
        System.out.println("调用了getAge()");
        return this.age;
    }    
}

复制

获取Class对象

主要有三种方法获取Class对象

  • 根据类路径获取类对象
  • 直接获取
  • 实例对象的getclass方法
//1、根据类路径获取类对象
try {
    Class clz = Class.forName("com.example.testapplication.reflection.User");
} catch (ClassNotFoundException e) {
    e.printStackTrace();
}

//2、直接获取
Class clz = User.class;

//3、对象的getclass方法
Class clz = new User().getClass();

复制

获取类的构造方法

1、获取类所有构造方法

Class clz = User.class;
//获取所有构造函数(不包括私有构造方法)
Constructor[] constructors1 = clz.getConstructors();
//获取所有构造函数(包括私有构造方法)
Constructor[] constructors2 = clz.getDeclaredConstructors();

复制

2、获取类的单个构造方法

    try {
        //获取无参构造函数
        Constructor constructor1 = clz.getConstructor();

        //获取参数为String的构造函数
        Constructor constructor2 =clz.getConstructor(String.class);

        //获取参数为int,String的构造函数
        Class[] params = {int.class,String.class};
        Constructor constructor3 =clz.getDeclaredConstructor(params);
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    }

复制

需要注意的是,User(int age, String name)为私有构造方法,所以需要使用getDeclaredConstructor获取。

调用类的构造方法生成实例对象

1、调用Class对象的newInstance方法

这个方法只能调用无参构造函数,也就是Class对象的newInstance方法不能传入参数。

Object user = clz.newInstance();

复制

2、调用Constructor对象的newInstance方法

Class[] params = {int.class,String.class};
Constructor constructor3 =clz.getDeclaredConstructor(params);
constructor3.setAccessible(true);
constructor3.newInstance(22,"Bob");

复制

这里要注意下,虽然getDeclaredConstructor能获取私有构造方法,但是如果要调用这个私有方法,需要设置setAccessible(true)方法,否则会报错:

can not access a member of class com.example.testapplication.reflection.User with modifiers "private"

复制

获取类的属性(包括私有属性)

Class clz = User.class;
Field field1 = clz.getField("name");
Field field2 = clz.getDeclaredField("age");

复制

同样的,getField获取public类变量,getDeclaredField可以获取所有变量(包括私有变量属性)。

所以一般直接用getDeclaredField即可。

修改实例的属性

接上例,获取类的属性后,可以去修改类实例的对应属性,比如我们有个user的实例对象,我们来修改它的name和age。

//修改name,name为public属性
Class clz = User.class;
Field field1 = clz.getField("name");
field1.set(user,"xixi");

//修改age,age为private属性
Class clz = User.class;
Field field2 = clz.getDeclaredField("age");
field2.setAccessible(true);
field2.set(user,123);

复制

获取类的方法(包括私有方法)

    //获取getName方法
    Method method1 = clz.getDeclaredMethod("getName");
 //获取setName方法,带参数
    Method method2 = clz.getDeclaredMethod("setName", String.class);
    //获取getage方法
    Method method3 = clz.getMethod("getAge");

复制

调用实例的方法

method1.setAccessible(true);
Object name = method1.invoke(user);


method2.setAccessible(true);
method2.invoke(user, "xixi");

Object age = method3.invoke(user);

复制

反射优缺点

虽然反射很好用,增加了程序的灵活性,但是也有他的缺点:

  • 性能问题。由于用到动态类型(运行时才检查类型),所以反射的效率比较低。但是对程序的影响比较小,除非对性能要求比较高。所以需要在两者之间平衡。
  • 不够安全。由于可以执行一些私有的属性和方法,所以可能会带来安全问题。
  • 不易读写。当然这一点也有解决方案,比如jOOR库,但是不适用于Android定义为final的字段。

Android中的应用

插件化(Hook)

Hook 技术又叫做钩子函数,在系统没有调用该函数之前,钩子程序就先捕获该消息,钩子函数先得到控制权,这时钩子函数既可以加工处理(改变)该函数的执行行为,还可以强制结束消息的传递。

在插件化中,我们需要找到可以hook的点,然后进行一些插件的工作,比如替换Activity,替换mH等等。这其中就用到大量反射的知识,这里以替换mH为例:

// 获取到当前的ActivityThread对象
Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
Field currentActivityThreadField = activityThreadClass.getDeclaredField("sCurrentActivityThread");
currentActivityThreadField.setAccessible(true);
Object currentActivityThread = currentActivityThreadField.get(null);

//获取这个对象的mH
Field mHField = activityThreadClass.getDeclaredField("mH");
mHField.setAccessible(true);
Handler mH = (Handler) mHField.get(currentActivityThread);


//替换mh为我们自己的HandlerCallback
Field mCallBackField = Handler.class.getDeclaredField("mCallback");
mCallBackField.setAccessible(true);
mCallBackField.set(mH, new MyActivityThreadHandlerCallback(mH));

复制

动态代理

动态代理的特点是不需要提前创建代理对象,而是利用反射机制在运行时创建代理类,从而动态实现代理功能。

public class InvocationTest implements InvocationHandler {
    // 代理对象(代理接口)
    private Object subject;

    public InvocationTest(Object subject) {
        this.subject = subject;
    }
    @Override
    public Object invoke(Object object, Method method, Object[] args)
            throws Throwable {
        //代理真实对象之前
        Object obj = method.invoke(subject, args);
        //代理真实对象之后
        return obj;
    }
}

复制

三方库(注解)

我们可以发现很多库都会用到注解,而获取注解的过程也会有反射的过程,比如获取Activity中所有变量的注解:

public void getAnnotation(Activity activity){
    Class clazz = activity.getClass();
    //获得activity中的所有变量
    Field[] fields = clazz.getDeclaredFields();
    for (Field field : fields) {
        field.setAccessible(true);
        //获取变量上加的注解
        MyAnnotation test = field.getAnnotation(MyAnnotation.class);
        //...
    }
}

复制

这种通过反射处理注解的方式称作运行时注解,也就是程序运行状态的时候才会去处理注解。但是上文说过了,反射会在一定程度上影响到程序的性能,所以还有一种处理注解的方式:编译时注解。

所用到的注解处理工具是APT

APT是一种注解处理器,可以在编译时进行扫描和处理注解,然后生成java代码文件,这种方法对比反射就能比较小的影响到程序的运行性能。文章来源地址https://www.toymoban.com/news/detail-501179.html

到了这里,关于Java中的反射的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Java中的单元测试,反射和枚举

    2024年02月05日
    浏览(43)
  • Java中的反射(通过反射获取类的结构、invoke方法、获取注解)

    创建运行时类的对象是反射机制应用最多的地方。创建运行时类的对象有两种方式: 方式1:直接调用Class对象的newInstance()方法 要求: 1)类必须有一个无参数的构造器。 2)类的构造器的访问权限需要足够。 方式一的步骤 : 1)获取该类型的Class对象 2)调用Class对象的 new

    2024年02月04日
    浏览(36)
  • Java 中的反射是什么?如何使用它?

    在 Java 编程中,反射是一种高级的编程技术,可以在运行时动态地获取和操作类的信息。反射使得程序可以在运行时对类进行检查和操作,而不需要在编译时知道类的完整信息。这使得程序可以更加灵活和动态地处理对象,同时也为框架和库的开发提供了更大的自由度。 反射

    2024年02月16日
    浏览(30)
  • Java 中的反射机制(两万字超全详解)

    反射( Reflection ),Java 中的反射机制是指,Java 程序在运行期间可以获取到一个对象的全部信息。 反射机制 一般用来解决Java 程序运行期间,对某个实例对象一无所知的情况下,如何调用该对象内部的方法问题。 反射机制允许 Java 程序在运行时调用 Reflection API 取得任何类的

    2024年02月05日
    浏览(31)
  • Java反射机制,动态代理,hook以及在Retrofit源码中的应用

    1.反射的基础知识: Java的反射机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机

    2024年02月13日
    浏览(31)
  • antv/l7地图,鼠标滚动,页面正常滑动-- 我们忽略的deltaY

    背景 在官网项目中,需要使用一个地图,展示产品的分布区域及数量。希望的交互是,鼠标放上标点,tooltip展示地点和数量等信息。鼠标滚动,则页面随着滚动。但是鼠标事件是被地图代理了的,鼠标滚动意味着地图的缩放。 问题解决 我们首先想到的就是关闭地图的缩放

    2024年02月14日
    浏览(28)
  • 由于找不到msvcp140.dll文件,我们要怎么解决这种情况?

    在使用电脑的过程中,我们经常会遇到各种各样的问题,其中之一就是缺少msvcp140.dll文件。这个问题通常会导致某些软件无法正常运行,而且很多人对于如何解决这个问题并不是很清楚。本文将会介绍多种修复方法,并对比哪种方法比较方便。 一.什么是msvcp140.dll msvcp140.dll是

    2024年02月10日
    浏览(41)
  • Java Excel 打开文件报发现“xx.xlsx”中的部分内容有问题。是否让我们尽量尝试恢复问题解决

    发现“文件.xlsx”中的部分内容有问题。是否让我们尽量尝试恢复? 1、后端的导出接口写的不对,又返回流数据,又返回响应体数据,导致前端将流数据和响应体数据都下载到了excel文件中。  解决办法: 接口仅返回流数据即可。

    2024年02月13日
    浏览(43)
  • .NET 反射的介绍和简单应用

    什么是反射? 反射就是动态发现类型信息的能力。它帮助程序设计人员在程序运行时利用一些信息去动态地使用类型,这些信息在设计时是未知的,这种能力类似于后期绑定。反射还支持的更高级的行为,能在运行时动态创建新类型,并且对这些新类型的操作进行调用。 听

    2024年02月01日
    浏览(26)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包