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 }