Java系列之反射
最近知识比较零碎,不适合整理成文,来一篇以前的关于反射的学习笔记,主要内容如下:
反射机制
反射获取类的信息
反射操作类的信息
反射获取泛型
反射获取注解信息
反射机制
Java 的反射机制是指在运行状态下,对于任意一个类,都能够知道这个类的所有属性和方法,反射是一种可在代码运行时动态获取类的信息的一种机制,可通过反射获取在编译期不可能获取到的类的信息,当一个任意类被类加载器(ClassLoader)首次加载之后会自动生成一个该类对应的 Class 对象,这个 Class 对象保存了对应类的所有信息。这种当一个任意类被类加载器加载之后,动态获取 Class 对象的信息以及动态操作 Class 对象的属性和方法的功能称之为 Java 的反射机制。Java 反射机制的关键是获取某个任意类的 Class 对象,下面的内容就是如何通过这个 Class 对象获取和动态操作类的相关信息,为了便于后文中对反射的使用,这里先创建一个 User 类供下文中使用,具体如下:1package com.manu.reflection.bean;
2
3/**
4 * 反射测试类
5 */
6public class User {
7
8 private int id;
9 private String name;
10 private String password;
11
12 public User() {
13 super();
14 }
15 public User(int id, String name, String password) {
16 super();
17 this.id = id;
18 this.name = name;
19 this.password = password;
20 }
21 public int getId() {
22 return id;
23 }
24 public void setId(int id) {
25 this.id = id;
26 }
27 public String getName() {
28 return name;
29 }
30 public void setName(String name) {
31 this.name = name;
32 }
33 public String getPassword() {
34 return password;
35 }
36 public void setPassword(String password) {
37 this.password = password;
38 }
39 @Override
40 public String toString() {
41 return "User [id=" + id + ", name=" + name + ", password=" + password + "]";
42 }
43
44}
反射获取类的信息
这里总结一下如何获取类的基本信息,如类的构造方法、属性、方法等,可通过某个类对应的 Class 对象对应的 getter 方法获取某个类的名称、构造方法、属性、方法等,下面以获取某个类的构造方法为例说明获取方式不同:getConstructors:表示获取某个类中修饰符为 public 的所有构造方法,如 getFields()、getMethods() 还包括从父类继承来的 public 修饰的属性和方法。
getDeclaredConstructors:表示获取某个类中所有声明的构造方法,只限定于本类中。
1/**通过反射获取某个类的相关信息主要如上,来上述代码的执行结果如下:
2 * 反射获取类的信息
3 */
4private static void getReflectClassInfo() {
5 try {
6 //获取某个类的Class对象
7 String name = "com.manu.reflection.bean.User";
8 Class clazz = Class.forName(name);
9
10 //反射获取类的名称
11 System.out.println("----------反射获取类的名称----------");
12 String n1 = clazz.getName();//完整路径:包名+类名 (com.manu.reflection.bean.User)
13 String n2 = clazz.getSimpleName();//类名(User)
14 System.out.println("获取类的名称n1:"+n1);
15 System.out.println("获取类的名称n2:"+n2);
16
17 //反射获取类的构造方法
18 System.out.println("----------反射获取类的构造方法----------");
19 Constructorc1 = clazz.getDeclaredConstructor(null);
20 System.out.println("获取无参构造方法:"+c1);
21 Constructorc2 = clazz.getDeclaredConstructor(int.class,String.class,String.class);
22 System.out.println("获取有参构造方法:"+c2);
23 Constructor[] constructors = clazz.getDeclaredConstructors();
24 for(Constructor c: constructors) {
25 System.out.println("获取所有的构造方法:"+c);
26 }
27
28 //反射获取类的属性
29 System.out.println("----------反射获取类的属性----------");
30 Field f1 = clazz.getDeclaredField("name");
31 System.out.println("获取名称为name的属性:"+f1);
32 Field[] fields = clazz.getDeclaredFields();
33 for(Field f : fields) {
34 System.out.println("获取所有的属性:"+f);
35 }
36
37 //反射获取类的方法
38 System.out.println("----------反射获取类的方法----------");
39 Method m1 = clazz.getDeclaredMethod("getName", null);//获取无参方法
40 Method m2 = clazz.getDeclaredMethod("setName", String.class);//获取有参方法
41 System.out.println("获取方法名为getName的方法m1:"+m1);
42 System.out.println("获取方法名为setName的方法m2:"+m2);
43 Method[] mathods = clazz.getDeclaredMethods();
44 for(Method m: mathods) {
45 System.out.println("获取所有方法:"+m);
46 }
47
48 } catch (Exception e) {
49 e.printStackTrace();
50 }
51}
1----------反射获取类的名称----------
2获取类的名称n1:com.manu.reflection.bean.User
3获取类的名称n2:User
4----------反射获取类的构造方法----------
5获取无参构造方法:public com.manu.reflection.bean.User()
6获取有参构造方法:public com.manu.reflection.bean.User(int,java.lang.String,java.lang.String)
7获取所有的构造方法:public com.manu.reflection.bean.User()
8获取所有的构造方法:public com.manu.reflection.bean.User(int,java.lang.String,java.lang.String)
9----------反射获取类的属性----------
10获取名称为name的属性:private java.lang.String com.manu.reflection.bean.User.name
11获取所有的属性:private int com.manu.reflection.bean.User.id
12获取所有的属性:private java.lang.String com.manu.reflection.bean.User.name
13获取所有的属性:private java.lang.String com.manu.reflection.bean.User.password
14----------反射获取类的方法----------
15获取方法名为getName的方法m1:public java.lang.String com.manu.reflection.bean.User.getName()
16获取方法名为setName的方法m2:public void com.manu.reflection.bean.User.setName(java.lang.String)
17获取所有方法:public java.lang.String com.manu.reflection.bean.User.toString()
18获取所有方法:public java.lang.String com.manu.reflection.bean.User.getName()
19获取所有方法:public int com.manu.reflection.bean.User.getId()
20获取所有方法:public void com.manu.reflection.bean.User.setName(java.lang.String)
21获取所有方法:public java.lang.String com.manu.reflection.bean.User.getPassword()
22获取所有方法:public void com.manu.reflection.bean.User.setId(int)
23获取所有方法:public void com.manu.reflection.bean.User.setPassword(java.lang.String)
反射操作类的信息
通过 Java 的反射机制可以获取的某个类的构造方法、属性以及方法,然后就可以对该类进行相关操作了,下面是将通过 Java 的反射机制构建该类的对象、操作该类对象的属性以及调用该类对象的方法,具体参考如下:1/**上述代码的执行结果如下:
2 * 反射操作类的信息
3 */
4private static void setReflectClassInfo() {
5 try {
6 //获取某个类的Class对象
7 String name = "com.manu.reflection.bean.User";
8 Class clazz = Class.forName(name);
9
10 //反射操作类的构造方法
11 System.out.println("----------反射操作类的构造方法----------");
12 Constructorc1 = clazz.getDeclaredConstructor(null);//获取无参构造方法
13 Constructorc2 = clazz.getDeclaredConstructor(int.class,String.class,String.class);//获取带参构造方法
14 User u1 = c1.newInstance();
15 User u2 = c2.newInstance(1000,"jzman-blog","111111");
16 System.out.println("u1:"+u1);
17 System.out.println("u2:"+u2);
18
19 //反射操作类的属性
20 System.out.println("----------反射操作类的属性----------");
21 User u3 = c1.newInstance();
22 Field field = clazz.getDeclaredField("name");
23 field.setAccessible(true);//设置该属性不需要安全检查,可直接放访问
24 field.set(u3, "jzman");//反射设置User对象的name属性值
25 System.out.println("u3:"+u3);
26 System.out.println("获取User对象u3的name属性值:"+field.get(u3));
27
28 //反射操作类的方法
29 System.out.println("----------反射操作类的方法----------");
30 User u4 = c1.newInstance();
31 Method method = clazz.getDeclaredMethod("setPassword", String.class);
32 method.invoke(u4, "222222");//设置User对象u4的password属性,等同于u4.setPassword("222222);
33 System.out.println("u4:"+u4);
34
35 } catch (Exception e) {
36 e.printStackTrace();
37 }
38}
1----------反射操作类的构造方法----------实际开发中肯定会遇到某个组件中需要某个属性,但是该属性又是私有的,这时候就可以使用 Java 的反射机制了。
2u1:User [id=0, name=null, password=null]
3u2:User [id=1000, name=jzman-blog, password=111111]
4----------反射操作类的属性----------
5u3:User [id=0, name=jzman, password=null]
6获取User对象u3的name属性值:jzman
7----------反射操作类的方法----------
8u4:User [id=0, name=null, password=222222]
反射操作泛型
Java 采用泛型擦除的机制,Java 中的泛型仅仅是给编译器 javac 使用的,这样可确保数据的安全性的免去强制类型转换的麻烦,当编译完成之后,所有和泛型相关的类型将会被全部擦除,为了能够使用反射操作这些类型,新增了四种类型 GenericArrayType、ParameterizedType、TypeVariable 和 WildcardType 来表示不能被归一到 Class 类中的类型但又是和原始类型齐名的类型,也就是说正常的能够获取对应的 Class 对象的 Type 还是 Class 对象,如基本数据类型。反射操作泛型就要涉及到 Java 中的 Type, 在 Java 中 Type 表示所有类型的公共接口,这些类型包括原始类型、参数化类型、数组类型、类型变量和基本类型,其声明如下:1/**Type 有四个直接子接口,具体含义如下:
2 * Type 是 Java 语言中所有类型的公共接口
3 * 这些类型包括原始类型、参数化类型、数组类型、类型变量和基本类型
4 */
5public interface Type {
6 default String getTypeName() {
7 return toString();
8 }
9}
1GenericArrayType:表示泛型数组类型,如 ArraryList下面以 GenericArrayType 和 ParameterizedType 为例来说明如何使用反射来操作泛型类型,具体如下:[] listArrary
2ParameterizedType:表示参数化类型(泛型),如 ArrayListlist
3TypeVariable:表示类型变量,如T
4WildcardType:表示一个通配符类型,如 ?、? extends Number 或 ? super Integer
1//用来测试获取泛型的属性上述代码中方法 refectGenericType 的执行结果如下:
2private String[] array;
3private List[] listArray;
4//用来测试获取泛型的方法
5private String testGenericType(Mapmap, String[] array, int name,User user) {
6 System.out.println("testGenericType");
7 return null;
8}
9
10//通过反射获取泛型
11private static void refectGenericType() {
12 try {
13 System.out.println("---------GenericArrayType---------");
14
15 //获取泛型数组(GenericArrayType)
16 Field field = ReflectTest02.class.getDeclaredField("listArray");//获取属性listArray
17 GenericArrayType type = (GenericArrayType) field.getGenericType();
18 System.out.println("获取泛型数组:"+type);
19
20 System.out.println("---------ParameterizedType---------");
21
22 //获取参数化类型(泛型)(ParameterizedType)
23 Method method = ReflectTest02.class.getDeclaredMethod("testGenericType", Map.class,String[].class,int.class,User.class);
24 Type[] types = method.getGenericParameterTypes();//获得方法参数类型
25 for(Type type1: types) {
26 System.out.println("方法参数类型:"+type1);
27 if(type1 instanceof ParameterizedType) {
28 System.out.println("ParameterizedType:"+type1);
29 }
30 }
31
32 } catch (Exception e) {
33 e.printStackTrace();
34 }
35}
1---------GenericArrayType---------可参照代码查看对应的输出结果。
2获取泛型数组:java.util.List[]
3---------ParameterizedType---------
4方法参数类型:java.util.Map
5ParameterizedType:java.util.Map
6方法参数类型:class [Ljava.lang.String;
7方法参数类型:int
8方法参数类型:class com.manu.reflection.bean.User
9
反射获取注解信息
通过反射还可以获取注解信息,记如果对注解比较陌生可以参考之前分享的一篇文章 Java 系列之注解,这里简单模仿数据库表字段与 Java 对象的属性是如何通过注解信息一一对应的,为什么我们使用一些数据库框架的时候,通过一些表注解、字段注解就能够创建表,这里就涉及到使用反射来获取注解信息来生成 SQL,进而生成对应的表。创建两个注解分别作为表注解和字段注解,参考如下:1//表注解然后,创建一个类并使用该注解,参考如下:
2@Target(ElementType.TYPE)
3@Retention(RetentionPolicy.RUNTIME)
4public @interface TableAnnotation {
5 String value();
6}
7
8//字段注解
9@Target(ElementType.FIELD)
10@Retention(RetentionPolicy.RUNTIME)
11public @interface FieldAnnotation {
12 String column();
13 int length();
14 String type();
15}
1/**最后,获取注解信息,参考如下:
2 * 反射读取注解信息测试Bean
3 * @author jzman
4 */
5
6@TableAnnotation(value = "student_table")
7public class Student {
8 @FieldAnnotation(column = "uid", length = 20, type = "int")
9 private int sId;
10 @FieldAnnotation(column = "name", length = 10, type = "varchar")
11 private String sName;
12 @FieldAnnotation(column = "uiaged", length = 3, type = "varchar")
13 private int sAge;
14
15 //setter、getter方法
16 //...
17}
1/**上述代码的执行结果如下:
2 * 反射获取注解信息
3 * @author jzman
4 */
5public class ReflectTest03 {
6 public static void main(String[] args) {
7 try {
8 Class clazz = Class.forName("com.manu.reflection.bean.Student");
9
10 //反射获取类的注解信息
11 TableAnnotation tableAnnotation = (TableAnnotation) clazz.getAnnotation(TableAnnotation.class);
12 System.out.println("反射获取类的注解信息:"+tableAnnotation);
13
14 //反射获取属性的注解信息
15 Field field = clazz.getDeclaredField("sName");
16 FieldAnnotation fieldAnnotation = field.getAnnotation(FieldAnnotation.class);
17 System.out.println("反射获取属性的注解信息:"+fieldAnnotation);
18
19 //获取其他注解信息使用方式类似
20 //...
21 } catch (Exception e) {
22 e.printStackTrace();
23 }
24 }
25}
1反射获取类的注解信息:@com.manu.reflection.TableAnnotation(value=student_table)显然,通过反射获取到了相应的注解信息,这些注解信息标注了该类对应数据库表的一些关键信息,然后就可以生成对应的 SQL 语句,这样就不难理解数据库表字段与Java对象属性的映射关系了。使用反射获取私有属性或私有方法是必须设置 setAccessible 为 true 才能跳过 Java 安全检查,从而获取私有的属性、方法等,同时设置 setAccessible 为 true 在一定程度上可以提高反射的运行速度。推荐阅读:
2反射获取属性的注解信息:@com.manu.reflection.FieldAnnotation(column=name, length=10, type=varchar)
评论