首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >深度解析反射机制

深度解析反射机制

作者头像
南风
发布2019-04-22 16:56:30
发布2019-04-22 16:56:30
6670
举报
文章被收录于专栏:Java大联盟Java大联盟

前言:

之前的文章,有小伙伴留言说希望出一篇反射的教程,那今天我们就来说一说反射。对,就是这么好,所有小伙伴在留言,私信中提的问题,我都会逐一解答,提的一些要求,我也会尽快安排时间写相关教程分享给大家,今天就来搞一波反射。

我们通过一个实际的例子来演示反射在编程中的应用,学一个技术,一定是要应用的,可能之前大家对反射的学习,仅仅是停留在概念层面,不知道反射究竟应用在哪,所以是一头雾水。相信通过这篇教程,会让你对反射有一个更深层次的认知。

首先简单介绍反射的概念:

Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。

如何理解反射?简单的一句话解释,将传统的开发思路反向逆转。

传统的方式是通过类创建对象:类 ---> 对象。

反射就是将这个过程逆转,通过对象得到类:对象 ---> 类。

通过对象得到的这个类该如何表示?

使用Class类来表示,此类是Java反射的源头,是用来描述其他类的类,Class类的每一个实例化对象就是对其他类的描述。

在Object类中定义了以下的方法,此方法将被所有子类继承:

public final Class getClass()。

也就是说每一个类,都可以调用getClass()方法获取对应的Class对象,用来描述目标类,我们将这个Class类叫做目标类的运行时类。

有了Class对象,能做什么?

调用Class对象的newInstance()方法,可以动态创建目标类的对象。

要求:

1)目标类必须有无参数构造方法。

2)外部方法有足够的权限访问目标类的构造方法。

除了动态创建目标类的对象,反射也可以动态调用对象的各种方法,访问成员变量。

Java反射机制提供的功能

在运行时判断任意一个对象所属的类。

在运行时构造任意一个类的对象。

在运行时判断任意一个类所具有的成员变量和方法。

在运行时调用任意一个对象的成员变量和方法。

反射相关的主要API

java.lang.Class:描述一个类。

java.lang.reflect.Method:描述类的方法。

java.lang.reflect.Field:描述类的成员变量。

java.lang.reflect.Constructor:描述类的构造方法。

Class用来描述目标类的结构,叫做目标类的运行时类。

Class的常用方法:

public Class<?>[] getInterfaces()

返回运行时类实现的全部接口。

public Class<? Super T> getSuperclass()

返回运行时类的父类。

public Constructor<T>[] getConstructors()

返回运行时类的public构造方法。

public Constructor<T>[] getDeclaredConstructors()

返回运行时类的全部构造方法。

public Method[] getMethods()

返回运行时类的public方法。

public Method[] getDeclaredMethods()

返回运行时类的全部方法。

public Field[] getFields()

返回运行时类的public成员变量。

public Field[] getDeclaredFields()

返回运行时类的全部成员变量。

Method用来描述运行时类的方法。

Method的常用方法:

public Class<?> getReturnType()

返回方法的返回值类型。

public Class<?>[] getParameterTypes()

返回方法的参数列表。

public int getModifiers()

返回方法的访问权限修饰符。

public String getName();

返回方法名。

public Class<?>[] getExceptionTypes()

返回方法的异常信息。

Field用来描述运行时类的成员变量。

Field的常用方法:

public int getModifiers()

返回成员变量的访问权限修饰符。

public Class<?> getType()

返回成员变量的数据类型。

public String getName()

返回成员变量的名称。

Constructor用来描述运行时类的构造方法。

Constructor的常用方法:

public int getModifiers()

返回构造方法的访问权限修饰符。

public String getName()

返回构造方法名。

public Class<?>[] getParameterTypes()

返回构造方法参数列表。

反射在实际中的应用主要是动态创建对象,动态调用对象的方法。

1.创建对象:

调用Class类的newInstance()方法创建对象。

2.调用指定方法:

(1)通过Class类的getMethod(String name,Class…parameterTypes)方法获取一个Method对象,并设置此方法操作时所需要的参数类型。

(2)调用Object invoke(Object obj, Object[] args)方法,并向方法中传递目标obj对象的参数信息。

代码:

需求:

创建一个查询数据库的工具类,自动将SQL语句查询出的结果集,封装成不同的对象返回,一个简化版的MyBatis工具。

思路:

工具类查询方法的参数列表:Connection对象,SQL语句,目标运行时类对象clazz,数据表的id值。

1.通过Connection对象,SQL语句,id值查询出对应的结果集。

2.利用反射机制调用clazz的无参构造方法创建目标对象。

3.获取clazz的Filed,即目标类的所有成员变量。

4.找到与成员变量名相同的结果集字段,并获取字段值。

5.通过成员变量名找到对应的setter方法。

6.利用反射机制调用setter方法完成赋值。

实现步骤:

1.导入mysql驱动,c3p0数据源相关jar包。

2.创建c3p0-config.xml。

代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>

    <named-config name="testc3p0">

        <!-- 指定连接数据源的基本属性 -->
        <property name="user">root</property>
        <property name="password">root</property>
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/school?useUnicode=true&amp;characterEncoding=UTF-8</property>

        <!-- 若数据库中连接数不足时, 一次向数据库服务器申请多少个连接 -->
        <property name="acquireIncrement">5</property>
        <!-- 初始化数据库连接池时连接的数量 -->
        <property name="initialPoolSize">5</property>
        <!-- 数据库连接池中的最小的数据库连接数 -->
        <property name="minPoolSize">5</property>
        <!-- 数据库连接池中的最大的数据库连接数 -->
        <property name="maxPoolSize">10</property>

        <!-- C3P0 数据库连接池可以维护的 Statement 的个数 -->
        <property name="maxStatements">20</property>
        <!-- 每个连接同时可以使用的 Statement 对象的个数 -->
        <property name="maxStatementsPerConnection">5</property>

    </named-config>

