Java反射技术机制及一键封装原理

由于Java反射技术运用的方面很广泛,借此机会做一个总结,也为自己忘记了可以随时翻阅便可快速回忆起来!

首先我们来说说什么是类的加载?

类的加载:当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载、连接、初始化 三步来实现对这个类进行初始化。

加载:
*就是指将class文件读入内存,并为之创建一个Class对象。
*任何类被使用时系统都会建立一个Class对象。

连接:
验证 是否有正确的内部结构,并和其他类协调一致
准备 负责为类的静态成员分配内存,并设置默认初始化值
解析 将类的二进制数据中的符号引用替换为直接引用

初始化:类栈开辟空间、堆开辟空间、默认初始化、显示初始化、构造初始化……

类初始化时机:

创建类的实例
访问类的静态变量,或者为静态变量赋值
调用类的静态方法
使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
初始化某个类的子类
直接使用java.exe命令来运行某个主类

类加载器的作用:负责将.class文件加载到内在中,并为之生成对应的Class对象。
类加载器的组成:
Bootstrap ClassLoader 根类加载器
Extension ClassLoader 扩展类加载器
Sysetm ClassLoader 系统类加载器

**什么是反射?**
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.

反射获取class文件的三种方式:

A:Object类的getClass()方法 (对象.getClass())
B:数据类型的静态属性class、(类.class)
C:Class类中的静态方法(class.forName(“类的全路径名称”),开发中推荐使用这种,因为可以将名称配置在配置文件中,比较灵活)

通过反射获取无参构造方法并使用:

准备一个Person类对象:

package com.yida.reflect;

public class Person {
    private String name;
    private String id;


    public Person() {
        super();
        // TODO Auto-generated constructor stub
    }


    private Person(String name) {
        super();
        this.name = name;
    }


    public Person(String name, String id) {
        super();
        this.name = name;
        this.id = id;
    }


    public String getName() {
        return name;
    }

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

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public void sleep(){
        System.out.println("人要睡觉");
    }
    public void eat(String name){
        System.out.println("人吃"+name);
    }
    @Override
    public String toString() {
        return "Person [name=" + name + ", id=" + id + "]";
    }
}

接下来就是关于反射的具体应用,注释已经写在代码里了,就不做过多解释了!

package com.yida.reflect;

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

import org.junit.Test;

public class Test01 {

    @Test
    public void test001() throws Exception{
        //获取字节码文件对象
        Class clazz = Class.forName("com.yida.reflect.Person");
        //获取构造方法(只能获取该对象的所有公共构造方法,就是被public修饰符修饰的)
        Constructor[] constructors = clazz.getConstructors();
        //遍历构造方法并将方法名输出
        for (Constructor constructor : constructors) {
            //使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。
            //Object newInstance = constructor.newInstance();
            //Person p1 = (Person) newInstance;
            //p1.sleep();
            //p1.eat("肉");
            System.out.println(constructor);//只能获取公有的构造 方法
            /*输出结果:
             * public com.yida.reflect.Person(java.lang.String,java.lang.String)
               public com.yida.reflect.Person()*/

        }
        //获取 所有构造方法
        Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
        for (Constructor constructor : declaredConstructors) {
            System.out.println(constructor);
            /*运行结果:
            public com.yida.reflect.Person(java.lang.String,java.lang.String)
            private com.yida..j.Person(java.lang.String)
            public com.yida.reflect.Person()*/
        }
    }

    @Test
    public void test002() throws Exception{
        //获取字节码文件对象
        Class clazz = Class.forName("com.yida.reflect.Person");
        //获取单个构造方法    public Constructor<T> getConstructor(Class<?>... parameterTypes)  
        Constructor con = clazz.getConstructor();//返回的是构造方法对象
        Object newInstance = con.newInstance();
        System.out.println(newInstance);//输出结果:Person [name=null, id=null]
    }

    @Test
    public void test003() throws Exception{
        //通过反射获取带参构造方法并使用
        //获取字节码文件对象
        Class clazz = Class.forName("com.yida.reflect.Person");
        Constructor constructor = clazz.getConstructor(String.class,String.class);
        Object instance = constructor.newInstance("zhoujie","66666");
        System.out.println(instance);//运行结果:Person [name=zhoujie, id=66666]
    }

    /**
     * 通过反射获取私有方法并使用
     * @throws Exception 
     * 
     */
    @Test
    public void test004() throws Exception{
        //获取字节码文件对象
        Class clazz = Class.forName("com.yida.reflect.Person");
        //获取私有构造方法对象
        Constructor constructor = clazz.getDeclaredConstructor(String.class);
        //值为true,则指示反射的对象在使用时应取消Java语言的访问检查
        constructor.setAccessible(true);
        //用私有构造方法创建 对象
        Object obj = constructor.newInstance("zhoujie");
        System.out.println(obj);//运行结果:Person [name=zhoujie, id=null]

    }

