00 23Java高级之反射与简单Java类

1 传统属性赋值弊端

简单Java类主要由属性所组成,并且提供有相应的setter和getter处理方法,同时简单Java类最大的特征就是通过对象保存相应的类属性内容。但是如果使用传统的简单Java类开发,那么也会面临非常麻烦的困难。
范例:传统简单Java的操作

class Emp{
	private String name;
	private String job;
	
	public Emp() {}
	
	public void setName(String name) {
		this.name = name;
	}
	
	public void setJob(String job) {
		this.job = job;
	}
	
	public String getName() {
		return this.name;
	}
	
	public String getJob() {
		return this.job;
	}
}

特别强调,为了方便理解,本次Emp类之中定义的ename、job两个属性的类型使用的都是String类型。按照传统的做法此时应该首先实例化Emp类的对象,而后通过实例化对象进行setter方法的调用以设置属性内容。
范例:传统调用

public class JavaAPIDemo {
	public static void main(String[] args) {
		Emp emp = new Emp();
		emp.setName("lks");
		emp.setJob("Programmer");
		System.out.println("Emp [name: " + emp.getName() + ", job: " + emp.getJob() + "]");
	}
}

在整个进行Emp对象实例化并设置数据的操作过程中,设置数据的部分是最麻烦的,可以想象一下,如果说现在Emp类里面提供有大概50个属性,那么对整个的程序将会出现有一堆的setter方法调用。或者再进一步说明,在实际的开发之中,简单Java类的个数是非常多,那么如果所有的简单Java类都牵扯到属性赋值的时候,这种情况代码编写的重复性将会非常的高。

按照传统的直观的编程方式,所带来的问题就是代码会存在有大量的重复操作,如果要想解决对象的重复处理操作,那么唯一的解决方案就是反射机制,反射机制最大的特征就是可以根据其自身的特点(Object类直接操作、可以直接操作属性或方法)实现相同的相同功能类的重复操作的抽象处理。

2 属性自动赋值实现思路

经过分析之后已经确认了当前简单Java类操作的问题所在,而对于开发者而言就需要想办法通过一种解决方案来实现一种属性内容的自动设置,那么这个时候的设置强烈建议采用字符串的形式来描述对应的类型。
1、再进行程序开发的时候String字符串可以描述的内容有很多,并且也可以由开发者自行定义字符串的结构,下面就采用“属性:内容|属性:内容|”的形式来为简单Java类中的属性初始化。
2、类设计的基本结构:应该由一个专门的ClassInstanceFactory类负责所有的反射处理,即:接受反射对象与要设置的属性内容,,同时可以获取指定类的实例化对象。
3、设计的基本结构

那么在当前的开发之中,所需要留给用户完善的就是ClassInstanceFactory.create()处理方法

3 单属性赋值

对于此时的Emp类里面会发现所给出的数据类型都没有其他的引用关联了,只是描述了Emp本类的对象,所以这样的设置称为单级设置处理,所以此时应该处理两件事情:
(1)需要通过反射进行指定类对象的实例化处理;
(2)进行内容的设置(Field属性类型、方法名称、要设置的内容);
1、定义StingUtil实现首字母大写

package org.lks.util;

public class StringUtil {
	private StringUtil() {}
	
	public static String capitalize(String str) {
		if(str == null | "".equals(str)) {
			return null;
		}
		
		if(str.length() == 1) {
			return str.toUpperCase();
		}else {
			return str.substring(0,1).toUpperCase() + str.substring(1);
		}
	}
}

2、定义BeanUtil工具类,该工具类主要实现属性的设置。

package org.lks.util;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class BeanUtil {

	private BeanUtil() {}
	
	public static void create(Object obj, String infos) {
		Class<?> clazz = obj.getClass();
		String[] objectInfos = infos.split("\\|");
		for(int i = 0; i < objectInfos.length; i++) {
			String[] objectInfo = objectInfos[i].split(":");
			try {
				Field objectField = clazz.getDeclaredField(objectInfo[0]);
				Method objectSetMethod = clazz.getDeclaredMethod("set" + StringUtil.capitalize(objectInfo[0]), objectField.getType());
				objectSetMethod.invoke(obj, objectInfo[1]);
			} catch (Exception e) {}
		}
	}
}

3、ClassInstanceFactory负责实例化对象并且调用BeanUtils类实现属性内容的设置。

package org.lks.factory;

import org.lks.util.BeanUtil;

