该文章主要讲述Java中Reflection反射相关的API使用,以及一些常见问题。
Jdk-Reflection 概述 Java反射机制可以让我们在编译期(Compile Time)之外的运行期(Runtime)检查类,接口,变量以及方法的信息。反射还可以让我们在运行期实例化对象,调用方法,通过调用get/set方法获取变量的值。
Class 概述 Class的实例表示正在运行的Java应用程序的类和接口
。 枚举是一种类和注释是一种接口。
每个数组都属于一个被映射成Class对象的类,该对象由具有相同元素类型和大小的所有数组共享。
原始类型(boolean, byte, char, short, int, long, float,double)
,以及关键字void
也表示为Class对象。
特征 Class没有公共构造函数。 相反, Class对象由Java虚拟机的类加载并通过调用自动构造defineClass的类加载器方法 。
获取Class 获取Class对象的方式一共有三种:通过Class.forName("全路径包名"),类.class,类实例.getClass()
。
示例代码:
1 2 3 4 5 6 Class<?> stringClass = Class.forName("java.lang.String" ); Class<?> integerClass = String.class; Class<?> stringObjClass = new String ("1" ).getClass(); System.out.println(stringClass == integerClass); System.out.println(stringObjClass == integerClass);
输出结果:
输出的结果证实了相同元素类型的Class实例是相同且共享的。
核心方法 在这个阶段会讲述Class类中常用方法的使用
ReflectionBean
类:
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 public class ReflectionBean extends ClassBean { private String name; private Integer age; String phone; protected String address; public Boolean sex; public void reflection () { } public void reflection (String name) { } private boolean validInfo () { return name != null && name.length() > 0 && Objects.nonNull(age); } public ReflectionBean (@NotNull String name) { this .name = name; } private ReflectionBean (String name, Integer age) { this .name = name; this .age = age; } public ReflectionBean () { } public static ReflectionBean instance () { return new ReflectionBean (); } }
ClassBean
类:
1 2 3 4 5 6 7 8 9 10 11 public class ClassBean { private String className; public boolean validClassName () { return className != null && className.length() > 0 ; } public ClassBean () { } public ClassBean (String className) { this .className = className; } }
Construtor
一个Constructor
对应着类中的一个构造函数,可以使用Class.getConstructors()
获取指定类所有的构造函数(私有无法获取)。也可以使用Class.getConstructor(Class<?> ... paramType)
获取指定参数的构造方法。
测试代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Test public void testConstructor () throws NoSuchMethodException { Class<ReflectionBean> reflectionBeanClass = ReflectionBean.class; Constructor<ReflectionBean> specificConstructor = reflectionBeanClass.getConstructor(String.class); System.out.println(specificConstructor.getName()); System.out.println(Arrays.toString(specificConstructor.getParameterAnnotations())); System.out.println(specificConstructor.getParameterCount()); System.out.println(Arrays.toString(specificConstructor.getParameterTypes())); Constructor<?>[] constructors = reflectionBeanClass.getConstructors(); System.out.println(Arrays.toString(constructors)); }
输出结果:
1 2 3 4 5 io.better.jdk.reflection.ReflectionBean [[Ljava.lang.annotation.Annotation;@1b701da1] 1 [class java.lang.String] [public io.better.jdk.reflection.ReflectionBean(java.lang.String), public io.better.jdk.reflection.ReflectionBean()]
上面代码中演示了Class.getConstructors
和Class.getConstructor(Class<?> ..params)
获取构造方法。
通过Constructor对象我们可以获取到构造方法中的注解,参数,参数类型,参数数量
等信息,但这两种方式都不能获取私有构造方法。那如何获取私有的构造器呢?
使用getDeclaredConstructors()
和getDeclaredConstructor(Class<?> ..params)
可以获取到类中所有的构造方法,包括私有的。
1 2 3 4 5 6 7 8 9 @Test public void testGetDeclaredConstructors () throws NoSuchMethodException { Class<ReflectionBean> reflectionBeanClass = ReflectionBean.class; Constructor<?>[] declaredConstructors = reflectionBeanClass.getDeclaredConstructors(); Constructor<ReflectionBean> specificDeclaredConstructor = reflectionBeanClass.getDeclaredConstructor(String.class, Integer.class); }
介绍完Constructor
对象的获取方式后,接下来通过Constructor
对象来实例化目标对象。
我们可以通过Constructor.newInstance(Object ...params)
方法传入实际的参数来创建目标对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Test public void testConstructorInstance () throws Exception { Class<ReflectionBean> reflectionBeanClass = ReflectionBean.class; Constructor<ReflectionBean> stringConstructor = reflectionBeanClass.getConstructor(String.class); ReflectionBean reflectionBean = stringConstructor.newInstance("Constructor" ); System.out.println(reflectionBean); Constructor<ReflectionBean> declaredConstructor = reflectionBeanClass.getDeclaredConstructor(String.class, Integer.class); declaredConstructor.setAccessible(true ); reflectionBean = declaredConstructor.newInstance("DeclaredConstructor" , 1 ); System.out.println(reflectionBean); }
Method
讲完Constructor
的使用,已经能通过其创建对象了,那如何调用目标对象中的方法呢?
与Constructor类似,Class提供了getMethods,getMethod(String methodName, Class<?> ...paramType)
方法分别获取执行类的所有方法(包括静态方法,父类继承下来的方法)
和指定方法
。
1 2 3 4 5 6 7 8 9 10 11 12 13 @Test public void testGetMethod () throws Exception { Class<ReflectionBean> reflectionBeanClass = ReflectionBean.class; Method[] methods = reflectionBeanClass.getMethods(); Method method = reflectionBeanClass.getMethod("reflection" , String.class); System.out.println("指定方法: " + method.getName()); for (Method item : methods) { System.out.println(item); } }
执行结果:
从结果可以看出,私有方法未被获取到,和Constructor类似这两个方法不能获取私有方法,要想获取私有方法必须使用
1 2 3 4 5 6 7 8 9 10 11 12 @Test public void testGetDeclaredMethod () throws Exception { Class<ReflectionBean> reflectionBeanClass = ReflectionBean.class; Method[] methods = reflectionBeanClass.getDeclaredMethods(); Method method = reflectionBeanClass.getDeclaredMethod("validInfo" ); System.out.println(method.getName()); List<String> methodName = Stream.of(methods).map(Method::getName).collect(Collectors.toList()); System.out.println(methodName); }
执行结果:
1 2 validInfo [equals, toString, hashCode, getName, setName, instance, reflection, reflection, validInfo, canEqual, getAge, setAge]
获取类中方法已经了解,接下一来尝试调用执行一下方法。
与Constructor不太一样,要想执行方法需要调用invoke(Object obj, Object... args)
。该方法接受两个参数,第一个为目标对象(即拥有此方法的类对象),第二为方法所需的参数。当调用私有方法时做法与Constructor一致。
1 2 3 4 5 6 7 8 9 10 11 12 13 @Test public void testInvokedMethod () throws Exception { Class<ReflectionBean> reflectionBeanClass = ReflectionBean.class; Constructor<ReflectionBean> constructor = reflectionBeanClass.getDeclaredConstructor(String.class, Integer.class); constructor.setAccessible(true ); ReflectionBean object = constructor.newInstance("TEST" , 1 ); Method method = reflectionBeanClass.getDeclaredMethod("validInfo" ); method.setAccessible(true ); Object methodResult = method.invoke(object); System.out.println(methodResult); }
Field
与Method类似,我们可以使用getFields,getField(String name)
两个方法来获取类中所有非私有的字段。
1 2 3 4 5 6 7 8 9 10 11 @Test public void testGetField () throws Exception { Class<ReflectionBean> reflectionBeanClass = ReflectionBean.class; Field[] fields = reflectionBeanClass.getFields(); for (Field field : fields) { System.out.println(field); } }
这两个方法只能获取公共字段(public修饰)
,private,default,protected
都获取不到。
我们可以使用getDeclaredFields
,getDeclaredField
两个方法来获取所有的字段和指定字段,
1 2 3 4 5 6 7 8 9 10 11 @Test public void testGetDeclaredField () throws Exception { Class<ReflectionBean> reflectionBeanClass = ReflectionBean.class; Field[] fields = reflectionBeanClass.getDeclaredFields(); for (Field field : fields) { System.out.println(field); } Field age = reflectionBeanClass.getDeclaredField("age" ); System.out.println(age); }
执行结果:
如果想要获取某个字段对应的值,则需要调用get(Object obj)
方法,传入包含此字段的目标类对象,私有字段需要调用setAccessible()
方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Test public void testFieldMethod () throws Exception { Class<ReflectionBean> reflectionBeanClass = ReflectionBean.class; Constructor<ReflectionBean> constructor = reflectionBeanClass.getDeclaredConstructor(String.class, Integer.class); constructor.setAccessible(true ); ReflectionBean object = constructor.newInstance("TEST" , 1 ); Field age = reflectionBeanClass.getDeclaredField("age" ); age.setAccessible(true ); System.out.println(age.getDeclaringClass()); System.out.println(age.get(object)); }
总结 Constructor,Method,Field
三个类分别 对应类中 的构造方法、方法、字段
,分别提供了getDeclared
开头的方法来获取对应所有的信息。
其他方法 newInstance()
在获取到类的Class对象时,可以通过调用此方法来创建目标类对象,该方法会默认调用类的无参构造方法
来创建对象。
1 2 3 4 5 6 @Test public void testNewInstance () throws Exception { Class<ReflectionBean> reflectionBeanClass = ReflectionBean.class; ReflectionBean reflectionBean = reflectionBeanClass.newInstance(); System.out.println(reflectionBean); }
isInstance(Object obj)
该方法用于断定指定Object是赋值兼容与此表示的对象Class ,等效instanceof
运算符。
1 2 3 4 Class<String> stringClass = String.class; stringClass.isInstance("test" ); stringClass.isInstance(123456 );
使用场景 反射进行数组扩容 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @Test public void testArrayExtension () { String[] stringArray = new String []{"1" , "2" , "3" , "4" , "5" }; System.out.println("原始数组长度: " + stringArray.length); Object newStringArray = ArrayExtension(stringArray, stringArray.length * 2 ); System.out.println("扩容后数组长度: " + Array.getLength(newStringArray)); }public Object ArrayExtension (Object sourceArray, int extensionLength) { if (extensionLength < Array.getLength(sourceArray)) throw new IllegalArgumentException ("扩展长度不合法" ); Class<?> componentType = sourceArray.getClass().getComponentType(); Object newStringArray = Array.newInstance(componentType, extensionLength); System.arraycopy(sourceArray, 0 , newStringArray, 0 , Array.getLength(sourceArray)); return newStringArray; }
反射跳过泛型检查 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Test public void testJumpTypeCheck () throws Exception { List<String> stringList = new ArrayList <>(); stringList.add("a" ); stringList.add("b" ); stringList.add("c" ); Class<? extends List > listClass = stringList.getClass(); Method addMethod = listClass.getMethod("add" , Object.class); addMethod.invoke(stringList, 1 ); System.out.println(stringList.size()); }