    /**
     * 通过反射获取成员变量并使用
     * @throws Exception 
     */
    @Test
    public void test005() throws Exception{
        //获取字节码文件对象
        Class clazz = Class.forName("com.yida.reflect.Person");
        //只能获取用public修饰的成员变量
        //Field[] fields = clazz.getFields();
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            System.out.println(field);
        }
        /*运行结果:
        private java.lang.String com.yida.reflect.Person.name
        private java.lang.String com.yida.reflect.Person.id*/
    }

    @Test
    public void test006() throws Exception{
        //获取字节码文件对象
        Class clazz = Class.forName("com.yida.reflect.Person");
        //Method[] methods = clazz.getMethods();获取自己的包括父亲的公共方法 
        //Method[] declaredMethods = clazz.getDeclaredMethods();获取自己的所有方法

        Constructor con = clazz.getConstructor();
        Object obj = con.newInstance();
        //获取单个方法   第一个参数表示方法名,第二个参数表示的是方法参数的class类型
        Method method = clazz.getMethod("eat",String.class);
        method.invoke(obj, "肉");//运行结果:人吃肉
    }


}

这里只是简单的介绍反射的的基本用法,最后在分享一个反射的运用—-一键封装
原始需求:用户提交的表单数据我们需要进行封装到数据库中,那么这么一个操作如果我们运用request.getParameter(“name”)……这么 一条一条的进行取值存值的话未免工作量太大,特别是数据一多的情况下根本不好使,那么我们这里就可以运用反射的原理对数据进行抽取然后“一键封装 ”插到数据库,是不是很神奇呢?具体我们先来看看一键封装的原理及具体用法吧!

比如数据库中有一个用户表user,首先我们准备一个User对象用来封装数据User.java如下:

package com.example.domain;

public class User {
    private Integer id;
    private String name;
    private String password;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "User [id=" + id + ", name=" + name + ", password=" + password + "]";
    }

}

具体原理 如下:BeanPushUtils.java

package com.example.utils;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;

import com.example.domain.User;

public class BeanPushUtlis {

    //把数据自动封装到bean对象,user
    public static void  push(Object obj) throws Exception{
        //1.获取user的描述类
        BeanInfo  info = Introspector.getBeanInfo(obj.getClass());
        //2.获取user里面的属性和方法: setXXX
        PropertyDescriptor[] ps = info.getPropertyDescriptors();
        //3.遍历数组
        for (PropertyDescriptor por : ps) {
            //4.获取user里面的属性名称
            String attrName = por.getName();
            //5.获取属性里面的读方法,写方法
            Method  readMethod = por.getReadMethod();
            Method  writeMethod = por.getWriteMethod();
            System.out.println(attrName+" "+ readMethod+" "+ writeMethod);
        }
    }
    //把数据自动封装到bean对象, Map<String,Object> map = request.getParameterMap();
    // map的key: name属性的值,和 javaBean的属性名称一致
    //map的value: 用户输入的值
    public static void  pop(Object obj,Map<String,Object> map ) throws Exception{
        //1.获取user的描述类
        BeanInfo  info = Introspector.getBeanInfo(obj.getClass());
        //2.获取user里面的属性和方法: setXXX
        PropertyDescriptor[] ps = info.getPropertyDescriptors();
        //3.遍历map,获取map的key ,和 属性描述器的属性名称进行匹配
        Set<String> keys = map.keySet();
        for (String key : keys) {//key就是name属性的值 , 和PropertyDescriptor的属性匹配
            for (PropertyDescriptor por: ps) {//里面的属性 其实就是javaBean的属性名称
                String userAttr = por.getName();
                if(key.equals(userAttr)){//表单提交的name属性值  和  javaBean的属性名称一致
                    //获取写方法
                    Method setMethod = por.getWriteMethod();
                    setMethod.invoke(obj,map.get(key));// new User().setId("001");
                }
            }
        }
    }
    public static void main(String[] args) throws Exception {
        User user = new User();
        //Map map = request.getParameterMap();
        Map<String,Object> map = new LinkedHashMap<String,Object>();
//      map.put("id", 100);
//      map.put("name", "张三");
//      map.put("passwordss", "123456");
        BeanPushUtlis.pop(user,map);
        System.out.println(user);
    }

}

实际上Apache已经为我们封装好了BeanUtils工具类,我们要使用的话直接引入jar包就可以了,我这里使用的是commons-beanutils-1.8.3.jar
具体使用很简单,本来是想截图的,但是这个博客管理器老是上传不了图片(上传的时候显示不了)也由于时间关系,我也不做详细的步骤了,因为这个使用真的很简单!
比如用户提交的商品信息数据,最后我们可能需要插入到数据库中,那么提交到servlet时怎么处理呢?见如下代码:

//在servlet里封装用户信息
        Manager manager = new Manager();
        Map<String, String[]> map = request.getParameterMap();
        try {
            BeanUtils.populate(manager, map);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } 
        //交由service层处理....
        //ManagerService ms = new ManagerServiceImpl();
        ManagerService ms = BeanFactory.getBean("managerServiceImpl");
        Boolean login = ms.login(manager);
        //....代码省略
        //最后在Dao层进行查询或者插入操作
        @Override
    public Manager login(Manager manager) {
        // 用户登录
        QueryRunner qr = new QueryRunner(JDBCUtils.getDataSource());
        String sql = "select * from manager where userName = ? and password = ?";
        try {
            Manager query = qr.query(sql, new BeanHandler<Manager>(Manager.class),manager.getUserName(),manager.getPassword());
            return query;
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }

怎么样,是不是很神奇呢!(今天就到这里了,如有错误欢迎指正批评!纯手打加所有代码注释都是自己打上去的,喜欢的点个赞哦~)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值