java反射机制学习

java的反射机制是Java特性之一,反射机制是构建框架技术的基础所在。灵活掌握Java反射机制,对大家以后学习框架技术有很大的帮助

1、什么是反射

只要提供引用对象或者代表类的字符串信息,就可以得到该对象或者该字符串对应的类型信息,利用这些信息,在运行时候动态去调用类里面的各种方法和各种属性;换句话说只要有类的实例对象或者类的完整路径信息,就可以通过反射获得该类所有的属性、方法、构造方法,并使用;所以,反射就会突出下面两个特点:

1)灵活性高:编译的时候都不清楚会生成哪个类的对象,直到运行调用的时候,才知道哪个类;

2)牺牲效率:一般正常的jvm工作是将.java编译成.class运行,反射则是将.class变成.java,读取相关信息后,再编译运行;

2、反射的实现

第一步:首先获得class类对象:有两种方法

  ①通过对象:class类对象.getClass()

  ②通过类名(路径):Class.forName(String className)

第二步:通过获得的class类对象获取属性、方法

  获得private属性:class类对象. getDeclaredFields()

  获得普通属性:class类对象. getFields()

  获得构造方法组:Constructor[] 数组名 = class类对象.getConstructors()

  获得方法组:Method[] 数组名 = class类对象.getMethods()

第三步:通过构造方法创建对象

  1)调用无参构造方法(下面c代表Class类对象

    先通过Class类对象获得无参构造方法对象(只要对象就可以,不要数组):

      Constructor constructor=c.getConstructor();

    再利用构造方法对象的newInstance()创建出实例

      Object obj = constructor.newInstance();

  2) 调用有参构造方法创建对象(指定参数类型、个数、顺序)

    先通过class类对象获得有参构造方法对象,还要知道有参参数类型、个数:

      Constructor constructor = c.getConstructor(包装类1.class, 包装类2.class,...)

       再利用构造方法对象的newInstance(对应类型赋值)创建出实例

      Object obj = constructor.newInstance(赋值);

另外:在获得对象的基础上,可以调用里面任意方法

如下一个简单例子:

新建一个User类,这边我是放在fanshe包下面(反射的一般都要是包装类型,即UserInfo里面属性及方法参数类型

 1 package fanshe;
 2 
 3 public class User {
 4     private Integer id; // 包装类
 5     private String name;
 6     public Integer age;
 7 
 8     public Integer getId() {
 9         return id;
10     }
11 
12     public Integer getAge() {
13         return age;
14     }
15 
16     public void setAge(Integer age) {
17         this.age = age;
18     }
19 
20     public void setId(Integer id) {
21         this.id = id;
22     }
23 
24     public String getName() {
25         return name;
26     }
27 
28     public void setName(String name) {
29         this.name = name;
30     }
31 
32     // 无参构造
33     public User() {
34         // TODO Auto-generated constructor stub
35     }
36 
37     // 有参构造
38     public User(Integer id, String name) { // 参数包装类
39         this.id = id;
40         this.name = name;
41     }
42 
43     public String toString() {
44         System.out.println("重写toString");
45         return "id:" + this.id + ":" + "用户名:" + this.name;
46     }
47 
48     public void show() {
49         System.out.println(this.toString());
50     }
51 
52     public Double getSalary() {
53         System.out.println("无参有返回值");
54         return (double) (100 * id);
55     }
56 
57     public Double getSalay(int year, double baseSalary) {
58         System.out.println("有参有返回值");
59         return year * baseSalary;
60     }
61 }

1)获取属性、方法、构造方法 java代码

 1 package fanshe;
 2 
 3 import java.lang.reflect.Constructor;
 4 import java.lang.reflect.Field;
 5 import java.lang.reflect.Method;
 6 
 7 import org.junit.Test;
 8 
 9 public class FansheTest {
10     @Test
11     public void getInfo() {
12         // 已知类,先获得实例对象
13         User user = new User();
14         // 获得class类对象
15         Class c = user.getClass();
16         // 利用 class类对象 得到属性数组
17         Field[] fieldArr = c.getDeclaredFields();// 这个属性可以得到包括访问权限为
18         for(Field field : fieldArr) {
19             System.out.println("所有属性:" + field.getName());
20         }
21         Field[] fields = c.getFields();// 这个属性可以得到公有属性
22         for(Field field : fields) {
23             System.out.println("public属性:" + field.getName());
24         }
25         // 利用 class类对象 得到构造方法(每个类至少有一个默认的无参构造方法)
26         Constructor[] constructorArr = c.getConstructors();
27         System.out.println("有" + constructorArr.length + "个构造方法:");
28         // 利用 class类对象 得到普通方法
29         Method[] methodArr = c.getMethods();
30         for(Method method : methodArr) {
31             System.err.println("方法名:" + method.getName());
32         }
33 
34     }
35 
36 }

