1,傳統屬性自動賦值弊端
簡單Java類主要由屬性構成,而且提供有setter與getter類,同時簡單Java類最大的特徵就是經過對象保存相應的類屬性的內容。可是若是使用傳統的簡單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方法的調用以設置內容。工具
·範例:傳統的調用spa
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類直接操做、能夠直接操做屬性和方法】實現相同功能類的重複操做的抽象處理。code
2,屬性自動賦值實現思路
通過分析以後已經確認了當前簡單Java類操做的問題所在,而對於開發這而言就須要想辦法經過一種解決方案來實現屬性內容的自動設置,那麼這個時候設置強烈建議採用字符串的形式描述對應的類型。orm
①在進行程序開發的時候String字符串能夠描述的內容有不少,而且也能夠由開發者自行定義字符串的結構,下面就採用【屬性:內容|屬性:內容】的形式來爲簡單Java類進行賦值對象
②類設計的基本結構:應該由一個專門的ClassInstanceFactory類負責全部的反射處理,即:接收反射對象與要設置的屬性內容同時能夠獲取指定類的實例化對象。blog
③設計的基本結構:開發
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方法操做完成,而這種處理形式是在正軌開發中廣泛採用的方式。