一、反射的概叙
什么是Java的反射机制?
反射是Java的特征之一,是一种间接操作目标对象的机制,核心是JVM在运行的时候才动态加载类,并且对于任意一个类,都能够知道这个类的所有属性和方法,调用方法/访问属性,不需要提前在编译期知道运行的对象是谁,他允许运行中的Java程序获取类的信息,并且可以操作类或对象内部属性。程序中对象的类型一般都是在编译期就确定下来的,而当我们的程序在运行时,可能需要动态的加载一些类,这些类因为之前用不到,所以没有加载到jvm,这时,使用Java反射机制可以在运行期动态的创建对象并调用其属性,它是在运行时根据需要才加载。
Java反射机制的作用?
允许编程人员在对类未知的情况下,获取类相关信息的方式变得更加多样灵活,调用类中相应方法,是Java
增加其灵活性与动态性的一种机制(从.class --> .java)。
反射机制的优缺点?
优点:
使用反射,我们就可以在运行时获得类的各种内容,进行反编译,对于Java这种先编译再运行的语言,能够让我们很方便的创建灵活的代码,这些代码可以在运行时装配,无需在组件之间进行源代码的链接,更加容易实现面向对象。
缺点:
1、性能问题。
Java反射机制中包含了一些动态类型,所以Java虚拟机不能够对这些动态代码进行优化。因此,反射操作的效率要比正常操作效率低很多。我们应该避免在对性能要求很高的程序或经常被执行的代码中使用反射。而且,如何使用反射决定了性能的高低。如果它作为程序中较少运行的部分,性能将不会成为一个问题。
2、安全限制。
使用反射通常需要程序的运行没有安全方面的限制。如果一个程序对安全性提出要求,则最好不要使用反射。
3、程序健壮性。
反射允许代码执行一些通常不被允许的操作,所以使用反射有可能会导致意想不到的后果。反射代码破坏了Java程序结构的抽象性,所以当程序运行的平台发生变化的时候,由于抽象的逻辑结构不能被识别,代码产生的效果与之前会产生差异。同时也会使得程序的代码复杂度上升,所以还要我们慎重的使用它。
二、反射的基本使用
sun为反射机制提供的常用的类
Java.lang.Class;
Java.lang.reflect.Constructor;
Java.lang.reflect.Field;
Java.lang.reflect.Method;
Java.lang.reflect.Modifier;
反射常用的API?
对象. setAccessible(true);// 修改访问权限
Class:
类对象.getField("属性名"); //获取公开属性
类对象.getDeclaredField("属性名"); //获取私有属性
类对象.getMethod("方法名", null); //通过指定方法名称获取公开无参方法对象
类对象.getMethods(); //获取所有公开方法对象
类对象.getDeclaredMethods(); //获取所有方法对象
类对象.getDeclaredMethod("方法名", 方法参数的类型......); //通过指定方法名称获取私有有参方法对象
类对象.getConstructor(构造参数类型); //调用公开有参构造
类对象.getDeclaredConstructor(构造参数类型); //调用私有有参构造
类对象.isInstance(obj);// 判断是否是某个类的实例
Field:
属性对象.getName(); //获取属性名
属性对象.getType(); 获取属性的类型
属性对象.getModifiers(); //获取属性的修饰符
属性对象.set(obj,obj); //设置属性值 1:类实例化对象 要设置的参数值
……
Method:
方法对象.invoke(类实例化对象, 方法的参数数组); //执行方法
Constructor:
NewInstance();//通过构造获取到类的实例对象
三、站在反射的角度看待ORM框架
ORM框架的简单实现(JDBC部分):
jdbc连接类:
public class DBConnectionFactory {
public static Connection getDBConnection() {
Connection conn = null;
try {
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/sixstar_test";
String user = "root";
String password = "123";
conn = DriverManager.getConnection(url, user, password);
} catch (Exception e) {
e.printStackTrace();
}
return conn;
}
public static void main(String[] args){
Connection dbConnection = DBConnectionFactory.getDBConnection();
System.out.println(dbConnection);
}
}
CRUD基类:
得到增加SQL的方法:
/**
* 解析出保存对象的sql语句
*
* @param object :需要保存的对象
* @return:保存对象的sql语句
*/
public static String getSaveObjectSql(Object object) {
// 定义一个sql字符串
String sql = "insert into ";
// 得到对象的类
Class c = object.getClass();
// 得到对象中所有的方法
Method[] methods = c.getMethods();
// 得到对象中所有的属性
Field[] fields = c.getFields();
// 得到对象类的名字
String cName = c.getName();
// 从类的名字中解析出表名
String tableName = cName.substring(cName.lastIndexOf(".") + 1, cName.length());
sql += tableName + "(";
List<String> mList = new ArrayList<String>();
List vList = new ArrayList();
for (Method method : methods) {
String mName = method.getName();
if (mName.startsWith("get") && !mName.startsWith("getClass")) {
String fieldName = mName.substring(3, mName.length());
mList.add(fieldName);
System.out.println("字段名字----->" + fieldName);
try {
Object value = method.invoke(object, null);
System.out.println("执行方法返回的值:" + value);
if (value instanceof String) {
vList.add("\"" + value + "\"");
System.out.println("字段值------>" + value);
} else {
vList.add(value);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
for (int i = 0; i < mList.size(); i++) {
if (i < mList.size() - 1) {
sql += mList.get(i) + ",";
} else {
sql += mList.get(i) + ") values(";
}
}
for (int i = 0; i < vList.size(); i++) {
if (i < vList.size() - 1) {
sql += vList.get(i) + ",";
} else {
sql += vList.get(i) + ")";
}
}
return sql;
}
增加方法:
/**
* 将对象保存到数据库中
*
* @param object :需要保存的对象
* @return:方法执行的结果;1:表示成功,0:表示失败
*/
public int saveObject(Object object) {
Connection con = DBConnectionFactory.getDBConnection();
String sql = getSaveObjectSql(object);
try {
Statement statement = (Statement) con.createStatement();
PreparedStatement psmt = con.prepareStatement(sql);
psmt.executeUpdate();
return 1;
} catch (SQLException e) {
e.printStackTrace();
return 0;
}
}
查询方法:
/**
* 从数据库中取得对象
*
* @paramarg0 :对象所属的类
* @paramid :对象的id
* @return:需要查找的对象
*/
public Object getObject(String className, int Id) {
// 得到表名字
String tableName = className.substring(className.lastIndexOf(".") + 1, className.length());
// 根据类名来创建Class对象
Class c = null;
try {
c = Class.forName(className);
} catch (ClassNotFoundException e1) {
e1.printStackTrace();
}
// 拼凑查询sql语句
String sql = "select * from " + tableName + " where Id = " + Id;
System.out.println("查找sql语句:" + sql);
// 获得数据库链接
Connection con = DBConnectionFactory.getDBConnection();
// 创建类的实例
Object obj = null;
try {
Statement stm = con.createStatement();
// 得到执行查寻语句返回的结果集
ResultSet set = stm.executeQuery(sql);
// 得到对象的方法数组
Method[] methods = c.getMethods();
// 遍历结果集
while (set.next()) {
System.out.println(c);
obj = c.newInstance();
// 遍历对象的方法
for (Method method : methods) {
String methodName = method.getName();
// 如果对象的方法以set开头
if (methodName.startsWith("set")) {
// 根据方法名字得到数据表格中字段的名字
String columnName = methodName.substring(3, methodName.length());
// 得到方法的参数类型
Class[] parmts = method.getParameterTypes();
if (parmts[0] == String.class) {
// 如果参数为String类型,则从结果集中按照列名取得对应的值,并且执行改set方法
method.invoke(obj, set.getString(columnName));
}
if (parmts[0] == int.class) {
method.invoke(obj, set.getInt(columnName));
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return obj;
}
从以上代码可以看出我们只需要传入一个Object对象就可以对数据库进行CRUD操作,Object对象是可以为任意实体类的,所以说这个类是我们通过反射的机制来写的一个对所有数据库表CRUD的基类。
类似与ORM框架Hibernate、Mybatis框架底层都是使用反射机制来保证框架的灵活性。我曾阅读过这些框架的源码,底层实现几乎就是使用这样的方式实现的,只是我这个实现写的有点简单,大家可以参考我的这篇文章再去阅读框架的源码,试着自己写一个ORM框架。