Java这么多高灵活性,非常多都是利用反射来实现的,所谓的反射是指,编译期间全然未知的classes,执行时,对任一个类(依据类名-字符串),可以知道这个类的全部属性和方法;对于任一个对象,都可以调用它的随意一个方法和属性。
简而言之,Java反射机制主要提供了下面功能:
Ø 在执行时推断随意一个对象所属的类;obj.getClass()
Ø 在执行时构造随意一个类的对象;
Ø 在执行时推断随意一个类所具有的成员变量和方法;
Ø 在执行时调用随意一个对象的方法。
以下我们先用一个简单小例体现一下Java Reflect上述的功能,然后模拟一下Java Reflect应用之中的一个Hibernate的 ORMapping的实现。
Java反射功能測试
首先定义一个User.java类,代码例如以下:
package reflect; /** * @author--zhipeng */ public class User { private String name; //构造方法1(默认构造方法)*********************** public User(){ } //构造方法2 public User(String name){ this.name=name; } //******自己定义方法************* public void getMessage(){ System.out.print("kobe bryant never stop trying"); } //******重写toString方法,在測试的时候会用到***** @Override public String toString() { return "name:"+this.name; } //************************** public String getName() { return name; } public void setName(String name) { this.name = name; } }
以下我将在ReflectTest.java类中,首先获得User的类名,然后依据类名来获得类的方法、属性、注解等,并调用方法,代码例如以下:
package reflect; import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import reflect.User; /** * @author--zhipeng */ public class ReflectTest { public static void main(String[] args) throws Exception { Class clazz = User.class;//获得User的类名,返回reflect.User Object obj = create(clazz);//创建User的一个对象 System.out.println(obj);//输出对象,会调用对象的toString方法 System.out.println("---------"); invoke1(obj, "getMessage");//调用User对象的getMessage方法 } /* **依据类名,new一个对象,并返回*/ static Object create(Class clazz) throws Exception { //假设clazz含有无參数的构造方法,能够例如以下方式实例化 //clazz.newInstance(); //依据类名和參数(类型、个数),找到对应的构造方法-以下创建构造方法參数为String的构造方法 Constructor con=clazz.getConstructor(String.class); //实例化对象 Object obj=con.newInstance("myName is zhipeng"); //返回对象 return obj; } /* **依据对象,方法名(字符串),来调用方法*/ static void invoke1(Object obj, String methodName)throws Exception{ //getDeclaredMethods能够获取类本身(不包含父类)全部方法的名字(包含私有方法)**一般不用这样的方法,私有的属性一般不能改动 Method[] ms = obj.getClass().getDeclaredMethods(); //getMethods能够获取类本身,以及父类的方法的名字,但不包含私有的方法 ms = obj.getClass().getMethods(); for (Method m : ms) { //假设方法名字匹配,则反射调用方法 if (methodName.equals(m.getName())) m.invoke(obj, null); } /* **防止方法重载,可用以下的方式(能够指明參数)--与上面的for循环(无法防止方法重载)一个效果 **Method m = obj.getClass().getMethod(methodName, null); **m.invoke(obj, null); */ } /* **依据类名获取类的属性(一般不直接操作属性)*/ static void field(Class clazz) throws Exception { Field[] fs = clazz.getDeclaredFields(); //fs = clazz.getFields(); for (Field f : fs) System.out.println(f.getName()); } /* **依据类名获取类的注解*/ static void annon(Class clazz) throws Exception { Annotation[] as = clazz.getAnnotations(); for (Annotation a : as) System.out.println(a.getName()); } }
执行结果:
简单模拟ORMapping
上面我们測试了Java反射所提供的基本功能,以下简单模拟Hibernate的OR映射,基本思路就是传入sql语句,将结果集包装为object,并将结果集的字段与object的属性映射好,代码例如以下:
package reflect; import java.lang.reflect.InvocationTargetException; 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; import java.util.ArrayList; import java.util.List; /** * @author--zhipeng */ public class ORMTest { public static void main(String[] args)throws Exception{ //将select结果集(这里为一条)包装为User对象,并将结果集的字段,与User对象的属性映射 User user = (User) getObject( "select id as Id, name as Name, birthday as Birthday, money as Money from user where id=1", User.class); System.out.println(user); } //获取一个ResultSet的字段(列)名 private static String[] getColNames(ResultSet rs) throws SQLException { ResultSetMetaData rsmd = rs.getMetaData(); int count = rsmd.getColumnCount(); String[] colNames = new String[count]; for (int i = 1; i <= count; i++) { colNames[i - 1] = rsmd.getColumnLabel(i); } return colNames; } //传入一个sql语句与类名,返回一个对应类的对象 static Object getObject(String sql, Class clazz) throws SQLException,Exception, IllegalAccessException, InvocationTargetException { Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { //JdbcUtils为自己定义类-这里不多介绍 conn = JdbcUtils.getConnection(); ps = conn.prepareStatement(sql); rs = ps.executeQuery(); //获取sql语句运行结果集的列名 String[] colNames = getColNames(rs); Object obj=null; //获取clazz的全部方法名 Method[] ms = clazz.getMethods(); while (rs.next()) { obj = clazz.newInstance();//要求类必须有參数为空的构造方法,相似Hibernate for (int i = 0; i < colNames.length; i++) { //用set+"字段名"来匹配,实体类中字段的set方法 String colName = colNames[i]; String methodName = "set" + colName; //OR映射 for (Method m : ms) { if (methodName.equals(m.getName())) { m.invoke(obj, rs.getObject(colName)); break; } } } } return obj; } finally { JdbcUtils.free(rs, ps, conn); } } }
上面的getObject(String sql, Class clazz)方法为映射一条数据,假设映射多条数据可改方法为:
static List<Object> getObjects(String sql, Class clazz) throws SQLException, Exception, IllegalAccessException, InvocationTargetException { Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { conn = JdbcUtils.getConnection(); ps = conn.prepareStatement(sql); rs = ps.executeQuery(); String[] colNames = getColNames(rs); List<Object> objects = new ArrayList<Object>(); Method[] ms = clazz.getMethods(); while (rs.next()) { Object object = clazz.newInstance(); for (int i = 0; i < colNames.length; i++) { String colName = colNames[i]; String methodName = "set" + colName; for (Method m : ms) { if (methodName.equals(m.getName())) { m.invoke(object, rs.getObject(colName)); break; } } objects.add(object); } } return objects; } finally { JdbcUtils.free(rs, ps, conn); } }
总结
Java反射这么灵活,无非是将曾经“编译时”必须做的事推迟到了“执行时”,而方式也就是由原先的对象到了灵活可配的“字符串”。
上面的映射方式是用构造字符串方式,set+”字段名”映射类的属性,Hibernate的映射当然要灵活复杂的多,通过配置文件来配置。可是实现原理差点儿相同,Java映射为Java灵活性做了非常好的支持。