执行结果

 1 所有属性:id
 2 所有属性:name
 3 所有属性:age
 4 public属性:age
 5 有2个构造方法:
 6 方法名:toString
 7 方法名:getName
 8 方法名:getId
 9 方法名:setName
10 方法名:getAge
11 方法名:getSalay
12 方法名:show
13 方法名:setAge
14 方法名:getSalary
15 方法名:setId
16 方法名:wait
17 方法名:wait
18 方法名:wait
19 方法名:equals
20 方法名:hashCode
21 方法名:getClass
22 方法名:notify
23 方法名:notifyAll

2)利用反射得到实例对象:已知类的路径信息:fanshe.User,

分析无参:获得class类对象→通过class类对象获得无参构造方法→利用构造方法的newInstance方法创建出实例

java代码

 1 // 反射获得实例对象
 2     @Test
 3     public void getObject() throws Exception {
 4         String className = "fanshe.User";
 5         // 得到class对象
 6         Class clazz = Class.forName(className);
 7 
 8         // 先得到无参构造方法对象,不是构造方法数组
 9         // Constructor constructor = clazz.getConstructor();
10         // 利用构造方法对象的newInstance()创建出user实例
11         // Object user = constructor.newInstance();//无参默认就有
12 
13         // 也可以用有参构造(前提是User里面要有这个构造方法)
14         Constructor constructor = clazz.getConstructor(Integer.class, String.class, Integer.class);
15         Object user = constructor.newInstance(1, "zm", 25);
16         System.out.println(user);
17     }

执行结果

1 重写toString
2 id:1:用户名:zm

3)利用反射调用类里面的普通方法

3.1)利用反射调用无参无返回值的普通方法

已知:已知类的路径信息:fanshe.User,要调用的方法名:show

java代码

 1 // 利用反射调用普通方法
 2     @Test
 3     public void getMethod() {
 4         // 通过反射调用无参无返回值的普通方法
 5         // 已知含有类名字符串、方法名
 6         String className = "fanshe.User";
 7         String methodName = "show";
 8         try {
 9             // 通过含有类名字符串得到class对象
10             Class clazz = Class.forName(className);
11             // 通过class对象创建出有参的构造方法Constructor对象
12             Constructor constructor = clazz.getConstructor(Integer.class, String.class,Integer.class);
13             // 通过有参构造方法对象创建出实例对象
14             Object user = constructor.newInstance(2, "hzm",26);
15             // 通过class对象创建出方法Method对象
16             // 提供方法名,要调用无参所以没有参数
17             Method method = clazz.getMethod(methodName);
18             // 调用指定的对象要执行指定的无参方法:
19             // user是根据字符串实例出对象,method是根据方法名实例出的对象
20             Object object = method.invoke(user);
21             System.out.println(object);
22         } catch(Exception e) {
23             // TODO Auto-generated catch block
24             e.printStackTrace();
25         }
26 
27     }

执行结果

1 重写toString
2 id:2:用户名:hzm

3.2)利用反射调用返回值的普通方法(这个主要是展示另外一种创建实例对象的方式)

java代码

 1 package fanshe;
 2 
 3 import java.lang.reflect.Method;
 4 
 5 import org.junit.Test;
 6 
 7 public class FansheTest {
 8 
 9     @Test
10     public void getMethod1() {
11         // 已知含有类名字符串、方法名
12         String className = "fanshe.User";
13         String methodName = "getSalay";
14         try {
15             // 通过含有类名字符串得到class对象
16             Class clazz = Class.forName(className);
17             // 创建实例对象的另外一种方式
18             // 利用class对象直接clazz.newInstance()(默认无参构造)
19             Object user = clazz.newInstance();
20             // 通过class对象创建出方法Method对象
21             // 提供方法名,要调用无参所以没有参数
22             Method method = clazz.getMethod(methodName, int.class, double.class);
23             // 调用指定的对象要执行指定的无参方法:
24             // user是根据字符串实例出对象,method是根据方法名实例出的对象
25             Object object = method.invoke(user, 3, 1000);
26             System.out.println(object);
27         } catch(Exception e) {
28             // TODO Auto-generated catch block
29             e.printStackTrace();
30         }
31 
32     }
33 }

