概述: 1.反射机制的本质 反射机制通过将硬盘上的class文件加载到内存,让开发人员了解当前的【陌生对象】 2.class文件有什么作用: class文件是【解释文件】。JVM可以通过【解释文件】了解当前【实例对象】的特征和行为 3.class文件在硬盘上的位置: 1.java prorject 中class文件 在 bin文件下的 2.web project 中class文件 在 tomcat---》webapps--->当前工程--->WEB-info-->bin--->classess 4.Class对象的含义
我们知道,类是对现实的某种事物的抽象定义,而Class是对于类的抽象定义。比如人这个对象。想要写成一个类,需要对人这个对象的属性和行为有所了解。比如人有五官,其中嘴巴能吃饭。眼睛能看东西。这些是我们对人这个对象的认知。并将其定义出来。那么Class就相当与人。Class是Java的类对象。这个类对象都有哪些属性和行为呢?类中有方法(Method),有属性(Field)。类有构造方法(Constract),类有继承(supperClass),其中方法可以让类执行一些行为,构造方法可以获取类的实例。Class就是java类的抽象定义。
一:获得某个类的Class引用这个Class是类在内存中的class文件 通过String指定要获取那个类的Class.注意必须是完整的类名(也就是包名+类名格式) 获取自定义类的相关信息.这个自定义的类必须要在当前项目中才可以
注意: Class.forName() 是反射机制【加载】硬盘上的class文件,会创建class文件对象. 实例对象.getClass() 或者 类名.class 是反射机制【定位】内存中已经存在的class文件
二:通过相关Class的引用调用相关方法,得到其方法,属性,构造方法的相关信息: Class类中的重要API
ClassLoader 返回该类的类加载器。
getClassLoader()
- 获取Field(属性)----->包含属性的修饰符,类型及名称 如:[private int age];
Field
返回一个getDeclaredField(String name)
对象,该对象反映此Field
对象所表示的类或接口的指定已声明字段。Class
Field[]
返回getDeclaredFields()
对象的一个数组,这些对象反映此Field
对象所表示的类或接口所声明的所有字段Class
- 获取Method(普通方法)----->包含方法的修饰符,返回值类型,方法名称及抛出的异常 如:[public string getName()];
Method getDeclaredMethod(String name, Class<?>... parameterTypes)
返回一个 Method 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方
Method[] getDeclaredMethods()
返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。
- 获取Constructor(构造方法)----->包含构造方法的修饰符,方法名称及抛出的异常 如:[public Student()];
Constructor<T> 返回一个
getDeclaredConstructor(Class<?>... parameterTypes)
对象,该对象反映此
Constructor
对象所表示的类或接口的指定构造方法。
Class
Constructor<?>[] 返回
getDeclaredConstructors()
对象的一个数组,这些对象反映此
Constructor
对象表示的类声明的所有构造方法。
Class
例如:(claz是相关类的字节码对象的引用)
Constructor[] cons = claz.getConstructors();//claz.getConstructors()获取的是类中的所有的公开的构造方法
claz.getDeclaredConstructors()//获取类中声声明的构造方法的数组,包括公开的,保护的,缺省的,私有的构造方法
三:不同的操作“对象”,使用不同的API完成想做的功能 在java中把构造方法封装成Constructor类,属性封装成Field类,方法封装成Method类。 而他们是继承Class实现AccessibleObject接口(在java.lang.reflect包下)。而这个接口中定义的以下的方法
setAccessible(boolean flag) | 将此对象的 accessible 标志设置为指示的布尔值。 |
这个方法三个类中都实现了,该方法是设置是否能够访问privat 修饰的属性,方法,构造方法。
继承图 四:反射的代码示例: 以下示例一代码是模拟Spring框架中将前台数据转换为实体类的小Demo。 前台数据的key必须保证和实体类中的属性名称绝对一致
public class ReflectUtil {
/**
* 过程: 前台数据------>后台实体类对象
* 功能:将【添加页面】发送的请求保存到对应的实体类对象中
* @param req 包含本次请求包含的请求参数
* @param classObj 与当前【添加页面】对应的【实体类文件】
* @return
*/
public static Object parseRequest(HttpServletRequest req,Class classObj)throws Exception{
//1. 根据获得【class文件】,创建一个【实例对象】
Object obj= classObj.newInstance();
//2. 获得本次请求,涉及的所有的【请求参数名称】
Enumeration paramArray= req.getParameterNames();
// 3. 循环遍历【枚举】
while(paramArray.hasMoreElements()){
// 3.1 每一次循环,从【枚举】中获得一个【请求参数名称】 sid
String paramName= (String) paramArray.nextElement();
//3.2 到【class文件】寻找与当前【请求参数名称】相同的【同名属性对象】
//Field fieldObj=classObj.getDeclaredField(paramName);
//表单域中有可能存在非实体类中的属性,所以采用以下方式
Field fieldObj =null;
try{
fieldObj=classObj.getDeclaredField(paramName);
}catch(NoSuchFieldException ex){
System.out.println("属性"+paramName+"在当前类 "+classObj.getName()+"不存在");
continue;
}
fieldObj.setAccessible(true);
// 3.3 读取【请求参数】包含的【内容】
String value = req.getParameter(paramName);
//3.4 读取【同名属性对象】的【数据类型名称】
String typeName=fieldObj.getType().getName();//[int,double,String ,java.util.Date,boolean]
//3.5 根据【同名属性对象】的【数据类型名称】对【请求参数】包含的【内容】进行【类型转换】
Object data=convertType(typeName,value);
// 3.6 将转换后的数据赋值给【同名属性对象】
fieldObj.set(obj, data);//通知JVM,强制为当前【实例对象】中指定的【属性】进行赋值。
}
//4. 返回赋值好的【实例对象】
return obj;
}
实例二:模拟数据库数据转换为实体类
/**
* 过程: 数据库数据------>后台实体类对象
* 功能:resultSet -----> List
* @param rs
* @param xmlPath
* @return
*/
public static List convert(ResultSet rs,String xmlPath)throws Exception{
//0.局部变量
List list = new ArrayList();
// 1.将硬盘上【实体类映射文件】加载到内存
InputStream in = new FileInputStream(xmlPath);
SAXReader saxObj = new SAXReader();
Document xmlObj = saxObj.read(in);
//2.获得【临时表】包含【内部结构】
ResultSetMetaData rsmd= rs.getMetaData();
//3.从【实体类映射文件】获得与当前【临时表】对应【实体类文件】
String xPath="//@class";
Attribute attrObj= (Attribute) xmlObj.selectSingleNode(xPath);
String classPath =attrObj.getValue();
Class classObj=Class.forName(classPath);
// 4.循环【临时表数据行】
while(rs.next()){
//4.1,每次循环时,生成一个【实例对象】
Object obj = classObj.newInstance();
//4.2 循环【当前数据行】中的字段信息
for(int col=1;col<=rsmd.getColumnCount();col++){
//4.2.1 每次循环,获得【当前数据行】中一个【字段名称】
String colName = rsmd.getColumnName(col); //DEPTNO
//4.2.2 到 【实体类映射文件】寻找与【字段名称】对应【属性名】
xPath="//property[@colName='"+colName+"']";
Element property= (Element) xmlObj.selectSingleNode(xPath);
String fieldName= property.attributeValue("name"); //departId
//4.2.3 到 【实体类文件】寻找对应的【属性对象】
Field fieldObj=classObj.getDeclaredField(fieldName);// private int departId;
fieldObj.setAccessible(true);
//4.2.4 读取【当前数据行】,这个【字段名称】关联的数据
String value = rs.getString(colName);
// 4.2.5 根据【属性对象】的数据类型名称,对取得字段内容进行类型转换
String typeName=fieldObj.getType().getName();
Object data=convertType(typeName,value);
// 4.2.6 将转换后的数据添加到【属性对象】 属性对象.set(【实例对象】,data)
fieldObj.set(obj, data);
}
//4.3 将赋值好的 【实例对象】保存到list
list.add(obj);
}
//5.将list返回
return list;
}
//公用的判断数据类型并转换的方法
private static Object convertType(String typeName,String value)throws Exception{
Object data =null;//保存类型转换后的数据
if("int".equals(typeName)){
data = Integer.valueOf(value);
}else if("double".equals(typeName)){
data = Double.valueOf(value);
}else if("boolean".equals(typeName)){ // "false" "true"
data = Boolean.valueOf(value);
}else if("java.util.Date".equals(typeName)){
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
data=sdf.parse(value);
}else if("java.lang.String".equals(typeName)){
data = value;
}
return data;
}
}
|