欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java反射--反射与简单Java类

程序员文章站 2022-05-22 12:42:12
...

内容学习于:edu.aliyun.com


简介:

  简单Java类已经在之前不断研究过了,而且在学习反射的时候针对于简单Java类之中的各个技术的使用与反射的结合也进行了良好的分析,但是这些只是表面上的情况,本次将针对于简单Java类在实际项目中的设计意义进行更加详细的说明(2005 年开始,Java 行业内开始大量的提出基于简单Java类的各种设计方案)。

1. 传统属性赋值弊端

  简单Java类最大的特点在于其主要进行数据的存储,并且不进行任何复杂的业务判断,即:对于所谓的循环、分支等语句实际上在开发里面是不会出现在简单Java类中的,现在假设有如下一个简单Java类。

定义一个雇员类:

public class Emp {
    private String ename;
    private String job;

    public String getEname() {
        return ename;
    }

    public void setEname(String ename) {
        this.ename = ename;
    }

    public String getJob() {
        return job;
    }

    public void setJob(String job) {
        this.job = job;
    }

    @Override
    public String toString() {
        return "Emp{" +
                "ename='" + ename + '\'' +
                ", job='" + job + '\'' +
                '}';
    }
}

定义一个模拟输入类:

public class InputData {
    public static Map<String, String> input() {
        Map<String, String> data = new HashMap<>();
        data.put("ename","张三");
        data.put("job","办事员");
        return data;
    }
}

实现Map集合向Emp对象的数据存储:

public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        Emp emp = new Emp();
        emp.setEname(InputData.input().get("ename"));
        emp.setJob(InputData.input().get("job"));
        System.out.println(emp);
    }
}

结果:

Emp{ename=‘张三’, job=‘办事员’}

  在此时模拟了一个键盘输入的处理操作,其中Map集合中的key 和雇员类中的属性的名称是相同的。
  于是这个时候看起来一切的操作都很完美,但是思考两个问题:

  • 问题一:现在Emp类中是两个属性,但是如果这个类中提供有100个属性的时候,意味着setter编写100次;
  • 问题二:那么如果要想进行一些合理的设计,必然要求其可以满足于所有的简单Java类。

  如下图所示:Java反射--反射与简单Java类

2. 自动赋值实现思路

  现在通过分析可以发现,如果要是将输入返回的Map集合直接转为简单Java类中的操作对象的数据那么是最方便的,但是如何进行转换呢?此刻就必须利用反射机制来实现(只有反射可以实现代码结构的深层次的重用定义)。
  如下图所示:Java反射--反射与简单Java类

程序实现思路:

public class ObjectInstanceFactory {
    private ObjectInstanceFactory(){}

    /**
     * 根据传入的class类型获取指类型的实例化对象,同时可以将传入的属性赋值(错误的属性不赋值)
     * @param clazz 要进行实例化对象的简单Java类型
     * @param value 包含有输入数据的Map集合,集中key和value的类型必须是String
     * @param <T> 根据传入的Class类型获取的一个具体实例
     * @return 带有属性内容的简单Java类对象
     */
    public static <T> T create(Class<?> clazz, Map<String,String> value){
        return null;
    }
}

外部调用形式:

public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        Emp emp = ObjectInstanceFactory.create(Emp.class,InputData.input());
        System.out.println(emp);
        
    }
}

3. 单级属性赋值

  所谓的单级属性指的是该类不存在有其它简单Java类的引用关联,在本类之中只考虑当前的常用类型,为了简化设计,本次先考虑当前类型是String的形式。
  如下图所示:Java反射--反射与简单Java类

定义BeanUtil类:

package com.xzzz.demo.reflect;

import com.xzzz.demo.StringUtil;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.Map;

