首页 > 代码库 > Java反射入门

Java反射入门

  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反射所提供的基本功能,下面简单模拟HibernateOR映射,基本思路就是传入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);		}	}

 

总结

 

  上面的映射方式是用构造字符串方式,set+”字段名映射类的属性,Hibernate的映射当然要复杂的多,通过配置文件来配置。但是实现原理差不多,Java映射为Java灵活性做了很好的支持。