public class ClassInstanceFactory {
	private ClassInstanceFactory() {}
	
	public static Object create(String className, String objectInfos) {
		Object resultObject = null;
		Class<?> clazz = null; 
		try {
			clazz = Class.forName(className);
			resultObject = clazz.getDeclaredConstructor().newInstance();
			BeanUtil.create(resultObject, objectInfos);
		}catch(Exception e) {}
		return resultObject;
	}
}

即使现在类中的属性再多,那么也可以轻松的实现setter的调用(类对象实例化处理)。

4 设置多种数据类型

现在已经成实现了单级的属性配置,但是这里面依然需要考虑一个实际的情况,当前所给定的数据类型只是String,但是在实际的开发之中面对简单java类中的属性类型一般的可选为:long(Long)、int(Integer)、double(Double)、String、Date(日期、日期时间),所以这个时候对于当前的程序代码就必须做出修改,要求可以实现各种数据类型的配置。
范例:StringUtil类中实现不同类型转换

public static Object convertEverything(Class<?> clazz, String str) {
		Object result = null;
		System.out.println(clazz.getSimpleName());
		switch(clazz.getSimpleName()) {
			case "Byte":{
				result = Byte.decode(str);
				break;
			}
			case "Short":{
				if(str.matches("\\d+")) {
					result = Short.decode(str);
				}
				break;
			}
			case "Integer":{
				if(str.matches("\\d+")){
					result = Integer.decode(str);
				}
				break;
			}
			case "Long":{
				if(str.matches("\\d+")) {
					result = Long.decode(str);
				}
				break;
			}
			case "Float":{
				if(str.matches("\\d+(([.]\\d+)|([.]?))")) {
					result = Float.parseFloat(str);
				}
				break;
			}
			case "Double":{
				if(str.matches("\\d+(([.]\\d+)|([.]?))")) {
					result = Double.parseDouble(str);
				}
				break;
			}
			case "Date":{
				try {
					if( str.matches("\\d{4}-\\d{2}-\\d{2}")) {
						result = new SimpleDateFormat("yyyy-MM-dd").parse(str);
					}else if( str.matches("\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}") ) {
						result = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").parse(str);
					}else {
						result = new Date();
					}
				}catch(Exception e) {}
				break;
			}
			case "String":{
				result = str;
			}
			default:{
				break;
			}
		}
		return result;
	}

此时只是列举出了常用的集中数据类型,如果要想将其作为一个产品推广,你必须要考虑所有可能出现的类型,同时所有可能出现的日期格式也需要考虑。

5 级联对象实例化

如果说现在给定的类对象之中存在有其它的引用的级联关系的情况下,称为多级设置。例如:一个雇员属于一个部门,一个部门属于一个公司,所以这个时候对于简单Java类的基本关系定义如下:
(1)Company.java
(2)Dept.java
(3)Emp.java

package org.lks.model;

import java.util.Date;

public class Emp {
	private Long empno;
	private String name;
	private String job;
	private Double salary;
	private Date hiredate;
	private Department department;
	
	public Emp() {}
	
	
	
	@Override
	public String toString() {
		return "Emp [empno=" + this.empno + ", name=" + this.name + ", job=" + this.job + ", salary=" + this.salary + ", hiredate="
				+ this.hiredate + ", department=" + this.department + "]";
	}



	public void setName(String name) {
		this.name = name;
	}
	
	public void setJob(String job) {
		this.job = job;
	}
	
	public String getName() {
		return this.name;
	}
	
	public String getJob() {
		return this.job;
	}

	public Long getEmpno() {
		return empno;
	}

	public void setEmpno(Long empno) {
		this.empno = empno;
	}

	public Double getSalary() {
		return salary;
	}

	public void setSalary(Double salary) {
		this.salary = salary;
	}

	public Date getHiredate() {
		return hiredate;
	}

	public void setHiredate(Date hiredate) {
		this.hiredate = hiredate;
	}



	public Department getDepartment() {
		return department;
	}



	public void setDepartment(Department department) {
		this.department = department;
	}
	
	
}


package org.lks.model;

public class Department {
	private String name;
	private Company company;

	public Department() {}
	
	

	@Override
	public String toString() {
		return "Department [name=" + this.name + ", company=" + this.company + "]";
	}



	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Company getCompany() {
		return company;
	}

	public void setCompany(Company company) {
		this.company = company;
	}
	
	
}


package org.lks.model;

public class Company {
	private String name;
	
	public Company() {}

	
	
