Java中反射的应用
作者:yuhuofei2021
反射的应用
1. 获取运行时类的完整结构
通过反射获取运行时类的完整结构,包括一下所列都是可以获取的,下面来试试
- Interface:实现的全部接口
- Superclass:所继承的父类
- Constructor:全部的构造器
- Method:全部的方法
- Field:全部的属性
- Annotation:注解
新建一个 Person.java 类
package com.javabasic.reflection; /** * @Description * @ClassName Person * @Author yuhuofei * @Date 2022/9/25 16:44 * @Version 1.0 */ public class Person { private String name; private Integer age; private String getName() { return name; } private void setName(String name) { this.name = name; } private Integer getAge() { return age; } private void setAge(Integer age) { this.age = age; } public Person() { } public Person(String name, Integer age) { this.name = name; this.age = age; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
新建 TestReflection.java 类,测试一下通过反射获取上面这个类的信息
package com.javabasic.reflection; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; /** * @Description 反射的应用 * @ClassName TestReflection * @Author yuhuofei * @Date 2022/10/2 15:57 * @Version 1.0 */ public class TestReflection { public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException { Class<?> aClass = Class.forName("com.javabasic.reflection.Person"); System.out.println("==================获取类名====================="); //获取类的名字 System.out.println("类名:" + aClass.getSimpleName()); //获取包名+类名 System.out.println("包名及类名:" + aClass.getName()); System.out.println("==================获取类的属性====================="); //获取类的public类型属性 Field[] fields1 = aClass.getFields(); for (Field field : fields1) { System.out.println("类的public类型属性:" + field); } //获取类的所有属性 Field[] fields = aClass.getDeclaredFields(); for (Field field : fields) { System.out.println("类的所有属性:" + field); } //获取指定的属性 Field name = aClass.getDeclaredField("name"); System.out.println("类的指定属性:" + name); System.out.println("==================获取类的方法====================="); //获取本类及父类的所有public类型方法 Method[] methods = aClass.getMethods(); for (Method method : methods) { System.out.println("本类及父类的所有public类型方法:" + method); } //获取本类及父类的所有方法 Method[] declaredMethods = aClass.getDeclaredMethods(); for (Method method : declaredMethods) { System.out.println("本类父类的所有方法:" + method); } //获取指定方法 Method getName = aClass.getDeclaredMethod("getName", null); Method setName = aClass.getDeclaredMethod("setName", String.class); System.out.println("获取指定的getName方法:" + getName); System.out.println("获取指定的setName方法:" + setName); System.out.println("==================获取类的构造器====================="); //获取所有public类型构造器 Constructor<?>[] constructors = aClass.getConstructors(); for (Constructor constructor : constructors) { System.out.println("获取所有public类型构造器:" + constructor); } //获取所有构造器 Constructor<?>[] declaredConstructors = aClass.getDeclaredConstructors(); for (Constructor constructor : declaredConstructors) { System.out.println("获取所有构造器:" + constructor); } //获取指定的构造器 Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(String.class, Integer.class); System.out.println("获取指定的构造器:" + declaredConstructor); } }
结果如下
2. 动态创建对象动态操作方法及属性
2.1 动态创建对象
通过调用 Class 对象的 newInstance() 方法,可以创建类的对象
有了类的对象,就可以通过对象.方法名
的形式使用方法了,这和平常的使用方式一样,但需要注意设置的访问权限问题
使用 Class 对象创建类的对象是需要条件的,不同情况下,条件不同
使用无参构造器时的条件
- 无参构造器存在
- 无参构造器的访问权限要足够
//获取Class对象 Class<?> aClass = Class.forName("com.javabasic.reflection.Person"); //通过一个无参构造器创建一个Person的对象 Person person01 = (Person) aClass.newInstance(); System.out.println(person01);
使用有参构造器时的条件
- 通过 Class 类的 getDeclaredConstructor(Class<?>… parameterTypes) 获取当前类的指定有参构造器
- 向构造器的形参中传递一个对象数组进去,里面要包含构造器中所需的各个参数
- 通过 Constructor 实例化对象
//获取Class对象 Class<?> aClass = Class.forName("com.javabasic.reflection.Person"); //通过有参构造器创建对象 Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(String.class, Integer.class); Person person02 = (Person) declaredConstructor.newInstance("张三", 20); System.out.println(person02);
2.2 动态操作方法及属性
这里延用上面的 Person.java 类,然后新建一个测试类来测试
package com.javabasic.reflection; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; /** * @Description * @ClassName Test01 * @Author yuhuofei * @Date 2022/10/2 17:08 * @Version 1.0 */ public class Test01 { public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException { //获取Class对象 Class<?> aClass = Class.forName("com.javabasic.reflection.Person"); //通过一个无参构造器创建一个Person的对象 Person person01 = (Person) aClass.newInstance(); System.out.println(person01); //通过有参构造器创建对象 Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(String.class, Integer.class); Person person02 = (Person) declaredConstructor.newInstance("张三", 20); System.out.println(person02); //通过反射调用普通方法 Method method = aClass.getDeclaredMethod("setName", String.class); method.setAccessible(true); //关闭安全检测,允许访问private方法 Person person03 = (Person) aClass.newInstance(); method.invoke(person03, "李四"); //向person03对象中的setName方法传参 System.out.println(person03); //通过反射调用属性 Field field01 = aClass.getDeclaredField("name"); Field field02 = aClass.getDeclaredField("age"); field01.setAccessible(true); //关闭安全检测,允许访问private属性 field02.setAccessible(true); Person person04 = (Person) aClass.newInstance(); field01.set(person04, "王五"); //设置名字 field02.set(person04, 19); //设置年龄 System.out.println(person04); } }
值得注意的是,如果方法或者属性的访问权限是 private,那么需要配置 .setAccessible(true);
,不然无法访问
3. 反射操作泛型
java 采用泛型擦除的机制来引入泛型,但却只是给编译器 javac 使用的,用于确保数据的安全性和免去强制类型转换的问题,而一旦编译完成,所有与泛型相关的类型会全部擦除
为了通过反射能操作这些类型,java 新增了 ParameterizedType、GenericArrayType、TypeVariable、WildcardType 这几种类型来代表不能被归一到 Class 类中的类型但又和原始类型齐名的类型
- ParameterizedType:表示一种参数化类型,比如 Collection< String >
- GenericArrayType:表示一种元素类型是参数化类型或者类型变量的数组类型
- TypeVariable:各种类型变量的公共父接口
- WildcardType :代表一种通配符类型表达式
下面写一个类来实践一下,这里还是延用前面的 Person 类
package com.javabasic.reflection; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.List; import java.util.Map; /** * @Description 测试反射获取泛型 * @ClassName Test02 * @Author yuhuofei * @Date 2022/10/2 20:06 * @Version 1.0 */ public class Test02 { public void getData(Map<String, Person> map, List<Person> list) { System.out.println("getData()"); } public Map<String, Person> getMap() { System.out.println("getMap()"); return null; } public static void main(String[] args) throws NoSuchMethodException { //获取入参泛型 Method method = Test02.class.getMethod("getData", Map.class, List.class); Type[] genericParameterTypes = method.getGenericParameterTypes(); for (Type genericParameterType : genericParameterTypes) { System.out.println("泛型:" + genericParameterType); //判断拿到的是不是参数化类型,即是不是前面定义的Map或者List if (genericParameterType instanceof ParameterizedType) { //获取真实参数 Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments(); for (Type actualTypeArgument : actualTypeArguments) { System.out.println(actualTypeArgument); } } } System.out.println("============分割线==========="); //获取返回值泛型 method = Test02.class.getMethod("getMap", null); Type genericReturnType = method.getGenericReturnType(); System.out.println("返回值类型:" + genericReturnType); if (genericReturnType instanceof ParameterizedType) { Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments(); for (Type at : actualTypeArguments) { System.out.println(at); } } } }
得到的结果如下:
4. 反射操作注解
定义一个注解,如下:
package com.javabasic.annotation; import java.lang.annotation.*; /** * @Description 自定义注解 * @InterfaceName MyAnnotation * @Author yuhuofei * @Date 2022/9/18 11:26 * @Version 1.0 */ @Target({ElementType.TYPE, ElementType.METHOD}) @Documented @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { /** * 描述信息 */ String value() default ""; }
定义一个使用上面的注解的类,如下:
package com.javabasic.annotation; /** * @Description 测试自定义注解的使用 * @ClassName TestAnnotation * @Author yuhuofei * @Date 2022/9/18 11:32 * @Version 1.0 */ @MyAnnotation(value = "打印出两个整数之和,这是标记在类上的注解") public class TestAnnotation { @MyAnnotation(value = "打印出两个整数之和,这是标记在方法上的注解") public static void printResult() { int a = 39; int b = 54; int result = a + b; System.out.println(result); } public static void main(String[] args) { printResult(); } }
定义一个测试类,测试通过反射操作注解,如下:
package com.javabasic.annotation; import java.lang.annotation.Annotation; import java.lang.reflect.Method; /** * @Description 测试反射获得注解 * @ClassName Test03 * @Author yuhuofei * @Date 2022/10/2 20:36 * @Version 1.0 */ public class Test03 { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException { Class c = Class.forName("com.javabasic.annotation.TestAnnotation"); //通过反射获得注解 Annotation[] annotations = c.getAnnotations(); for (Annotation annotation : annotations) { System.out.println(annotation); } //获得注解的value MyAnnotation annotation = (MyAnnotation) c.getAnnotation(MyAnnotation.class); String value = annotation.value(); System.out.println(value); //获得类中方法或者属性上指定的注解 System.out.println("============分割线==========="); Method method = c.getMethod("printResult", null); MyAnnotation an = method.getAnnotation(MyAnnotation.class); System.out.println(an); System.out.println(an.value()); } }
运行结果:
到此这篇关于Java中反射的应用的文章就介绍到这了,更多相关Java反射内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!