• Java Reflect 反射



     

    反射简介

    反射:通过类的class对象来获取类的元信息,动态操作类中的字段、调用类中的方法。
     

    常见的应用场景

    • 开发通用框架时,从外部(配置文件)加载类的配置,动态创建对象、设置属性值,比如spring
    • 实现动态代理,动态创建代理对象,对原功能进行扩展、增强
    • 实现注解。
       

    优缺点

    • 优点:动态操作,可以根据传入的参数或从配置文件中读取的配置,动态创建对象、设置属性,十分灵活。
    • 缺点:①通过反射可以操作类的私有成员,破坏了类的封装性,存在安全隐患;②开销大,使用频率低时性能还行,频繁使用时严重影响性能。

     

    Class

    获取Class的3种方式
    //类名.class
    Class<User> userClass = User.class;
    
    //对象名.getClass()
    Class<? extends User> userClass = user.getClass();
    
    //Class.forName("全限定类名"),类名使用String指定,编译时并不知道Class对象的泛型,所以是? 需要强转
    Class<?> clazz = Class.forName("com.chy.mall.entity.User");
    Class<User> userClass = (Class<User>)clazz;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

     

    is 判断
    //是否是指定类型
    boolean isInterface = userClass.isInterface();
    boolean isAnnotation = userClass.isAnnotation();
    boolean isEnum = userClass.isEnum();
    boolean isArray = userClass.isArray();
    
    
    //obj是否是该类的实例
    boolean isInstance = userClass.isInstance(obj);
    
    //是否是sonClass或sonClass的基类
    boolean isAssignableFrom = userClass.isAssignableFrom(sonClass);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

     

    get 获取基本信息
    //获取全限定类名
    String name = userClass.getName();
    //获取全限定类型名称(类名)
    String typeName = userClass.getTypeName();
    //获取短类名
    String simpleName = userClass.getSimpleName();
    
    //获取所属包的信息
    Package packageInfo = userClass.getPackage();
    
    
    //获取该类继承或实现的所有接口
    Class<?>[] interfaces = userClass.getInterfaces();
    
    //获取直接父类的Class对象,Object返回null
    Class<?> declaringClass = userClass.getSuperclass();
    
    //如果是数组,则返回元素类型;如果不是数组,则返回null
    Class<?> componentType = userClass.getComponentType();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

     

    get 获取 Field、Method、Constructor
    //获取全部public字段,没有时返回空数组
    Field[] fields = userClass.getFields();
    //获取指定名称的public字段
    Field id = userClass.getField("id");
    
    //获取直接在该类中声明的所有字段
    Field[] declaredFields = userClass.getDeclaredFields();
    //获取直接在该类中声明的指定名称的字段
    Field id = userClass.getDeclaredField("id");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    //获取全部public方法
    Method[] methods = userClass.getMethods();
    //获取指定名称、形参表的public方法
    Method setId = userClass.getMethod("setId", Integer.class);
    
    //获取直接在该类中声明的全部方法
    Method[] declaredMethods = userClass.getDeclaredMethods();
    //获取直接在该类中声明的指定名称、指定形参表的方法
    Method setId = userClass.getDeclaredMethod("setId", Integer.class);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    //获取全部public构造器
    Constructor<?>[] constructors = userClass.getConstructors();
    //获取指定形参表的public构造器
    Constructor<User> constructor = userClass.getConstructor(Integer.class, String.class, String.class);
    
    //获取直接在该类中声明的全部构造器
    Constructor<?>[] declaredConstructors = userClass.getDeclaredConstructors();
    //获取直接在该类中声明的指定形参表的构造器
    Constructor<User> declaredConstructor = userClass.getDeclaredConstructor(Integer.class, String.class, String.class);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • getXxx:只能获取到public的字段、方法、构造器,包括从父类继承来的public字段、方法(构造器不会被继承,不管使用哪种访问权限修饰);
    • getDeclaredXxx: declared 声明,只要直接在该class中声明了,不管该字段、方法、构造器的访问权限是哪种,都可以获取到,但不能获取到从父类继承的字段、方法。

    Constructor、Method是2个概念,Constructor虽然可以称为构造方法,但不属于Method,即获取的Method中不会包含Constructor。

    字段一般是private,获取字段通常使用 getDeclaredFields();方法、构造器一般是public,获取方法、构造器通常使用 getMethods()、getConstructors()。

     

    getResource 加载资源
    ClassLoader classLoader = userClass.getClassLoader();
    
    //实质是使用对应的类加载器来加载资源
    URL resource = userClass.getResource("/conf/app.xml");
    InputStream is = userClass.getResourceAsStream("/conf/app.xml");
    
    • 1
    • 2
    • 3
    • 4
    • 5

    路径以不以 / 开头的区别

    • 以 / 开头:/ 表示classpath根目录,在classpath中检索;
    • 不以 / 开头:默认取该class所在包的路径,在该class所在的包下检索。

    因为编译复制到target目录时,java包默认只复制源码文件,不会复制 xml、yml 之类的配置文本文件,路径不以 / 开头时会出现找不到对应文件的问题,所以这里的路径更推荐以 / 开头。

     

    newInstance 创建实例
    //创建实例,实质是调用无参构造器
    User user = userClass.newInstance();
    
    • 1
    • 2

     

    Class、Field、Method、Constructor的公共方法

    Modifier 修饰的关键字

    modifier 修饰,此处以Field为例

    //获取修饰此字段的所有关键字。这些关键字都是用16进制的int表示,计算得到一个总体的int
    int modifiers = field.getModifiers();
    
    //访问权限
    boolean isPrivate = Modifier.isPrivate(modifiers);
    boolean isProtected = Modifier.isProtected(modifiers);
    boolean isPublic = Modifier.isPublic(modifiers);
    
    //static
    boolean isStatic = Modifier.isStatic(modifiers);
    //final
    boolean isFinal = Modifier.isFinal(modifiers);
    //abstract
    boolean isAbstract = Modifier.isAbstract(modifiers);
    //interface
    boolean isInterface = Modifier.isInterface(modifiers);
    
    //native
    boolean isNative = Modifier.isNative(modifiers);
    //transient 不进行序列化
    boolean isTransient = Modifier.isTransient(modifiers);
    //volatile
    boolean isVolatile = Modifier.isVolatile(modifiers);
    //synchronized
    boolean isSynchronized = Modifier.isSynchronized(modifiers);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

     

    getAnnotations、getDeclaredAnnotations 获取注解
    • getAnnotations 是获取直接标注的 + 继承得到的注解,getDeclaredAnnotations 是获取直接标注的注解。
    • class是获取标注在类/接口上的注解,field是获取标注在字段上的注解,method是获取标注在方法上的注解,constructor是获取标注在构造器上的注解。
    • getAnnotations、getDeclaredAnnotations 都不能获取到标注在函数参数前的注解,获取标注在函数参数前的注解要用 getParameterAnnotations
    • 运行时获取不到 @SuppressWarnings、@Deprecated、@Slf4j 之类@Retention(RetentionPolicy.SOURCE) 的注解,因为这些注解在编译时会被丢弃掉。
    //获取该class上的全部注解
    Annotation[] annotations1 = userClass.getAnnotations();
    
    //获取指定注解
    Data[] annotationsByType = userClass.getAnnotationsByType(Data.class);
    Data annotation = userClass.getAnnotation(Data.class);
    
    
    //获取直接标注在该类上的全部注解
    Annotation[] annotation1 = userClass.getDeclaredAnnotations();
    
    //获取直接标注在该类上的指定注解
    Data[] declaredAnnotationsByType = userClass.getDeclaredAnnotationsByType(Data.class);
    Data declaredAnnotation = userClass.getDeclaredAnnotation(Data.class);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

     

    附:关于注解的继承
    @Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)  //保留到运行时
    @Inherited  //允许继承
    @Documented
    @interface Test {
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    @Test
    class A {
    
    }
    
    //因为@Test允许继承,类B继承类A时,会继承类上的@Test
    class B extends A {
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    如果注解自身标注了 @Inherited 允许继承,则Class、Field、Method、Constructor上的注解都可以被继承,其中

    • Field、Method、Constructor继承的注解,也算直接在该类中声明的,可以用 getDeclaredAnnotations() 获取到。
    • Class的注解继承只支持类继承,不支持接口继承、接口实现,示例:classA extend classB 会继承classB上的注解,interfaceA extends interfaceBclassA implements interfaceB 都不会继承 interfaceB 上的注解。
    • 类继承的注解,不算直接在该类中标注的,用 getDeclaredAnnotations() 获取不到,需要用 getAnnotations() 获取。

     

    setAccessible 设置访问权限

    此方法是Field、Method、Constructor都有的。

    如果Field、Method、Constructor的访问权限为private、protected、default,操作时可能无法访问,需要先 setAccessible(true) 设置为可访问

    field.setAccessible(true);
    
    • 1

     

    Field 其它常用方法

    //获取此字段的数据类型
    Class<?> type = field.getType();
    Type genericType = field.getGenericType();
    
    //获取字段名
    String name = field.getName();
    
    
    //获取目标类实例的此字段的值,可以使用get()返回Object,也可以使用getXxx()直接获取对应的基本类型
    Object o = field.get(user);
    //注意:此系列方法只能获取基本类型的值,不会自动拆箱,不能用于获取包装类型的值
    int id = field.getInt(user);
    
    //set的用法和get类似
    field.set(user, 10);
    field.setInt(user, 10);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

     

    Method 其它常用方法

    //是否是默认方法
    boolean isDefault = method.isDefault();
    //参数个数是否可变
    boolean varArgs = method.isVarArgs();
    
    
    //获取方法名
    String name = method.getName();
    
    
    //获取此方法声明的可能抛出的异常
    Class<?>[] exceptionTypes = method.getExceptionTypes();
    Type[] genericExceptionTypes = method.getGenericExceptionTypes();
    
    
    //获取形参表信息
    Parameter[] parameters = method.getParameters();
    
    //参数个数
    int parameterCount = method.getParameterCount();
    
    //参数类型
    Type[] genericParameterTypes = method.getGenericParameterTypes();
    Class<?>[] parameterTypes = method.getParameterTypes();
    
    //获取标注在各个形参上的注解,一个形参可以标注多个注解,一个一维数组 <=> 标注在一个形参上的所有注解
    Annotation[][] parameterAnnotations = method.getParameterAnnotations();
    
    
    //获取返回值类型
    Class<?> returnType = method.getReturnType();
    Type genericReturnType = method.getGenericReturnType();
    
    
    //调用此方法,参数分别为目标类实例、实参表。返回值是Object,需要强转
    method.invoke(user, 10);
    String username = (String) method.invoke(user);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

     

    Constructor 其它常用方法

    //参数个数是否可变
    boolean isVarArgs = constructor.isVarArgs();
    
    //获取此方法声明的可能抛出的异常
    Class<?>[] exceptionTypes = constructor.getExceptionTypes();
    Type[] genericExceptionTypes = constructor.getGenericExceptionTypes();
    
    
    //获取形参表信息
    Parameter[] parameters = constructor.getParameters();
    
    //参数个数
    int parameterCount = constructor.getParameterCount();
    
    //参数类型
    Type[] genericParameterTypes = constructor.getGenericParameterTypes();
    Class<?>[] parameterTypes = constructor.getParameterTypes();
    
    //获取标注在各个形参上的注解,一个形参可以标注多个注解,一个一维数组 <=> 标注在一个形参上的所有注解
    Annotation[][] parameterAnnotations = constructor.getParameterAnnotations();
    
    
    //调用该构造器创建目标类实例,传入实参表。Constructor 构造器泛型需要为目标类,未指定泛型时返回的是Object
    User user = constructor.newInstance(1, "chy", "abcd");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

     

    使用反射创建类的实例

    //通过Class对象的 newInstance() 方法创建实例,实质是调用无参构造器创建实例。此种方式只能调用无参构造器
    User user = userClass.newInstance();
    
    
    //调用对应的构造方法创建实例,可以调用任何构造方法
    Constructor<User> constructor = userClass.getConstructor(Integer.class, String.class, String.class);
    User user = constructor.newInstance(1, "chy", "abcd");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    如果有工厂方法之类的方法可以创建类的实例,也可以获取、调用对应的Method来创建类的实例。

     

    反射常用工具类

    spring的ReflectionUtils
    //获取指定字段,可以指定字段类型
    Field id = ReflectionUtils.findField(User.class, "id");
    Field id = ReflectionUtils.findField(User.class, "id", Integer.class);
    
    
    //获取指定方法,可以指定方法的形参表
    Method getId = ReflectionUtils.findMethod(User.class, "getId");
    Method setId = ReflectionUtils.findMethod(User.class, "setId", Integer.class);
    
    
    
    //获取指定类中声明的方法,包括重写的基类方法,不包括基类的方法(从基类继承的方法)
    Method[] declaredMethods = ReflectionUtils.getDeclaredMethods(User.class);
    
    //获取指定类及其基类中声明的所有方法,包括基类的私有方法
    Method[] allDeclaredMethods = ReflectionUtils.getAllDeclaredMethods(User.class);
    
    //获取指定类中声明的所有方法,如果重写了基类的方法,会剔除基类中相同的方法(声明)
    Method[] uniqueDeclaredMethods = ReflectionUtils.getUniqueDeclaredMethods(User.class);
    //可指定方法过滤器,对 uniqueDeclaredMethods 进行过滤
    ReflectionUtils.getUniqueDeclaredMethods(User.class, method -> method.isAccessible());
    
    
    
    //是否是equals、hashCode之类的特定方法
    boolean isEqualsMethod = ReflectionUtils.isEqualsMethod(method);
    boolean isHashCodeMethod = ReflectionUtils.isHashCodeMethod(method);
    
    //判断该方法或重写的对应父类方法的原型上是否声明了指定异常
    boolean declaresException = ReflectionUtils.declaresException(method, NullPointerException.class);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    // setAccessible(true)
    ReflectionUtils.makeAccessible(field);
    ReflectionUtils.makeAccessible(method);
    ReflectionUtils.makeAccessible(constructor);
    
    //获取特定形参表的的构造器,会自动设置 setAccessible(true)
    Constructor<User> constructor = ReflectionUtils.accessibleConstructor(User.class, Integer.class, String.class, String.class);
    
    
    
    //获取指定对象的指定字段的值,返回Object,需要强转
    Object id = ReflectionUtils.getField(field, user);
    
    //设置指定对象的指定字段的值
    ReflectionUtils.setField(field, user, value);
    
    //调用指定对象的指定方法,obj...是实参表
    ReflectionUtils.invokeMethod(method, user, obj...);
    
    //如果操作的是静态成员(static),无需指定实例对象,直接传null
    
    
    
    //遍历字段|方法,包括静态的,包括从父类继承的
    ReflectionUtils.doWithFields(User.class, field -> System.out.println(field.getName()));
    ReflectionUtils.doWithMethods(User.class, method -> System.out.println(method.getName()));
    
    //指定过滤器
    ReflectionUtils.doWithFields(User.class, field -> System.out.println(field.getName()), field -> field.isAccessible());
    ReflectionUtils.doWithMethods(User.class, method -> System.out.println(method.getName()), method -> method.isAccessible());
    
    //只遍历该class中直接声明的字段,包括静态的,不包括从父类继承的
    ReflectionUtils.doWithLocalFields(User.class, field -> System.out.println(field.getName()));
    ReflectionUtils.doWithLocalMethods(User.class, method -> System.out.println(method.getName()));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
  • 相关阅读:
    人生进步法则-提问法则
    【软件评测】Apowersoft 傲软抠图AI智能换背景工具软件
    Flask, Access-Control-Allow-Origin 跨域请求的解决方法
    【Docker项目实战】使用Docker安装Blossom 笔记应用
    linux 清理垃圾文件
    机器学习强基计划1-3:图文详解Logistic回归原理(两种优化)+Python实现
    STC89C52+DHT20设计的环境温湿度检测仪
    `算法题解` `AcWing` 4605. 最大周长
    Program Header Table(转载)
    同事老是吐槽我的接口性能差,原来真凶就在这里!
  • 原文地址:https://blog.csdn.net/chy_18883701161/article/details/126380585