前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >担心被问到-反射,我来给你壮壮胆

担心被问到-反射,我来给你壮壮胆

作者头像
狼王编程
发布2021-06-01 16:14:02
2730
发布2021-06-01 16:14:02
举报
文章被收录于专栏:狼王编程之路狼王编程之路

大家好,我是狼王,一个爱打球的程序员

这篇让我们聊聊什么是反射,为啥好多程序员听到反射就莫名的害怕呢,本文给你壮壮胆,其实没多少东西的!


在Java中,想要运行时操作对象的方法、属性,其中有效的手段,就有反射。这也是框架最常使用的手段,因为框架在编译时并不知晓系统中有哪些类会被使用。

Java中对象的类型信息在运行时由Class对象表示,Class对象是伴随类加载而实例化产生的,而反射的实现就围绕着Class对象。

有很多种方式可以获取类的Class对象,包括:

代码语言:javascript
复制
Class<Object> c1 = Object.class;
Class<?> c2 = Class.forName("java.lang.Object");
Class<?> c3 = new Object().getClass();

反射可以做什么?

1. 实例化对象

对象的实例化,可以通过new关键字直接实例化,也可以通过反射,例如:

代码语言:javascript
复制
class.newInstance() 
class.getConstructor(Class<?>... parameterTypes).newInstance(Object ... initargs)

为什么需要用反射来进行对象实例化?

  1. 有不能使用new关键字直接实例化对象的场景,例如:Spring容器管理的Bean,只能通过类的全限定名加载类,然后反射实例化。
  2. 在不想使用new关键字的场景,目的是为了简化编程,使代码美观,可能大家经常看到类似的用法,例如:
代码语言:javascript
复制

public static <T> T parseObject(String text, Class<T> clazz) {
  return parseObject(text, clazz, new Feature[0]);
}
代码语言:javascript
复制
/**
* 简单的复制出新类型对象
*/
public static <S, D> D map(S source, Class<D> destinationClass){
  return mapper.map(source, destinationClass);
}

2. 筛选合适的类

实际开发中,经常会有这种需要,如果类有某某特征,就进行某某操作。在Spring进行扫描的时候,我们会通过过滤器,来精细化控制bean的生成,包括:

  1. 根据isInstance(Object obj)判断是否实现某某接口或者继承特殊的父类;
  2. 根据isAnnotationPresent(Class<? extends Annotation> annotationClass)判断是否被注解标记。

3. 方法调用

有些场景不能或者不合适直接调用方法,例如:我们处理HTTP请求,需要从URI映射到方法调用,如果我们能够穷举所有的URL到对象方法的映射关系,那么也没问题,但是无数的if条件判断,显然不是明智的选择。

通常我们先会获取资源对象,然后反射调用对象的方法。

代码语言:javascript
复制
Method.invoke(Object obj, Object... args)

如何获取对象的方法对象,也就是Method对象呢?Class类提供了以下实现:

代码语言:javascript
复制
Method[] getMethods();
Method[] getDeclaredMethods();
Method getMethod(String name, Class<?>... parameterTypes);
Method getDeclaredMethod(String name, Class<?>... parameterTypes);

这些方法可以分为两类:

  1. 方法签名中带有Declared的,会在当前类的所有方法中查找,但不会遍历父类。
  2. 不带Declared的会遍历所有父类,但只会查找public方法。

推荐使用工具类:org.apache.commons.lang3.reflect.MethodUtils 此类中包含遍历所有父类查找方法、当前类查找public方法或者反射执行方法的便捷操作。

4. 属性操作

代码语言:javascript
复制
Class.getFields(), Class.getField(String), 
Class.getDeclaredFields(), Class.getDeclaredField(String)

命名规则同方法,推荐使用工具类:org.apache.commons.lang3.FieldUtils,进行读取或者赋值操作。

反射进行赋值有几点需要注意:

  1. 如果不为public类型,那么设置字段前必须通过Field.setAccessible(true)方法进行访问权限设置,不然会抛出异常:IllegalAccessException 。
  2. 如果字段为static类型,那么通过set方法进行赋值时,会忽略obj对象,因为静态字段属于类。
  3. 如果字段为final类型,不管是public还是 private,那么用set方法赋值时,只有setAccessible进行访问权限设置后,才能正确调用,不然会报异常:IllegalAccessException。但是对final字段进行set方法赋值时,尽管方法正常调用,但是并不会改变fianl字段的值。
  4. 如果字段为final static 类型,那么进行set方法赋值时,总会抛出IllegalAccessException异常。

最后如果对当前对象的所有方法,或者所有字段进行某种操作,那么推荐工具类:org.springframework.util.ReflectionUtils

4、总结

所以反射其实没那么可怕,消灭恐惧的最好办法就是面对恐惧,加油学起来吧!

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-03-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 狼王编程 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 反射可以做什么?
    • 1. 实例化对象
      • 2. 筛选合适的类
        • 3. 方法调用
          • 4. 属性操作
            • 4、总结
            相关产品与服务
            容器服务
            腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档