Java反射机制主要提供了以下功能: 在运行时判断任意一个对象所属的类;在运行时构造任意一个类的对象;在运行时判断任意一个类所具有的成员变量和方法;在运行时调用任意一个对象的方法;生成动态代理。个人理解是,反射是在程序运行时,对未知的类进行操作的一种方法。例如,eclipse的这么多第三方插件,在eclipse在开发时肯定是不会知道有哪些,是未知的。而只要预留了接口,通过反射,就可动态的加载插件,增加系统的可扩展性。
先说一下Object类,Object类是类层次结构的根,Java中所有的类都继承自这个类,也是唯一没有父类的类。在其中声明了一些方法,也就是每个对象都会有的方法,如常见的equals()、toString()、getClass()。
getClass方法就可以获得这个对象的Class对象,如下
方法1:Class c = SomeClass.getClass();
方法2:Class c = Class.forName("类名");
此时可以获得类的各种信息:
a)Class.getDeclaredField(String name);
返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。
b)Class.getDeclaredFields();
返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段。
c)Class.getField(String name);
返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段。
d)Class.getFields();
返回一个包含某些 Field 对象的数组,这些对象反映此 Class 对象所表示的类或接口的所有可访问公共字段。
。。。。。。
进而就可以创建出这个未知的类的实例。
Object o = c.newInstance();
看到这想起来,刚学数据库的时候会有这样懵逼的一句话,“Class.forName("com.mysql.jdbc.Driver.class").newInstance()”,其实这就是创建了一个驱动对象的实例。
下面来一个小例子试一下:
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
*
* @author Zongzan
*注意,getMethods(),getFields()只返回public类型的成员变量或方法
*Test2类中的b就没有被计算在内.
*若要返回所有声明的函数,可以用getDeclaredMethods().
*/
class Test2{
public int a;
int b;
public void print(){
}
}
public class test {
public static void main(String[] args) {
Test2 it = new Test2();
Class c = null;
c = it.getClass();
Method[] m = c.getMethods();
Field[] f = c.getFields();
System.out.println("name :" + c.getName());
System.out.println("method :" + m[0]);
System.out.println("field :" + f[0]);
System.out.println("num of field :" + f.length);
}
}
运行结果:
name :dao.Test2
method :public void dao.Test2.print()
field :public int dao.Test2.a
num of field :1
/***************************************************************割*******************************************************************/
以前写过一个java web的程序,我把每个表都对应了一个Dao,一下就搞了20多个Dao,但是数据库的增删改查变化都不大,每个dao类里面就copy其他类的代码,一不小心就复制错了,很恶心。。。后来才发现,只需要一个Dao,通过反射就可以实现所有表的增删改查。下面是个小例子,不很完整,只用来说明反射在这里的应用。
一、数据库如下:
1.user表
2.message表
二、pojo类
package pojo;
public class User {
int idUser;
String userName;
String sex;
public int getIdUser() {
return idUser;
}
public void setIdUser(int idUser) {
this.idUser = idUser;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
}
package pojo;
public class Message {
int idMessage;
String recvname;
String content;
public int getIdMessage() {
return idMessage;
}
public void setIdMessage(int idMessage) {
this.idMessage = idMessage;
}
public String getRecvname() {
return recvname;
}
public void setRecvname(String recvname) {
this.recvname = recvname;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
三、重点来了,dao类:
package dao;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import util.util;
public class Dao {
public static String getAddObjectSQL(Object obj){
Class<?> c = obj.getClass();
Method[] meths = c.getMethods(); //获得所有方法
Field[] fields = c.getFields(); //获得所有属性
String cName = c.getName(); //获得类名
//解析获得表名,因此要求pojo中的类的名字和表名一一对应
//至此得到了组装sql语句中所需的所有信息
String tableName = cName.substring(cName.lastIndexOf(".") + 1, cName.length());
//调用getSQL()方法,组装sql语句
return new util().getSQL(obj, meths, fields, cName, tableName);
}
}
通过回调,我们得到了sql语句所需要的各种信息(注意表名是通过解析类的方法名得到的,所以要有规范的命名)。再用一个工具类的getSQL()方法做sql语句的组装:
package util;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
public class util {
public String getSQL(Object obj, Method[] meths, Field[] fields, String cName, String tableName){
String sql = "insert into ";
sql += tableName + " (";
// System.out.println("表名--" + tableName);
List<String> mList = new ArrayList<String>(); //每个字段的名字
List<Object> vList = new ArrayList<Object>(); //字段值
for (Method method : meths) {
String mName = method.getName();
if (mName.startsWith("get") && !mName.startsWith("getClass")) {
String fieldName = mName.substring(3, mName.length());
mList.add(fieldName); //得到每个字段的名字,即“get”后面的部分
// System.out.println("字段名--" + fieldName);
try {
//value是执行方法返回值,method方法参数为null
Object value = method.invoke(obj, null); //obj为包含method方法的对象。若有需要,可用Object obj = c.newInstance()创建
if (value instanceof String) {
vList.add("\"" + value + "\"");
// System.out.println("字段值--" + value);
} else {
vList.add(value);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
int count = 0;
for (String s : mList) {
if (count < mList.size() - 1) {
sql += s + ",";
} else {
sql += s + ") values (";
}
count++;
}
count = 0;
for (Object s : vList) {
if (count < vList.size() - 1) {
sql += s + ",";
} else {
sql += s + ")";
}
count++;
}
return sql;
}
}
最后,通过一个测试类来测试一下:
import java.sql.PreparedStatement;
import java.sql.SQLException;
import com.mysql.jdbc.Connection;
import dao.Dao;
import database.DataBase;
import pojo.Message;
import pojo.Relation;
import pojo.User;
public class OprateTest {
public static void main(String[] args){
User u = new User();
u.setIdUser(1);
u.setSex("man");
u.setUserName("zongzan");
Message m = new Message();
m.setContent("hahahaha~");
m.setRecvname("zongzan");
Dao dao = new Dao();
String sql1 = dao.getAddObjectSQL(u);
System.out.println("sql1 =" + sql1);
String sql2 = dao.getAddObjectSQL(m);
System.out.println("sql2 =" + sql2);
Connection conn = (Connection) DataBase.getConnection();
PreparedStatement pstmt = null;
try {
pstmt = conn.prepareStatement(sql1);
pstmt.executeUpdate();
pstmt = conn.prepareStatement(sql2);
pstmt.executeUpdate();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("\nInsert Error\n");
}finally{
DataBase.freeStatement(conn, pstmt);
}
}
}
sql语句打印输出为:
sql1 = insert into User (IdUser,Sex,UserName) values (1,"man","zongzan")
sql2 = insert into Message (Content,Recvname,IdMessage) values ("hahahaha~","zongzan",0)
数据库:
这只是个例子,还有很多没做,反射机制会损耗效率,因此应该对反射得到的类做缓存等等。