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;

 

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);

 

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();

 

get 获取 Field、Method、Constructor
//获取全部public字段,没有时返回空数组
Field[] fields = userClass.getFields();
//获取指定名称的public字段
Field id = userClass.getField("id");

//获取直接在该类中声明的所有字段
Field[] declaredFields = userClass.getDeclaredFields();
//获取直接在该类中声明的指定名称的字段
Field id = userClass.getDeclaredField("id");
//获取全部public方法
Method[] methods = userClass.getMethods();
//获取指定名称、形参表的public方法
Method setId = userClass.getMethod("setId", Integer.class);

//获取直接在该类中声明的全部方法
Method[] declaredMethods = userClass.getDeclaredMethods();
//获取直接在该类中声明的指定名称、指定形参表的方法
Method setId = userClass.getDeclaredMethod("setId", Integer.class);
//获取全部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);
  • 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");

路径以不以 / 开头的区别

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

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

 

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

 

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);

 

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);

 

附:关于注解的继承
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)  //保留到运行时
@Inherited  //允许继承
@Documented
@interface Test {

}
@Test
class A {

}

//因为@Test允许继承,类B继承类A时,会继承类上的@Test
class B extends A {

}

如果注解自身标注了 @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);

 

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);

 

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);

 

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<User> 构造器泛型需要为目标类,未指定泛型时返回的是Object
User user = constructor.newInstance(1, "chy", "abcd");

 

使用反射创建类的实例

//通过Class对象的 newInstance() 方法创建实例,实质是调用无参构造器创建实例。此种方式只能调用无参构造器
User user = userClass.newInstance();


//调用对应的构造方法创建实例,可以调用任何构造方法
Constructor<User> constructor = userClass.getConstructor(Integer.class, String.class, String.class);
User user = constructor.newInstance(1, "chy", "abcd");

如果有工厂方法之类的方法可以创建类的实例,也可以获取、调用对应的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);
// 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()));
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值