前言:
之前的文章,有小伙伴留言说希望出一篇反射的教程,那今天我们就来说一说反射。对,就是这么好,所有小伙伴在留言,私信中提的问题,我都会逐一解答,提的一些要求,我也会尽快安排时间写相关教程分享给大家,今天就来搞一波反射。
我们通过一个实际的例子来演示反射在编程中的应用,学一个技术,一定是要应用的,可能之前大家对反射的学习,仅仅是停留在概念层面,不知道反射究竟应用在哪,所以是一头雾水。相信通过这篇教程,会让你对反射有一个更深层次的认知。
首先简单介绍反射的概念:
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。
<?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&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。
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;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。
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 + "]";
}
}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工具类。
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,核心代码。
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对象。
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对象。
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