java 反射机制

概述:
1.反射机制的本质
反射机制就是对I/O流的封装版,读取硬盘上的class文件,同时也是SUN公司对客户做出让步的选择。
反射机制通过将硬盘上的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是类在内存中的class文件
               通过String指定要获取那个类的Class.注意必须是完整的类名(也就是包名+类名格式)
        获取自定义类的相关信息.这个自定义的类必须要在当前项目中才可以

      
           注意:
 Class.forName() 是反射机制【加载】硬盘上的class文件,会创建class文件对象.
 实例对象.getClass() 或者 类名.class 是反射机制【定位】内存中已经存在的class文件

 二:通过相关Class的引用调用相关方法,得到其方法,属性,构造方法的相关信息:

         Class类中的重要API

     
 ClassLoadergetClassLoader()                                                                         
          返回该类的类加载器。

  1. 获取Field(属性)----->包含属性的修饰符,类型及名称 如:[private int age];
    FieldgetDeclaredField(String name)
              返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。
    Field[]getDeclaredFields()
              返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段
  2. 获取Method(普通方法)----->包含方法的修饰符,返回值类型,方法名称及抛出的异常 如:[public string getName()];


    Method

    getDeclaredMethod(String name, Class<?>... parameterTypes)

              返回一个 Method 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方

    Method[]

    getDeclaredMethods()

              返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。



  3. 获取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;
          }
}


          

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章