	@Override
	public String toString() {
		return "Company [name=" + this.name + "]";
	}



	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	
	
}

如果要通过Emp进行操作,则应该使用“.”作为级联关系的处理;
(1)dept.dname:财务部Emp类实例化对象.getDept().setDname("财务部")
(2)dept.company.name:LKEmp类实例化对象.getDept().getCompany().setName("LK")

但是考虑到代码的简洁性,所以应该考虑可以通过级联的配置自动实现类中属性的实例化。

现在的属性存在有多级关系,那么对于多级的关系就必须与单级的配置区分开。

这些自动的级联配置的实例化操作,在以后的项目的编写之中一定会使用到。

6 级联属性赋值

现在已经成功的实现级联的对象实例化处理,那么随后就需要去考虑级联的属性的设置了,在之前考虑级联对象实例化处理的时候会发现,循环的时候都是少了一位的。

当此时代码循环处理完成之后,currentObject表示的就是可以进行setter方法调用的对象了,并且理论上该对象一定不可能为null,随后就可以按照之前的方式利用对象进行setter方法调用。
范例:实现对象的级联属性设置

package org.lks.util;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class BeanUtil {

	private BeanUtil() {}
	
	public static void create(Object obj, String infos) {
		Class<?> clazz = obj.getClass();
		String[] objectInfos = infos.split("\\|");
		for(int i = 0; i < objectInfos.length; i++) {
			String[] objectInfo = objectInfos[i].split(":");
			try {
				if(objectInfo[0].contains(".")) {
					String[] objectChains = objectInfo[0].split("\\.");
					Object currentObject = null;
					Class<?> currentObjectClass = null;
					Field currentObjectField = null;
					Object nextObject = null;
					Class<?> nextObjectClass = null;
					Field nextObjectField = null;
					if(currentObject == null) {
						currentObjectField = clazz.getDeclaredField(objectChains[0]);
						currentObjectClass = currentObjectField.getType();
						if(clazz.getDeclaredMethod("get" + StringUtil.capitalize(objectChains[0])).invoke(obj) == null) {
							currentObject = currentObjectClass.getDeclaredConstructor().newInstance();
							Method objectSetMethod = clazz.getDeclaredMethod("set" + StringUtil.capitalize(objectChains[0]), currentObjectClass);
							objectSetMethod.invoke(obj, currentObject);
						}else {
							currentObject = clazz.getDeclaredMethod("get" + StringUtil.capitalize(objectChains[0])).invoke(obj);
						}
					}
					for(int j = 1; j < objectChains.length - 1; j++) {
						nextObjectField = currentObjectClass.getDeclaredField(objectChains[j]);
						nextObjectClass = nextObjectField.getType();
						if(currentObjectClass.getDeclaredMethod("get" + StringUtil.capitalize(objectChains[j])).invoke(currentObject) == null) {
							nextObject = nextObjectClass.getDeclaredConstructor().newInstance();
						}else {
							currentObject = currentObjectClass.getDeclaredMethod("get" + StringUtil.capitalize(objectChains[j])).invoke(currentObject);
							continue;
						}
						Method objectSetMethod = currentObjectClass.getDeclaredMethod("set" + StringUtil.capitalize(objectChains[j]), nextObjectClass);
						objectSetMethod.invoke(currentObject, nextObject);
						currentObject = nextObject;
						currentObjectField = nextObjectField;
						currentObjectClass = nextObjectClass;
					}
					Field objectField = currentObjectClass.getDeclaredField(objectChains[objectChains.length - 1]);
					Method objectSetMethod = currentObjectClass.getDeclaredMethod("set" + StringUtil.capitalize(objectChains[objectChains.length - 1]), objectField.getType());
					objectSetMethod.invoke(currentObject, StringUtil.convertEverything(objectField.getType(), objectInfo[1]));
				}else {
					Field objectField = clazz.getDeclaredField(objectInfo[0]);
					Method objectSetMethod = clazz.getDeclaredMethod("set" + StringUtil.capitalize(objectInfo[0]), objectField.getType());
					objectSetMethod.invoke(obj, StringUtil.convertEverything(objectField.getType(), objectInfo[1]));
				}
				
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
}

在以后的开发之中简答Java类的赋值处理将不再重复调用setter操作完成,而这种处理形式是在正规开发中普遍采用的方式。

发布了122 篇原创文章 · 获赞 11 · 访问量 4225

猜你喜欢

转载自blog.csdn.net/weixin_43762330/article/details/104770776