执行结果

1 有参有返回值
2 3000.0

4)反射得到get/set方法

java代码

 1 // 反射得到get/set方法
 2     @Test
 3     public void getOrSet() {
 4         // 首先用反射生成UserInfo实例对象,调用的是有参的构造方法
 5 
 6         // 已知含有类名字符串、方法名
 7         String className = "fanshe.User";
 8         try {
 9             // 通过含有类名字符串得到class对象
10             Class clazz = Class.forName(className);
11             // 得到类名:User
12             System.out.println(clazz.getSimpleName());
13             // 通过class对象创建出有参构造方法
14             Constructor con = clazz.getConstructor(Integer.class, String.class,Integer.class);
15             Object user = con.newInstance(3, "hong",27);
16             // 通过get方法显示出所有属性的值
17             // 得到所有属性对象
18             Field[] fields = clazz.getDeclaredFields();
19             // 遍历fields
20             for(Field field : fields) {
21                 // PropertyDescriptor(属性名,class对象)
22                 PropertyDescriptor pd = new PropertyDescriptor(field.getName(), clazz);
23                 // 利用pd对象得到get对象方法getReadMethod()
24                 Method getMethod = pd.getReadMethod();
25                 System.out.println(getMethod);
26                 // invoke(实例对象)可以获得属性值
27                 System.out.println("属性名:" + field.getName() + " 属性值:" + getMethod.invoke(user));
28             }
29         } catch(Exception e) {
30 
31         }
32 
33     }

执行结果

1 User
2 public java.lang.Integer fanshe.User.getId()
3 属性名:id 属性值:3
4 public java.lang.String fanshe.User.getName()
5 属性名:name 属性值:hong
6 public java.lang.Integer fanshe.User.getAge()
7 属性名:age 属性值:null

反射的应用 

处理数据新增:实现通用版本的新增处理,默认约定:类名与表名一致,属性与列名一致,最大多是大小写区别,默认自增列为id,仅适用于类与表结构完全一致;

 1 package fanshe;
 2 
 3 import java.beans.PropertyDescriptor;
 4 import java.lang.reflect.Field;
 5 import java.lang.reflect.Method;
 6 
 7 public class TransaSql {
 8     /***
 9      * 实现通用版本的新增处理 该方法使用注意事项:默认的约定:类名与表名一致,最多就是大小写区别, 属性名与列名一致,自增列默认名称为id
10      * 目前仅适用于类与表结构完全一致的情况
11      * 
12      * @param obj——就是要保存到数据库的对象
13      * @return >0表示新增成功,<=0表示没有新增成功
14      * 
15      */
16     public static int doAdd(Object object) {
17         int i = 0;
18         // 关键点是sql语句的构造
19         // 构造的关键点1表名/2列名/3值
20         Class clazz = object.getClass();
21         // 得到表名
22         String tableName = clazz.getSimpleName();
23         String sql = " insert into " + tableName + "(";
24         String colStr = "";// 保存列名
25         String valStr = "";// 保存值
26         // 接下来遍历得到列名,就先要得到属性名
27         Field[] fields = clazz.getDeclaredFields();
28         Object[] objArr = new Object[fields.length - 1];
29         // 增加一个变量用来指示数组下标
30         int arrIndex = 0;
31         try {
32             for(Field field : fields) {
33                 String colName = field.getName();// 保存字段名
34                 if("id".equals(colName.toLowerCase()) == false) {
35                     colStr += colName + ",";// 构造列名
36                     valStr += "?,";// 构造值
37                     // 得到相应的值
38                     PropertyDescriptor pd = new PropertyDescriptor(field.getName(), clazz);
39                     // 利用pd对象得到get对象
40                     Method getMethod = pd.getReadMethod();
41                     objArr[arrIndex] = getMethod.invoke(object);
42                     arrIndex++;
43                 }
44             }
45             // 遍历完毕,构造出sql
46             colStr = colStr.substring(0, colStr.length() - 1);
47             valStr = valStr.substring(0, valStr.length() - 1);
48             sql += colStr + ")";
49             sql += " values(" + valStr + ")";
50             System.out.println(sql);
51             // 省略数据jdbc部分
52         } catch(Exception e) {
53             // TODO Auto-generated catch block
54             e.printStackTrace();
55         }
56 
57         return i;
58     }
59 }
作者:howtosay
         
放牛娃的个人笔记整理,每天记录一点点,进步一点点
原文地址:https://www.cnblogs.com/hongzm/p/8276994.html