public class BeanUtil {
    private BeanUtil(){}
    public static void SetValue(Object object, Map<String, String> value){
        Iterator<Map.Entry<String,String>> iterator = value.entrySet().iterator();
        while (iterator.hasNext()){
            Map.Entry<String,String> entry = iterator.next();//获得每组数据
            try {//防止某些成员输入错误造成异常
                Field field = object.getClass().getDeclaredField(entry.getKey());
                //依据传入的(Map-key)获取setter方法,并利用Field设置方法的参数类型
                Method setMethod = object.getClass().getDeclaredMethod("set"+ StringUtil.initcap(entry.getKey()),field.getType());
                setMethod.invoke(object,entry.getValue());//反射调用setter方法并设置属性内容
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

修改ObjectInstanceFactory类:

package com.xzzz.demo.reflect;

import java.util.Map;


public class ObjectInstanceFactory {
    private ObjectInstanceFactory() {
    }

    /**
     * 根据传入的class类型获取指类型的实例化对象,同时可以将传入的属性赋值(错误的属性不赋值)
     *
     * @param clazz 要进行实例化对象的简单Java类型
     * @param value 包含有输入数据的Map集合,集中key和value的类型必须是String
     * @param <T>   根据传入的Class类型获取的一个具体实例
     * @return 带有属性内容的简单Java类对象
     */
    public static <T> T create(Class<?> clazz, Map<String, String> value) {
        Object object = null;
        try {
            //1.在工厂类调用类的无参构造方法进行对象实例化
            object = clazz.getDeclaredConstructor().newInstance();
            //2.利用反射进行内容的设置
            BeanUtil.SetValue(object,value);//赋值交于其他类处理
        } catch (Exception e) {
            e.printStackTrace();
        }
        return (T)object;
    }
}

定义测试类:

package com.xzzz.demo;


import com.xzzz.demo.reflect.ObjectInstanceFactory;

public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        Emp emp = ObjectInstanceFactory.create(Emp.class,InputData.input());
        System.out.println(emp);
    }
}

结果:

Emp{ename=‘张三’, job=‘办事员’}

4. 设置多种数据类型

  在之前的代码里面为了方便起见所有的数据类型都只是简单的考虑到了String 的问题,但是在现实的开发之中,对于使用的数据类型比较常见的为: Integer (int)、 Double (double)、 Long (long)、 Date (日期、日期时间)。
  如下图所示:Java反射--反射与简单Java类

在BeanUtil类定义一个数值转换的方法:

package com.xzzz.demo.reflect;

import com.xzzz.demo.StringUtil;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Iterator;
import java.util.Map;

public class BeanUtil {
    private BeanUtil(){}
    public static void SetValue(Object object, Map<String, String> value){
        Iterator<Map.Entry<String,String>> iterator = value.entrySet().iterator();
        while (iterator.hasNext()){
            Map.Entry<String,String> entry = iterator.next();//获得每组数据
            try {//防止某些成员输入错误造成异常
                Field field = object.getClass().getDeclaredField(entry.getKey());
                //依据传入的(Map-key)获取setter方法,并利用Field设置方法的参数类型
                Method setMethod = object.getClass().getDeclaredMethod("set"+ StringUtil.initcap(entry.getKey()),field.getType());
                setMethod.invoke(object,convertValue(entry.getValue(),field));//反射调用setter方法并设置属性内容
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 实现字符串数据向定向数据类型转换的功能
     * @param value 接收输入的字符串数据内容,所有的内容都为String
     * @param field 要转换的数据类型
     * @return 返回转换后的数据
     */

    private static Object convertValue(String value,Field field){
        String fieldName = field.getType().getName();
        if ("java.lang.String".equals(fieldName)){
            return value;
        }
        if ("int".equalsIgnoreCase(fieldName)||"java.lang.Integer".equalsIgnoreCase(fieldName)){
           try {
               return Integer.parseInt(value);
           }catch (Exception e){
               return 0;
           }
        }
        if ("long".equalsIgnoreCase(fieldName)||"java.lang.Long".equalsIgnoreCase(fieldName)){
            try {
                return Long.parseLong(value);
            }catch (Exception e){
                return 0;
            }
        }
        if ("double".equalsIgnoreCase(fieldName)||"java.lang.Double".equalsIgnoreCase(fieldName)){
            try {
                return Double.parseDouble(value);
            }catch (Exception e){
                return 0.0;
            }
        }
        if ("java.util.Date".equalsIgnoreCase(fieldName)){
            SimpleDateFormat sdf = null;
            if (value.matches("\\d{4}-\\d{2}-\\d{2}")){
                sdf = new SimpleDateFormat("yyyy-MM-dd");
            }else if (value.matches("\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}")){
                sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            }
            try {
                return sdf.parse(value);
            } catch (ParseException e) {
              return null;
            }
        }
        return null;
    }
}

修改Emp类定义,增加属性:

public class Emp {
    private String ename;
    private String job;
    private Long empno;
    private Integer age;
    private Date hiredate;
    private double sal;
}

修改输入数据部分:

package com.xzzz.demo;

import java.util.HashMap;
import java.util.Map;

public class InputData {
    public static Map<String, String> input() {
        Map<String, String> data = new HashMap<>();
        data.put("ename","张三");
        data.put("job","办事员");
        data.put("empno","73659");
        data.put("sal","7365.2");
        data.put("age","26");
        data.put("hiredate","1990-10-10");
        return data;
    }
}

编写调用:

package com.xzzz.demo;


import com.xzzz.demo.reflect.ObjectInstanceFactory;

public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        Emp emp = ObjectInstanceFactory.create(Emp.class,InputData.input());
        System.out.println(emp);
    }
}

结果:

Emp{ename=‘张三’, job=‘办事员’, empno=73659, age=26, hiredate=Wed Oct 10 00:00:00 CST 1990, sal=7365.2}

5. 多级对象实例化

  对于当前的结构主要是针对于Emp的形式来完成的,所以在输入数据的时候,Map集合中key的数据全部都是单级的属性,但是在一些时候也可能继续进行其它引用数据的设置,现在假设有如下的关系: 一个雇员属于一个部门,一个部门属于一个公司,于是在这样的环境下,自然可以进行三个类的设计。

三个类的设计:

public class Emp {
    private String ename;
    private String job;
    private Long empno;
    private Integer age;
    private Date hiredate;
    private double sal;
    private Dept dept;
}

public class Dept {
    private String dname;
    private String loc;
    private Company company;
}

public class Company {
    private String cname;
    private Date creatDate;
}

  如下图所示:Java反射--反射与简单Java类

修改数据输入格式:

package com.xzzz.demo;

import java.util.HashMap;
import java.util.Map;

public class InputData {
    public static Map<String, String> input() {
        Map<String, String> data = new HashMap<>();
        data.put("ename","张三");
        data.put("job","办事员");
        data.put("empno","73659");
        data.put("sal","7365.2");
        data.put("age","26");
        data.put("hiredate","1990-10-10");
        data.put("dept.dname","教学部");
        data.put("dept.loc","北京");
        data.put("dept.company.cname","MLDN");
        data.put("dept.company.creatDate","2006-11-15");
        return data;
    }
}

修改BeanUtil类:

package com.xzzz.demo.reflect;

import com.xzzz.demo.StringUtil;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Iterator;
import java.util.Map;

public class BeanUtil {
    private BeanUtil() {
    }

    public static void SetValue(Object object, Map<String, String> value) {
        Iterator<Map.Entry<String, String>> iterator = value.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, String> entry = iterator.next();//获得每组数据
            try { //防止某些成员输入错误造成异常
                if (entry.getKey().contains(".")) {//那么此时就可能出现级联关系
                    //依据“.”进行拆分处理,而后以后判断,如果发现getter方法调用的返回为null,则利用setter进行对象实例化设置
                    String[] fieldSplit = entry.getKey().split("\\.");//进行拆分处理
                    Object currentObject = object;
                    for (int i = 0; i <fieldSplit.length-1;i++){
                        Method getMethod = currentObject.getClass().getDeclaredMethod("get"+StringUtil.initcap(fieldSplit[i]));
                        Object tempReturn = getMethod.invoke(currentObject);
                        if (tempReturn == null){//当前对象未实例化
                            Class<?> currentType = currentObject.getClass().getDeclaredField(fieldSplit[i]).getType();
                            Method setMethod = currentObject.getClass().getDeclaredMethod("set" + StringUtil.initcap(fieldSplit[i]),currentType);
                            tempReturn = currentType.getDeclaredConstructor().newInstance();
                            setMethod.invoke(currentObject,tempReturn);
                        }
                        currentObject = tempReturn;
                    }
                } else {

                    Field field = object.getClass().getDeclaredField(entry.getKey());
                    //依据传入的(Map-key)获取setter方法,并利用Field设置方法的参数类型
                    Method setMethod = object.getClass().getDeclaredMethod("set" + StringUtil.initcap(entry.getKey()), field.getType());
                    setMethod.invoke(object, convertValue(entry.getValue(), field));//反射调用setter方法并设置属性内容

                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 实现字符串数据向定向数据类型转换的功能
     *
     * @param value 接收输入的字符串数据内容,所有的内容都为String
     * @param field 要转换的数据类型
     * @return 返回转换后的数据
     */

    private static Object convertValue(String value, Field field) {
        String fieldName = field.getType().getName();
        if ("java.lang.String".equals(fieldName)) {
            return value;
        }
        if ("int".equalsIgnoreCase(fieldName) || "java.lang.Integer".equalsIgnoreCase(fieldName)) {
            try {
                return Integer.parseInt(value);
            } catch (Exception e) {
                return 0;
            }
        }
        if ("long".equalsIgnoreCase(fieldName) || "java.lang.Long".equalsIgnoreCase(fieldName)) {
            try {
                return Long.parseLong(value);
            } catch (Exception e) {
                return 0;
            }
        }
        if ("double".equalsIgnoreCase(fieldName) || "java.lang.Double".equalsIgnoreCase(fieldName)) {
            try {
                return Double.parseDouble(value);
            } catch (Exception e) {
                return 0.0;
            }
        }
        if ("java.util.Date".equalsIgnoreCase(fieldName)) {
            SimpleDateFormat sdf = null;
            if (value.matches("\\d{4}-\\d{2}-\\d{2}")) {
                sdf = new SimpleDateFormat("yyyy-MM-dd");
            } else if (value.matches("\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}")) {
                sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            }
            try {
                return sdf.parse(value);
            } catch (ParseException e) {
                return null;
            }
        }
        return null;
    }
}

进行测试:

package com.xzzz.demo;


import com.xzzz.demo.reflect.ObjectInstanceFactory;

public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        Emp emp = ObjectInstanceFactory.create(Emp.class,InputData.input());
        System.out.println(emp.getDept());
    }
}

结果:

Dept{dname=‘null’, loc=‘null’, company=Company{cname=‘null’, creatDate=null}}

6. 多级属性赋值

  现在既然可以实现级联对象的实例化处理,那么自然也就应该可以针对于级联属性赋值进行处理,级联属性的赋值最关键性的问题在于如何获取要操作的对象。

修改BeanUtil类:

package com.xzzz.demo.reflect;

import com.xzzz.demo.StringUtil;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Iterator;
import java.util.Map;

public class BeanUtil {
    private BeanUtil() {
    }

    public static void SetValue(Object object, Map<String, String> value) {
        Iterator<Map.Entry<String, String>> iterator = value.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, String> entry = iterator.next();//获得每组数据
            try { //防止某些成员输入错误造成异常
                Object currentObject = object;
                String fieldKey = null;
                if (entry.getKey().contains(".")) {//那么此时就可能出现级联关系
                    //依据“.”进行拆分处理,而后以后判断,如果发现getter方法调用的返回为null,则利用setter进行对象实例化设置
                    String[] fieldSplit = entry.getKey().split("\\.");//进行拆分处理
                    for (int i = 0; i < fieldSplit.length - 1; i++) {
                        Method getMethod = currentObject.getClass().getDeclaredMethod("get" + StringUtil.initcap(fieldSplit[i]));
                        Object tempReturn = getMethod.invoke(currentObject);
                        if (tempReturn == null) {//当前对象未实例化
                            Class<?> currentType = currentObject.getClass().getDeclaredField(fieldSplit[i]).getType();
                            Method setMethod = currentObject.getClass().getDeclaredMethod("set" + StringUtil.initcap(fieldSplit[i]), currentType);
                            tempReturn = currentType.getDeclaredConstructor().newInstance();
                            setMethod.invoke(currentObject, tempReturn);
                        }
                        currentObject = tempReturn;
                    }
                    fieldKey = fieldSplit[fieldSplit.length - 1];
                } else {
                    fieldKey = entry.getKey();
                }


                Field field = currentObject.getClass().getDeclaredField(fieldKey);
                //依据传入的(Map-key)获取setter方法,并利用Field设置方法的参数类型
                Method setMethod = currentObject.getClass().getDeclaredMethod("set" + StringUtil.initcap(fieldKey), field.getType());
                setMethod.invoke(currentObject, convertValue(entry.getValue(), field));//反射调用setter方法并设置属性内容


            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 实现字符串数据向定向数据类型转换的功能
     *
     * @param value 接收输入的字符串数据内容,所有的内容都为String
     * @param field 要转换的数据类型
     * @return 返回转换后的数据
     */

    private static Object convertValue(String value, Field field) {
        String fieldName = field.getType().getName();
        if ("java.lang.String".equals(fieldName)) {
            return value;
        }
        if ("int".equalsIgnoreCase(fieldName) || "java.lang.Integer".equalsIgnoreCase(fieldName)) {
            try {
                return Integer.parseInt(value);
            } catch (Exception e) {
                return 0;
            }
        }
        if ("long".equalsIgnoreCase(fieldName) || "java.lang.Long".equalsIgnoreCase(fieldName)) {
            try {
                return Long.parseLong(value);
            } catch (Exception e) {
                return 0;
            }
        }
        if ("double".equalsIgnoreCase(fieldName) || "java.lang.Double".equalsIgnoreCase(fieldName)) {
            try {
                return Double.parseDouble(value);
            } catch (Exception e) {
                return 0.0;
            }
        }
        if ("java.util.Date".equalsIgnoreCase(fieldName)) {
            SimpleDateFormat sdf = null;
            if (value.matches("\\d{4}-\\d{2}-\\d{2}")) {
                sdf = new SimpleDateFormat("yyyy-MM-dd");
            } else if (value.matches("\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}")) {
                sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            }
            try {
                return sdf.parse(value);
            } catch (ParseException e) {
                return null;
            }
        }
        return null;
    }
}

进行测试:

public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        Emp emp = ObjectInstanceFactory.create(Emp.class,InputData.input());
        System.out.println(emp);
        System.out.println(emp.getDept());
    }
}

结果:

Emp{ename=‘张三’, job=‘办事员’, empno=73659, age=26, hiredate=Wed Oct 10 00:00:00 CST 1990, sal=7365.2}
Dept{dname=‘教学部’, loc=‘北京’, company=Company{cname=‘MLDN’, creatDate=Wed Nov 15 00:00:00 CST 2006}}