</c3p0-config>

3.创建数据表student,user。

代码语言:javascript
复制
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(11) DEFAULT NULL,
  `address` varchar(11) DEFAULT NULL,
  `tel` varchar(255) DEFAULT NULL,
  `score` double(11,1) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
代码语言:javascript
复制
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

4.创建实体类Student,User。

代码语言:javascript
复制
package com.southwind.entity;

public class Student {
    private int id;
    private String name;
    private String address;
    private String tel;
    private double score;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
    public String getTel() {
        return tel;
    }
    public void setTel(String tel) {
        this.tel = tel;
    }
    public double getScore() {
        return score;
    }
    public void setScore(double score) {
        this.score = score;
    }
    @Override
    public String toString() {
        return "Student [id=" + id + ", name=" + name + ", address=" + address
                + ", tel=" + tel + ", score=" + score + "]";
    }

}
代码语言:javascript
复制
package com.southwind.entity;

public class User {
    private int id;
    private String name;
    private int age;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "User [id=" + id + ", name=" + name + ", age=" + age + "]";
    }

}

5.创建JDBCTools工具类。

代码语言:javascript
复制
package com.southwind.util;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class JDBCTools {
    private static ComboPooledDataSource dataSource;

    static{
        dataSource = new ComboPooledDataSource("testc3p0");
    }

    /**
     * 获取Connection
     * @return
     */
    public static Connection getConnection(){
        try {
            return dataSource.getConnection();
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 释放资源
     * @param conn
     * @param stmt
     * @param rs
     */
    public static void release(Connection conn,Statement stmt,ResultSet rs){
        if(conn != null){
            try {
                conn.close();
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        if(stmt != null){
            try {
                stmt.close();
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        if(rs != null){
            try {
                rs.close();
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}

6.创建数据库查询工具类MyQueryRunner,核心代码。

代码语言:javascript
复制
package com.southwind.util;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;

/**
 * 通用工具类
 * @author southwind
 *
 */
public class MyQueryRunner {

    /**
     * 将结果集动态封装成对象
     * @param conn
     * @param sql
     * @param clazz
     * @param id
     * @return
     */
    public Object query(Connection conn,String sql,Class clazz,int id){
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        Object obj = null;
        try {
            pstmt = conn.prepareStatement(sql);
            pstmt.setInt(1, id);
            rs = pstmt.executeQuery();
            obj = clazz.newInstance();
            if(rs.next()){
                //遍历实体类属性集合,依次将结果集中的值赋给属性
                Field[] fields = clazz.getDeclaredFields();
                //获取ResultSet数据
                ResultSetMetaData rsmd = rs.getMetaData();
                for(int i = 0; i < fields.length; i++){
                    Object value = setFieldValueByResultSet(fields[i],rsmd,rs);
                    //通过属性名找到对应的setter方法
                    String name = fields[i].getName();
                    name = name.substring(0, 1).toUpperCase() + name.substring(1);
                    String MethodName = "set"+name;
                    Method methodObj = clazz.getMethod(MethodName,fields[i].getType());
                    //调用setter方法完成赋值
                    methodObj.invoke(obj, value);
                }
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return obj;
    }

    /**
     * 根据将结果集中的值赋给对应的属性
     * @param field
     * @param rsmd
     * @param rs
     * @return
     */
    public Object setFieldValueByResultSet(Field field,ResultSetMetaData rsmd,ResultSet rs){
        Object result = null;
        try {
            int count = rsmd.getColumnCount();
            for(int i=1;i<=count;i++){
                //找到与属性名相同的结果集字段
                if(field.getName().equals(rsmd.getColumnName(i))){
                    //获取属性的数据类型
                    String type = field.getType().getName();
                    switch (type) {
                        case "int":
                            result = rs.getInt(field.getName());
                            break;
                        case "java.lang.String":
                            result = rs.getString(field.getName());
                            break;
                        case "double":
                            result = rs.getDouble(field.getName());
                            break;
                    }
                }
            }
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return result;
    }
}

7.测试

通过id查询student表,调用工具方法,直接返回Student对象。

代码语言:javascript
复制
package com.southwind.test;

import java.sql.Connection;
import com.southwind.entity.Student;
import com.southwind.util.MyQueryRunner;
import com.southwind.util.JDBCTools;

public class Test {
    public static void main(String[] args) {
        Connection conn = JDBCTools.getConnection();
        String sql = "select * from student where id = ?";
        MyQueryRunner myQueryRunner = new MyQueryRunner();
        Student student = (Student) myQueryRunner.query(conn, sql, Student.class, 1);
        System.out.println(student);
    }
}

通过id查询user表,调用工具方法,直接返回User对象。

代码语言:javascript
复制
package com.southwind.test;

import java.sql.Connection;
import com.southwind.entity.User;
import com.southwind.util.MyQueryRunner;
import com.southwind.util.JDBCTools;

public class Test {
    public static void main(String[] args) {
        Connection conn = JDBCTools.getConnection();
        String sql = "select * from users where id = ?";
        MyQueryRunner myQueryRunner = new MyQueryRunner();
        User user = (User) myQueryRunner.query(conn, sql, User.class, 30);
        System.out.println(user);
    }
}

源码:

github

https://github.com/southwind9801/ReflectDemo.git

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2018-03-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Java大联盟 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档