Java 反射(Reflection)使用小记
目录
前言
在Java中,运行状态时,只要给定类的名字,就能知道这个类的所有信息,可以构造出指定对象,可以调用它的任意一个属性和方法。这种动态获取信息以及动态调用对象的方法的功能是Java语言的反射机制。
本篇对反射基础进行了讲解。
可以带着以下问题进行学习:
- 什么是Java语言的反射机制?
- 反射都提供了什么功能?
- 反射如何获取Class对象?
- 反射如何创建类对象?
- 反射如何获取类的成员变量和类的成员方法?
- 反射有什么作用?
反射基础
1.获取 class 类
//获取反射
//第一种,全类名forName获取
Class clazz1 = Class.forName("com.pjh.mySty.SonClass");
//第二种,使用 .class 方法。
Class<SonClass> clazz2 = SonClass.class;
//第三种,使用类实例对象的 getClass() 方法。
SonClass person = new SonClass("清风",23);
Class clazz3 = person.getClass();
可以通过以下api判定类的从属关系:
if (person instanceof FatherClass){
System.out.println("true");
}
if (clazz3.isAssignableFrom(SonClass.class)){
System.out.println("true2");
}
instanceof : 当前对象实例从属于后类。(包括后类的子类 —— 即当前类是后类或后类的子类)
isAssignableFrom:Class 方法,只能判定是否是指定类。
2.反射Api
//获取所有的构造方法 / private public
public Constructor<?>[] getDeclaredConstructors()
//获取特定的构造方法 / private public
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
//获取类的父类
public native Class<? super T> getSuperclass()
//获取类实现的接口
private Class<?>[] getInterfaces(boolean cloneArray)
//获取在类内定义的内部类或接口
public Class<?>[] getDeclaredClasses()
//获取所有的方法
public Method[] getDeclaredMethods() throws SecurityException
//根据方法名和参数获得特定的方法
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
//获取类型的定义的所有属性
public Field[] getFields() throws SecurityException
// 根据属性命名获得特定的Field
public Field getField(String name)
3.反射的作用
动态的加载类,动态获取类的信息(属性、方法、构造器)
动态的构造对象
动态调用类和对象的任意方法、构造器
获取泛型信息
处理注解
动态代理
…
反射的使用
注:在反射的使用中一般可以分为3大类:
1:class.getXXXs() -> 获取共有的全部 XXX;
2:class.getXXX(Class<?>... parameterTypes) -> 获取公有的指定参数的XXX; 3:class.getDeclaredXXXX[()/(Class<?>… parameterTypes)] -> 获取全部(包括私有)的XXX;
1和3 2和3 混合使用以达到获取全部的需求内容,一般包括 属性、方法、注解等。
1.构造函数(Constructor)
//获取 class 类
Class<SonClass> clazz = SonClass.class;
//获取所有的构造函数,返回构造函数数组
Constructor<?>[] pubAllConstructors = clazz.getConstructors();
//无参构造器
Constructor<SonClass> noArgsConstructor = clazz.getConstructor();
//获取指定参数的构造函数,返回构造函数
Constructor<SonClass> pubOneConstructor = clazz.getConstructor(String.class, Integer.class);
//获取所有的构造函数,包括私有构造函数,返回构造函数数组
Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors();
//获取指定参数的构造函数(这里是私有的),返回构造函数
Constructor<SonClass> declaredConstructor = clazz.getDeclaredConstructor(String.class);
拿到构造函数能干啥(实例化对象):
SonClass qf = pubOneConstructor.newInstance("qf", 20);
用处:在实际项目中特别是某些高度配置化、工作流项目中,工作工单就是通过工单场景来反射生成对应处理类的实例对象处理流程。
2.属性(Field)
作用:运行时获取属性值,或动态修改对象实例的值。
SonClass sonClass = new SonClass("qf",23);
Class<SonClass> clazz = SonClass.class;
// 获取所有public的属性(包括父类的属性)
Field[] fields = clazz.getFields();
for (Field field : fields) {
field.getAnnotations(); —— 属性上注解
field.getModifiers(); —— 属性的访问修饰符
field.getType(); —— 属性类型
field.getName(); —— 属性名
}
// 指定获取某个公有属性
Field mFatherName = clazz.getField("sonBirthday");
//获取所有的属性包括私有的(不包含任何父类的)
Field[] declaredFields = clazz.getDeclaredFields();
//指定获取某个属性,并改变已有实例的属性值
Field mSonName = clazz.getDeclaredField("sonName");
//设置私有属性访问权限设置为可访问
mSonName.setAccessible(true);
mSonName.set(sonClass,"aaa");
//不能直接获取父类的私有属性,可以通过 getSuperclass() 先获取父类Class 再通过父类反射获取
Class<? super SonClass> superclass = clazz.getSuperclass();
Field[] declaredFields1 = superclass.getDeclaredFields();
3.方法
作用:运行时获取方法,动态执行方法。
SonClass sonClass = new SonClass("qf",23);
Class<SonClass> clazz = SonClass.class;
// 获取所有public的方法(包括父类的方法)
Method[] methods = clazz.getMethods();
for (Method method : methods) {
method.getModifiers(); //访问修饰符
method.getAnnotations(); //方法注解
method.getName(); //方法名
method.getReturnType(); //方法返回类型
method.getParameterCount(); //参数个数
method.getParameterTypes(); //参数类型
method.getExceptionTypes(); //异常类型
}
// 指定获取某个公有方法
Method getSonName = clazz.getMethod("getSonName");
// 获取所有的方法包括私有的(不包含任何父类的方法)
Method[] declaredMethods = clazz.getDeclaredMethods();
// 指定获取某个方法
Method declaredMethod = clazz.getDeclaredMethod("printSonMsg");
//设置方法访问权限设置为可访问
declaredMethod.setAccessible(true);
//执行方法 invoke(对象实例,Object... args)
Object invoke = declaredMethod.invoke(sonClass);
4.其他
SonClass sonClass = new SonClass("qf",23);
Class<SonClass> clazz = SonClass.class;
// 获取父类的方法
//返回当前Class对象所表示的类的直接超类(父类)
Class<? super SonClass> superclass = clazz.getSuperclass();
//返回的是泛型化的超类信息
AnnotatedType annotatedSuperclass = clazz.getAnnotatedSuperclass();
//返回一个AnnotatedType对象,该对象代表带有注解的直接超类。这样可以获得超类上应用的所有元注解和类型注解信息。
Type genericSuperclass = clazz.getGenericSuperclass();
//获取类的包
Package aPackage = clazz.getPackage();
//获取类的接口
Class[] interfaces = clazz.getInterfaces();
//获取类的注解
Annotation[] annotations = clazz.getAnnotations();
进阶深入反射
1.反射通用方法
// java.util.Properties#setProperty
public synchronized Object setProperty(String key, String value) {
return put(key, value);
}
public static void main(String[] args) throws Exception {
Properties properties = new Properties();
// 用反射方式调用此方法 properties.setProperty("name", "boy");
invokeMethod(properties, "setProperty", new Class[]{String.class, String.class}, "name", "boy");
System.out.println(properties.toString());
}
/**
* 反射调用方法
* @param obj 对象
* @param methodName 方法名
* @param types 参数类型
* @param args 参数的值
* @return
*/
public static Object invokeMethod(Object obj, String methodName, Class<?>[] types, Object... args) throws Exception {
Class<?> clazz = obj.getClass();
Method method = clazz.getMethod(methodName, types);
return method.invoke(obj, args);
}
2.工厂模式
public static void main(String[] args) throws Exception {
// Usb usb = UsbFactory.createUsb(Mouse.class);
// usb.service();
UsbFactory.service(Mouse.class);
}
public class UsbFactory {
public static Usb createUsb(Class<?> clazz) {
Usb usb = null;
try {
usb = (Usb) clazz.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return usb;
}
public static void service(Class<?> clazz) {
try {
Usb usb = (Usb) clazz.newInstance();
usb.service();
} catch (Exception e) {
e.printStackTrace();
}
}
}
public interface Usb {
void service();
}
public class Mouse implements Usb {
@Override
public void service() {
System.out.println("mouse running...");
}
}
3. 注解处理
反射时能获取到注解@Retention(RetentionPolicy.RUNTIME)
指定注解可以保留的域
实战:可以在指定类上加上自定义注解来表明该类需要走特殊处理方法。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TypeValue {
String value();
}
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ValueInfo {
String name();
String type() default "";
}
@TypeValue("userInfo")
public class User {
@ValueInfo(name = "age", type = "int")
private Integer age;
private String name;
@ValueInfo(name = "test")
public void test(String name) {
System.out.println("test() running, name: " + name);
}
}
方法上注解
public static void main(String[] args) throws Exception {
Class<?> clazz = User.class;
Method method = clazz.getMethod("test", String.class);
ValueInfo annotation = method.getAnnotation(ValueInfo.class);
System.out.println(annotation.name() + ";" + annotation.type());
// 调用方法
User user = (User) clazz.newInstance();
method.invoke(user, annotation.name());
}
属性上注解
public static void main(String[] args) throws Exception {
Class<?> clazz = User.class;
System.out.println("类是否有TypeValue注解:" + clazz.isAnnotationPresent(TypeValue.class));
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(ValueInfo.class)) {
ValueInfo annotation = field.getAnnotation(ValueInfo.class);
System.out.println(field.getName() + "注解上的值为:" + annotation.name());
}
}
}
Field field = userClass.getDeclaredField("age");
FieldValue fieldValue = field.getAnnotation(FieldValue.class);
System.out.println(fieldValue.name() + "; " + fieldValue.type());
Annotation[] annotations = clazz.getAnnotations();
System.out.println(Arrays.toString(annotations));