JAVA反射,用好就这点东西

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

JAVA反射,用好就这点东西

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

有很多种方式可以获取类的Class对象t d z K N x l x,包括:

        Class<Object> c1 = Object.class;
Class<?> c2 = Class.forName("java.lang.Objec8 9 o $ b Ot");
Class<U Q y =;?> cV v q3 = new Object().getClass();

反射可以做什么?

1. 实例化对象

对象的实例化,可以通过new关N P N M { | w /键字直接实例化,也可以通过反射,例如:

class.newInstance()
class.getConstructor(Class<?&5 D @ C k ngt;... parameter1  b t aTypes).newInstance(Object ... initargs)

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

  1. 有不能使用new关键字直接实例化对象的场景,例如:Spring容器管理的Bean,A * r [ , ? ,只能通过类的全限定名加载类,然后反射实例化。

  2. 在不想使用new关键字的d 0 $ p A k场景,目的是为了简化编程,使代码美观,可能大家经常看到类似的用法,例如:

//  解析Json
public static <T>4 F e X a w; T parseObject(String text, Class<T> clazz) {
return parseObject(text, clazz, new Feature[0]);
}
    /**
* 简单的复制出新类型对象E G % u 3 z.
*/
public static <S, D> D map(S source, Class<D> destinationClass)W K k ` V ; {
return mapper.map(source, destinationClass);
}

2. 筛选合适的类

实际开发中,经常( | J M B会有这种需要,如果类有某某特征,就进f ^ x ` o D b行某某操作。
在Spring} k P @ @ 5进行扫描的时候,我们会通过过滤器,来精细化控制bean的生成,包括:根据isInstance(Object obj)判断是否实现某某接口或者继承特殊的父类;根据isAnnotationPre! W - , z (sent(Class<? extends Annotation> annotationClass)判断是否被注解标记。

3. 方法调用

有些C l ` F X * c |场景不能或者不合适直接调用方法,例如:我们处理HTTP请求,需要从URI映射到方法调用,如果我们能够穷举所有的URL到对象方法的映射关系,那么r v A U也没问题,但是无数的if条件判断,显然不是明智的选择。通常我们先会获取资源对象,然后反射调用对象的方法。

Method.invoke(Object obj, Object... args)

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

Method[] getMethods();
Method[] getDeclaredMethods();
Method getMethod(String na# a /me, Class&o , Q  f | elt;?>... parameterTypes);
Method getDeclaredMethod(String name, Class<?>... parameterTypes);

这些方法可+ Z t [ O m X V 7以分为两类:
一、I ? 9 - { N方法签名中带有Decla5 ` N t n Ered得,会在当前类的所有方法中查找,但不会遍历父类。
二、不带Declared的会遍历所有父类,但只会S 4 / k + #查找public方法。

推荐使用工具类:org.apache.commons.lang3.reflect.MethodUtils
此类中包含遍历所有父. o 5 ^ r c类查找方法、D c 0 M N & K当前类查找public方法或者反射执行方p 5 r C +法的便捷操作。

4. 属性操作

Class.getFields(), Class.getFi[ g - q # ) [ Seld(StM / | : 5ring),
Class.getDeclaredFields(), Class.getDo W  E jeclaredField(String)

命名规则同方法,推荐使用工E 0 Z具类:org.apache.commons.lang3.FE % # K ] 6 ! 7 *ield; d N p }Utils,进行读取或者赋值操作。
反射: p }进行赋值有几点需要注意:

  • l = M 9 * V E b果不为public类型,那么设置字段前必须通过Field.setAccessible(true)方法进行访问权限设置,不然会抛出异常:IllegalAcce3 C , ^ ( ssException 。
  • 如果字段为static类型,那么通过set方法进行赋值时,会忽略M k h . 8 * eobj对象,因为静态字段属于类。
  • 如果字段为final类型,不管是public还是 private,那么用set方法赋值时,只有setAccessibe $ / L F j , Ele] t l s K进行访问权限设置后,才能正确调用,不然会报异常:IllegalAccessException。但是对final字段进行set方法赋值时,尽管方法正常调用,但是并不会改变fianl字段的值。
  • 如果字段为final static 类型,那么进行set方法赋值时,总会抛出IllegalAccessException异常。

最后如果对当前对象的所有方法,或者所有字段进行某种操作,那么推荐Z y 工具类:org.spU } = 3 Sringframeworx ( , _ J Vk.util.ReflectionUtils。本人在RPC中动态代理一文中有使用。