进阶Java编程(10)反射与简单Java类
1,传统属性自动赋值弊端
简单Java类主要由属性构成,并且提供有setter与getter类,同时简单Java类最大的特征就是通过对象保存相应的类属性的内容。但是如果使用传统的简单Java类开发,那么也会面临非常麻烦的困难。
·范例:传统的简单Java类操作
1 package cn.demo11.demo;
2 class Emp{
3 private String ename;
4 private String job;
5 //setter、getter略
6 }
特别强调,为了方便理解,本次Emp类之中定义的ename、job两个属性都是使用String类型。按照传统的做法此时应该首先实例化Emp类的对象,而后通过实例化对象进行setter方法的调用以设置内容。
·范例:传统的调用
1 public static void main(String[] args) {
2 Emp emp=new Emp();
3 emp.setEname("万雨");
4 emp.setJob("Java开发工程师");
5 System.out.print("姓名:"+emp.getEname()+",职位:"+emp.getJob());
6 }
7 }
8 /*输出
9 姓名:万雨,职位:Java开发工程师
10 */
11
在整个进行Emp对象实例化并设置数据的操作过程之中,设置数据的部分是最麻烦的,你可以想象一下,如果Emp类里面提供有50个属性,那么对于整个程序将会成为一堆的setter方法的调用。或者再进一步说明,在实际开发之中,简单Java类的个数是非常多的,那么如果所有的简单Java列都牵扯到属性的赋值的时候,这种情况下代码编写的重复性将会非常高!!!
按照传统的直观的编程方法,所带来的问题就是代码会存在有大量的重复操作,如果想要解决对象的重复处理操作那么唯一的解决方案就是【反射机制】,反射机制最大的特定就是可以根据自身的特点【Object类直接操作、可以直接操作属性和方法】实现相同功能类的重复操作的抽象处理。
2,属性自动赋值实现思路
经过分析之后已经确认了当前简单Java类操作的问题所在,而对于开发这而言就需要想办法通过一种解决方案来实现属性内容的自动设置,那么这个时候设置强烈建议采用字符串的形式描述对应的类型。
①在进行程序开发的时候String字符串可以描述的内容有很多,并且也可以由开发者自行定义字符串的结构,下面就采用【属性:内容|属性:内容】的形式来为简单Java类进行赋值
②类设计的基本结构:应该由一个专门的ClassInstanceFactory类负责所有的反射处理,即:接收反射对象与要设置的属性内容同时可以获取指定类的实例化对象。
③设计的基本结构:
1 class ClassInstanceFactory{
2 private ClassInstanceFactory(){}//无参构造
3 /**
4 * 实例化对象的创建方法,该对象可以根据传入的字符串结构【属性:内容|属性:内容】
5 * @param clazz 要进行反射实例化的Class类对象,有Class就可以反射实例化对象
6 * @param value 要设置给对象的属性内容
7 * @param <T>
8 * @return 一个已经配置好属性内容的简单Java类对象
9 */
10 public static <T> T create(Class<?> clazz,String value){
11 return null;
12 }
13 }
那么在当前的开发之中,所需要留给用户完善的就是ClassInstanceFactory.create()处理方法。
3,单级属性配置
对于此时的Emp类里面会发现所给出的数据类型都没其他的引用关联了,只是描述了Emp本类的对象,所以这样的设置称为单级设置处理,所以此时应该处理两件事情:
①需要通过反射进行指定类对象的实例化处理;
②进行内容的设置(Field属性类型,方法名称、要设置的内容);
①定义StringUtils实现首字母大写功能
1 package cn.demo13.demo;
2
3 class StringUtils {
4 public static String initcap(String str){
5 if(str==null||"".equals(str)){
6 return str;
7 }
8 if(str.length()==1){
9 return str.toUpperCase();
10 }else{
11 return str.substring(0,1).toUpperCase()+str.substring(1);
12 }
13 }
14 }
②定义BeanUtils工具类,该工具类主要实现属性的设置
1 package cn.demo13.demo;
2
3 import java.lang.reflect.Field;
4 import java.lang.reflect.Method;
5
6 public class BeanUtils {//进行Bean处理类
7 private BeanUtils(){}
8 /**
9 * 实现指定对象的属性设置
10 * @param obj 要进行反射操作的实例化对象
11 * @param value 包含有指定内容的字符串,格式【属性:内容|属性:内容】
12 */
13 public static void setValue(Object obj,String value) {
14 String[] results=value.split("\\|");//字符串拆分
15 for(int x=0;x<results.length;x++){
16 //attval[0]保存的是属性的名称、attval[1]保存的是属性内容
17 String[] attval=results[x].split(":");//获取【属性名称】与【内容】
18 try{
19 Field field=obj.getClass().getDeclaredField(attval[0]);//获取属性成员
20 Method setMethod=obj.getClass().getDeclaredMethod("set"+StringUtils.initcap(attval[0]),field.getType());
21 setMethod.invoke(obj,attval[1]);//调用setter方法设置内容
22 }catch (Exception e){
23 e.printStackTrace();
24 }
25
26 }
27 }
28 }
③ClassInstanceFactroy负责实例化对象并且调用BeanUtils类实现属性内容的设置
1 package cn.demo13.demo;
2
3 class ClassInstanceFactory{
4 private ClassInstanceFactory(){}
5 public static <T> T create(Class<?> clazz,String value){
6 try{//如果想要采用反射进行简单Java类对象属性的设置,类中必须要有无参构造
7 Object obj=clazz.getDeclaredConstructor().newInstance();
8 BeanUtils.setValue(obj,value);//通过反射设置属性
9 return (T) obj;
10 }catch (Exception e){
11 e.printStackTrace();//即便出现错误也不影响后续代码的执行
12 return null;
13 }
14 }
15 }
4,设置多种数据类型
现在已经成功的实现了单级的属性配置,但是这里面依然需要考虑一个实际的情况,我们当前所给定的数据类型只是String,但是在实际的开发之中面对简单Java类中的属性类型一般的可选方案为:long(Long)、int(Integer)、double(Double)、String、Date(日期或者日期时间),所以对于当前的程序代码而言就必须做出修改,可以实现各种数据的类型的配置。
既然要求可以实现不同类型的内容的设置,并且BeanUtils类主要是完成属性赋值处理的,那么就可以在这个类之中追加有一些列的处理方法。
1 package cn.demo13.demo; 2 3 import java.lang.reflect.Field; 4 import java.lang.reflect.Method; 5 import java.text.ParseException; 6 import java.text.SimpleDateFormat; 7 import java.util.Date; 8 9 public class BeanUtils {//进行Bean处理类 10 private BeanUtils(){} 11 /** 12 * 实现指定对象的属性设置 13 * @param obj 要进行反射操作的实例化对象 14 * @param value 包含有指定内容的字符串,格式【属性:内容|属性:内容】 15 */ 16 public static void setValue(Object obj,String value) { 17 String[] results=value.split("\\|");//字符串拆分 18 for(int x=0;x<results.length;x++){ 19 //attval[0]保存的是属性的名称、attval[1]保存的是属性内容 20 String[] attval=results[x].split(":");//获取【属性名称】与【内容】 21 22 try{ 23 Field field=obj.getClass().getDeclaredField(attval[0]);//获取属性成员 24 Method setMethod=obj.getClass().getDeclaredMethod("set"+StringUtils.initcap(attval[0]),field.getType()); 25 Object convertVal=BeanUtils.convertAttributeValue(field.getType().getName(),attval[1]); 26 setMethod.invoke(obj,convertVal);//调用setter方法设置内容 27 }catch (Exception e){ 28 e.printStackTrace(); 29 } 30 31 } 32 } 33 34 /** 35 * 实现属性类型转换处理 36 * @param type 属性类型,通过Field获取 37 * @param value 属性的内容,传入的都是字符串,需要将其变为指定类型 38 * @return 转换后的数据 39 */ 40 private static Object convertAttributeValue(String type,String value){ 41 // System.out.println("type="+type+",value="+value); 42 if("long".equals(type)||"java.lang.Long".equals(type)){//长整型 43 return Long.parseLong(value); 44 }else if("int".equals(type)||"java.lang.int".equals(type)){ 45 return Integer.parseInt(value); 46 }else if("double".equals(type)||"java.lang.double".equals(type)){ 47 return Double.parseDouble(value); 48 }else if("java.util.Date".equals(type)){ 49 50 SimpleDateFormat sdf=null; 51 if(value.matches("\\d{4}-\\d{2}-\\d{2}")){//日期类型 52 sdf=new SimpleDateFormat("yyyy-MM-dd"); 53 }else if(value.matches("\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}")){//有日期 54 sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 55 }else { 56 return new Date(); 57 } 58 try { 59 return sdf.parse(value); 60 } catch (ParseException e) { 61 return new Date(); 62 } 63 }else { 64 return value; 65 } 66 } 67 }
此时只是列举出了几种常见的数据类型,如果想要将其作为一个产品推广,那么就必须要考虑所有可能出现的类型,同时所有可能的日期格式也需要考虑。
5,级联对象实例化
如果说给定的类对象之中存在其他的引用的级联关系的情况下,称为多级设置。例如:一个雇员属于一个部门,一个部门属于一个公司,所以这个时候对于简单Java类的基本关系定义如下:
初始化3个类:Emp、Company、Dept。
public class Emp { private String ename; private String job; private long empno; private double salary; private Date hiredate; private Dept dept;//setter、getter略 }
1 public class Company { 2 private String name; 3 private Date createDate;//setter、getter略 4 }
1 public class Dept { 2 private String dname; 3 private String loc; 4 private Company company;//setter、getter略 5 }
如果要通过Emp进行操作,则应该使用【.】作为级联关系的处理:
dept.dname:财务部——>Emp的实例化对象.getDept().setDname("财务部");
dept.company.name:Mufasa——>Emp的实例化对象.getDpet().getCompany().setName("Mufasa");
考虑到代码的简洁性,所以应该考虑可以通过级联的配置自动实现类中属性的实例化。
1 String value="ename:万雨|empno:7369|job:Java开发工程师|salary:750.00|hiredate:1989-10-10|" 2 +"dept.dname:财务部|dept.company.name:Mufasa";
现在的属性存在多级关系,那么多级的关系就必须与单级的配置区分开。
1 package com.company.mufasa; 2 3 import java.lang.reflect.Field; 4 import java.lang.reflect.Method; 5 import java.text.ParseException; 6 import java.text.SimpleDateFormat; 7 import java.util.Arrays; 8 import java.util.Date; 9 10 public class BeanUtils {//进行Bean处理类 11 private BeanUtils(){} 12 /** 13 * 实现指定对象的属性设置 14 * @param obj 要进行反射操作的实例化对象 15 * @param value 包含有指定内容的字符串,格式【属性:内容|属性:内容】 16 */ 17 public static void setValue(Object obj,String value) { 18 String[] results=value.split("\\|");//字符串拆分 19 for(int x=0;x<results.length;x++){ 20 //attval[0]保存的是属性的名称、attval[1]保存的是属性内容 21 String[] attval=results[x].split(":");//获取【属性名称】与【内容】 22 23 try{ 24 if(attval[0].contains(".")){//多级配置 25 String[] temp=attval[0].split("\\."); 26 Object currentObject=obj; 27 //最后一位肯定是类中的属性名称,所以不在本次实例化处理的范畴之内 28 for (int y=0;y<temp.length-1;y++){//实例化 29 //调用相应的getter方法,如果getter方法返回了null表示该对象为实例化 30 Method getMethod=currentObject.getClass().getDeclaredMethod("get"+StringUtils.initcap(temp[y])); 31 Object tempObject=getMethod.invoke(currentObject); 32 if(tempObject==null){//该对象没有实例化 33 Field field=currentObject.getClass().getDeclaredField(temp[y]);//获取属性类型 34 Method method=currentObject.getClass().getDeclaredMethod("set"+StringUtils.initcap(temp[y]),field.getType()); 35 Object newObject=field.getType().getDeclaredConstructor().newInstance(); 36 method.invoke(currentObject,newObject); 37 currentObject=newObject; 38 }else { 39 currentObject=tempObject; 40 } 41 // System.out.println(temp[y]+"----"+currentObject); 42 } 43 44 45 }else {//单级配置 46 Field field=obj.getClass().getDeclaredField(attval[0]);//获取属性成员 47 Method setMethod=obj.getClass().getDeclaredMethod("set"+StringUtils.initcap(attval[0]),field.getType()); 48 Object convertVal=BeanUtils.convertAttributeValue(field.getType().getName(),attval[1]); 49 setMethod.invoke(obj,convertVal);//调用setter方法设置内容 50 } 51 52 }catch (Exception e){ 53 e.printStackTrace(); 54 } 55 56 } 57 } 58 59 /** 60 * 实现属性类型转换处理 61 * @param type 属性类型,通过Field获取 62 * @param value 属性的内容,传入的都是字符串,需要将其变为指定类型 63 * @return 转换后的数据 64 */ 65 private static Object convertAttributeValue(String type,String value){ 66 // System.out.println("type="+type+",value="+value); 67 if("long".equals(type)||"java.lang.Long".equals(type)){//长整型 68 return Long.parseLong(value); 69 }else if("int".equals(type)||"java.lang.int".equals(type)){ 70 return Integer.parseInt(value); 71 }else if("double".equals(type)||"java.lang.double".equals(type)){ 72 return Double.parseDouble(value); 73 }else if("java.util.Date".equals(type)){ 74 75 SimpleDateFormat sdf=null; 76 if(value.matches("\\d{4}-\\d{2}-\\d{2}")){//日期类型 77 sdf=new SimpleDateFormat("yyyy-MM-dd"); 78 }else if(value.matches("\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}")){//有日期 79 sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 80 }else { 81 return new Date(); 82 } 83 try { 84 return sdf.parse(value); 85 } catch (ParseException e) { 86 return new Date(); 87 } 88 }else { 89 return value; 90 } 91 } 92 }
这些自动的级联配置的实例化处理操作,在以后进行项目的编写之中一定会使用到。
6,级联属性赋值
现在已经实现级联的实例化处理,随后要解决级联的属性设置问题。在进行级联实例化处理的时候循环都是少了一位的。
1 for (int y=0;y<temp.length-1;y++){//实例化 2 //调用相应的getter方法,如果getter方法返回了null表示该对象为实例化 3 Method getMethod=currentObject.getClass().getDeclaredMethod("get"+StringUtils.initcap(temp[y])); 4 Object tempObject=getMethod.invoke(currentObject); 5 if(tempObject==null){//该对象没有实例化 6 Field field=currentObject.getClass().getDeclaredField(temp[y]);//获取属性类型 7 Method method=currentObject.getClass().getDeclaredMethod("set"+StringUtils.initcap(temp[y]),field.getType()); 8 Object newObject=field.getType().getDeclaredConstructor().newInstance(); 9 method.invoke(currentObject,newObject); 10 currentObject=newObject; 11 }else { 12 currentObject=tempObject; 13 } 14 } 15 }
当此时代码循环处理完成之后,currentObject表示的就是可以进行setter方法调用的对象了,并且理论上该对象一定不可能为null,随后就可以按照之前的方式利用对象进行setter调用。
·范例:实现对象的级联属性设置
1 package com.company.mufasa; 2 3 import java.lang.reflect.Field; 4 import java.lang.reflect.Method; 5 import java.text.ParseException; 6 import java.text.SimpleDateFormat; 7 import java.util.Arrays; 8 import java.util.Date; 9 10 public class BeanUtils {//进行Bean处理类 11 private BeanUtils(){} 12 /** 13 * 实现指定对象的属性设置 14 * @param obj 要进行反射操作的实例化对象 15 * @param value 包含有指定内容的字符串,格式【属性:内容|属性:内容】 16 */ 17 public static void setValue(Object obj,String value) { 18 String[] results=value.split("\\|");//字符串拆分 19 for(int x=0;x<results.length;x++){ 20 //attval[0]保存的是属性的名称、attval[1]保存的是属性内容 21 String[] attval=results[x].split(":");//获取【属性名称】与【内容】 22 23 try{ 24 if(attval[0].contains(".")){//多级配置 25 String[] temp=attval[0].split("\\."); 26 Object currentObject=obj; 27 //最后一位肯定是类中的属性名称,所以不在本次实例化处理的范畴之内 28 for (int y=0;y<temp.length-1;y++){//实例化 29 //调用相应的getter方法,如果getter方法返回了null表示该对象为实例化 30 Method getMethod=currentObject.getClass().getDeclaredMethod("get"+StringUtils.initcap(temp[y])); 31 Object tempObject=getMethod.invoke(currentObject); 32 if(tempObject==null){//该对象没有实例化 33 Field field=currentObject.getClass().getDeclaredField(temp[y]);//获取属性类型 34 Method method=currentObject.getClass().getDeclaredMethod("set"+StringUtils.initcap(temp[y]),field.getType()); 35 Object newObject=field.getType().getDeclaredConstructor().newInstance(); 36 method.invoke(currentObject,newObject); 37 currentObject=newObject; 38 }else { 39 currentObject=tempObject; 40 } 41 42 } 43 Field field=currentObject.getClass().getDeclaredField(temp[temp.length-1]);//获取成员 44 Method setMethod=currentObject.getClass().getDeclaredMethod("set"+StringUtils.initcap(temp[temp.length-1]),field.getType()); 45 Object convertVal=BeanUtils.convertAttributeValue(field.getType().getName(),attval[1]); 46 setMethod.invoke(currentObject,convertVal);//调用setter方法设置内容 47 }else {//单级配置 48 Field field=obj.getClass().getDeclaredField(attval[0]);//获取属性成员 49 Method setMethod=obj.getClass().getDeclaredMethod("set"+StringUtils.initcap(attval[0]),field.getType()); 50 Object convertVal=BeanUtils.convertAttributeValue(field.getType().getName(),attval[1]); 51 setMethod.invoke(obj,convertVal);//调用setter方法设置内容 52 } 53 54 }catch (Exception e){ 55 e.printStackTrace(); 56 } 57 58 } 59 } 60 61 /** 62 * 实现属性类型转换处理 63 * @param type 属性类型,通过Field获取 64 * @param value 属性的内容,传入的都是字符串,需要将其变为指定类型 65 * @return 转换后的数据 66 */ 67 private static Object convertAttributeValue(String type,String value){ 68 // System.out.println("type="+type+",value="+value); 69 if("long".equals(type)||"java.lang.Long".equals(type)){//长整型 70 return Long.parseLong(value); 71 }else if("int".equals(type)||"java.lang.int".equals(type)){ 72 return Integer.parseInt(value); 73 }else if("double".equals(type)||"java.lang.double".equals(type)){ 74 return Double.parseDouble(value); 75 }else if("java.util.Date".equals(type)){ 76 77 SimpleDateFormat sdf=null; 78 if(value.matches("\\d{4}-\\d{2}-\\d{2}")){//日期类型 79 sdf=new SimpleDateFormat("yyyy-MM-dd"); 80 }else if(value.matches("\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}")){//有日期 81 sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 82 }else { 83 return new Date(); 84 } 85 try { 86 return sdf.parse(value); 87 } catch (ParseException e) { 88 return new Date(); 89 } 90 }else { 91 return value; 92 } 93 } 94 }
在以后的简单Java类之中简单Java类的幅值将不再重复调用setter方法操作完成,而这种处理形式是在正轨开发中普遍采用的方式。