一.java反射基础
1.1 什么叫java反射?
答:程序运行期间,动态的获取类的基本信息。比如:创建对象,调用类的方法,获得类的基本结构。这样给程序设计提供了很大的灵活性。个人总结就是:根据动态需求,生成动态的响应。java的Class类是java反射机制的基础,反射也是让虚拟机加载指定类。这就用到了java的类装载机制,jvm加载字节码文件,并生成class对象,并进行类的初始化。
1.2 java反射的好处?
答:java反射给程序设计提供了很大的灵活性,解决了很多死的东西。可以根据动态需求,产生动态响应。常用框架中基本都用到了反射,尤其在hibernate这种持久层框架中,用反射生成sql语句。
1.3 java反射不好的地方
答:缺点是对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。
这类操作总是慢于直接执行相同的操作。
1.4java反射发生在程序运行时期,java程序不都是被编译成.class才能执行么,即先编译再执行么?
答:现在再看JAVA反射,与之有关的概念全部都是“在运行时”,比如说
1.在运行时判断任意一个对象所属的类
2.在运行时构造任意一个类的对象
3.在运行时判断任意一个类所具有的成员变量和方法(通过反射甚至可以调用private方法)
4.在运行时调用任意一个对象的方法(注意:前提都是在运行时,而不是在编译时)
对这有些费解,JAVA运行的不都是编译之後的.class文件吗?先编译再运行啊,为什麽不是“在编译时”而是“在运行时”呢??? 请教下编译和运行的区别,希望能说详细些,好指点下小弟对JAVA反射的理解。感谢
答:
编译只是编译成了字节码,就是可执行的.class文件
运行时识别,主要是java的RTTI(运行时类型识别)机制和反射机制,而两者之间的区别在于:
”对RTTI来说,编译器在编译期打开和检查.class文件。(换句话说,我们可以用“普通”的方式调用一个对象的所有方法。),而对于反射机制来说,.class文件在编译期是不可获取的,所以是在运行期打开和检查.class文件。“
但是同样的,反射也需要加载这个类的Class对象,所以那个类的class对象对于jvm必须是可取的的,比如在本地机器上,或者通过网络取得,比如取得一串字节串。
另:运行是apache是执行java文件生成的.class文件,不是java文件
二.java反射常见用途有哪些?
答:1).jdbc中反射生成数据库驱动类的对象,Class.forName("com.mysql.jdbc.Driver")。---动态运行编译好的类/动态生成类的对象
2).spring ioc中,通过反射生成各种bean实例。通过xml文档的配置bean id=xxx,class=com.pojo.xxx。指定具体类,然后calss.forName()生成bean.并将它们放进hshmap中。
3).hibernate等orm框架中动态生成sql语句。相当于编程程序中的Dao实现类。
4).软件升级中。比如一个大型的软件,不可能一次就把把它设计的很完美,当这个程序编译后,发布了,当发现需要更新某些功能时,我们不可能要用户把以前的卸载,再重新安装新的版本。采用静态的话,需要把整个程序重新编译一次才可以实现功能的更新。而采用反射机制的话,它就可以不用卸载,只需要在运行时才动态的创建和编译,就可以实现该功能。
5).动态编译,如果一个程序员在写程序的时需要使用第二个程序员所写的类,但第二个程序员并没完成他所写的类。那么第一个程序员的代码是不能通过编译的。此时,利用Java反射的机制,就可以让第一个程序员在没有得到第二个程序员所写的类的时候,来完成自身代码的编译。即动态编译!
6).编写Dao的实现类,节省不同Pojo类的增删改查代码,只写一个Dao实现类,运用反射生成对应的sql语句。避免代码冗余。
三.java反射API
3.1获取相应的Class类对象
要正确使用Java反射机制就得使用java.lang.Class 这个类。当一个类被加载以后,Java虚拟机就会自动产生一个Class对象。通过这个Class对象我们就能获得加载到虚拟机当中这个Class对象对应的方法、成员以及构造方法的声明和定义等信息。
Date date1 = new Date();
Date date2 = new Date();
Class c1 = date1.getClass();
Class c2 = date2.getClass();
System.out.println(c1.getName()); // java.util.Date
System.out.println(c1 == c2); // true
代码中的date1和date2的getClass方法返回了相同的Class对象(c1==c2的值为true)。这是因为,对于相同的类,JVM只会载入一次,而与该类对应的Class对象也只会存在一个,无论该类实例化了多少对象。
java.lang.Class是一个比较特殊的类,它用于封装被装入到JVM中的类(包括类和接口)的信息。简单的说,获取某一个类所对应的Class对象可以通过如下三种途径:
1).Object的getClass方法
java.lang.Object中定义有getClass方法:
public final Class getClass(), 所有Java对象都具备这个方法。该方法用于返回与调用该方法对象所属类关联的Class对象,例如: Date date1 = new Date(); Date date2 = new Date(); Class c1 = date1.getClass(); Class c2 = date2.getClass(); System.out.println(c1.getName()); // java.util.Date System.out.println(c1 == c2); // true 上面的代码中,调用Date对象date1的getClass方法将返回用于封装Date类信息的Class对象。这里调用了Class类的getName方法: public String getName() 这个方法的含义很直观,即返回所封装的类的名称。 需要注意的是,代码中的date1和date2的getClass方法返回了相同的Class对象(c1==c2的值为true)。这是因为,对于相同的类,JVM只会载入一次,而与该类对应的Class对象也只会存在一个,无论该类实例化了多少对象。 另外,需要强调的是,当一个对象被其父类的引用或其实现的接口类型的引用所指向的时,getClass方法返回的是与对象实际所属类关联的Class对象。例如: List list = new ArrayList(); System.out.println(list.getClass().getName()); // java.util.ArrayList 上面的代码中,语句list.getClass()方法返回的是list所指向对象实际所属类java.util.ArrayList对应的 Class对象而并未java.util.List所对应的Class对象。有些时候可以通过这个方法了解一个对象的运行时类型,例如: HashSet set = new HashSet(); Iterator it = set.iterator(); System.out.println(it.getClass().getName()); //java.util.HashMap$KeyIterator 从代码可以看出,HashSet的iterator方法返回的是实现了Iterator接口的HashMap内部类(KeyIterator)对象。 因为抽象类和接口不可能实例化对象,因此不能通过Object的getClass方法获得与抽象类和接口关联的Class对象。
2). 使用.class的方式
使用类名加“.class”的方式即会返回与该类对应的Class对象。例如: Class clazz = String.class; System.out.println(clazz.getName()); // java.lang.String 这个方法可以直接获得与指定类关联的Class对象,而并不需要有该类的对象存在。
3). 使用Class.forName方法
Class有一个著名的static方法forName: public static Class forName(String className) throws ClassNotFoundException 该方法可以根据字符串参数所指定的类名获取与该类关联的Class对象。如果该类还没有被装入,该方法会将该类装入JVM。 该方法声明抛出ClassNotFoundException异常。顾名思义,当该方法无法获取需要装入的类时(例如,在当前类路径中不存在这个类),就会抛出这个异常。 例如,如果当前类路径中存在Foo类: package org.whatisjava.reflect; public class Foo { public Foo() { System.out.println("Foo()"); } static { System.out.println("Foo is initialized"); } } 运行下面的代码: Class clazz = Class.forName("org.whatisjava.reflect.Foo"); 控制台会有如下输出: Foo is initialized Class.forName("org.whatisjava.reflect.Foo")首先会将reflection.Foo类装入JVM,并 返回与之关联的Class对象。JVM装入Foo类后对其进行初始化,调用了其static块中的代码。需要注意的是:forName方法的参数是类的完 整限定名(即包含包名)。
区别于前面两种获取Class对象的方法。使用Class.forName方法所要获取的与之对应的Class对象的类可以通过字符串的方式给定。该方法通常用于在程序运行时根据类名动态的载入该类并获得与之对应的Class对象。
3.2 java.lang.Class常用API
① public static Class<?> forName(String className) :native 方法,动态加载类。非常重要。
如在sql中动态加载驱动程序:class.forName(sqlDriver);
②public T newInstance() :根据对象的class新建一个对象,用于反射。非常重要。可用在反射中构建对象,调用对象方法:
class doubleClass= Class.forName("java.lang.Double");
Object objDouble = doubleClass.newInstance();
如在javaBean中就应用了这个方法,因为java默认要有一个无参构造函数。
③public ClassLoader getClassLoader() ,获得类的类加载器Bootstrap。ClassLoader(一般为system classloader)。重要。
④public String getName() :获取类或接口的名字。
⑤public native Class getSuperclass():获取类的父类,继承了父类则返回父类,否则返回java.lang.Object。返回Object的父类为空-null。
⑥public Package getPackage() :反射中获得package,如java.lang.Object 的package为java.lang。
⑦public native int getModifiers() : 反射中获得修饰符,如public static void等 。
⑧
public Field getField(String name):反射中获得域成员。
public Field[] getFields() :获得域数组成员。
public Method[] getMethods() :获得方法。
⑨public Method getDeclaredMethod(String name, Class<?>... parameterTypes):加个Declared代表本类,继承,父类均不包括。
⑩public Constructor<?>[] getConstructors() :获得所有的构造函数。
四.java反射常见实例源码
下面是反射常见用途中的源码
1.类似spring ioc源码 public class BeanFactory { private static Map<String, Object> beanMap = new HashMap<String, Object>(); public void init(String xml) { try { //从class目录下获取指定的xml文件 ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); InputStream ins = classLoader.getResourceAsStream(xml); //解析xml SAXReader reader = new SAXReader(); Document doc = reader.read(ins); Element root = doc.getRootElement(); Element beanNode; //遍历bean for (Iterator i = root.elementIterator("bean"); i.hasNext();) { beanNode = (Element) i.next(); // 获取bean的属性id和class Attribute id = beanNode.attribute("id"); Attribute className = beanNode.attribute("class"); //利用Java反射机制,通过class的名称获取Class对象 Class beanClass = Class.forName(className.getText()); //实例化对象 Object obj = beanClass.newInstance(); //设置对象属性值 setBeanProperty(beanNode,beanClass,obj); // 将对象放入beanMap中,其中key为id值,value为对象 beanMap.put(id.getText(), obj); } } catch (Exception e) { System.out.println(e.toString()); e.printStackTrace(); } } //设置对象属性值 @SuppressWarnings("unchecked") private static void setBeanProperty(Element beanNode,Class beanClass,Object obj) throws Exception{ // 设置值的方法 Method method = null; // 获取对应class的信息 java.beans.BeanInfo beanInfo = java.beans.Introspector.getBeanInfo(beanClass); // 获取其属性描述 java.beans.PropertyDescriptor proDescriptor[] = beanInfo.getPropertyDescriptors(); // 遍历该bean的property属性 for (Iterator ite = beanNode.elementIterator("property"); ite.hasNext();) { Element beanPro = (Element) ite.next(); // 获取该property的name属性 Attribute name = beanPro.attribute("name"); Attribute ref = beanPro.attribute("ref"); Object proVal = null; if(ref!=null){ proVal=beanMap.get(ref.getValue()); }else{ // 获取该property的子元素value的值 for (Iterator ite1 = beanPro.elementIterator("value"); ite1.hasNext();) { Element node = (Element) ite1.next(); proVal = node.getText(); break; } } for (int k = 0; k < proDescriptor.length; k++) { if (proDescriptor[k].getName().equalsIgnoreCase(name.getText())) { method = proDescriptor[k].getWriteMethod(); // 利用Java的反射极致调用对象的某个set方法,并将值设置进去 method.invoke(obj, proVal); } } } } /** * * 通过bean的id获取bean的对象. * * * @param beanName * bean的id * * @return 返回对应对象 */ public static Object getBean(String beanName) { Object obj = beanMap.get(beanName); return obj; } static{ BeanFactory factory = new BeanFactory(); factory.init("config.xml"); }
2.利用java反射生成sql语句 public class NetJavaSession { /** * 解析出保存对象的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; } public static List getDatasFromDB(String tableName, int Id) { return null; } /** * 将对象保存到数据库中 * * @param object * :需要保存的对象 * @return:方法执行的结果;1:表示成功,0:表示失败 */ public int saveObject(Object object) { Connection con = Connect2DBFactory.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; } } /** * 从数据库中取得对象 * * @param arg0 * :对象所属的类 * @param id * :对象的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 = Connect2DBFactory.getDBConnection(); // 创建类的实例 Object obj = null; try { Statement stm = con.createStatement(); // 得到执行查寻语句返回的结果集 ResultSet set = stm.executeQuery(sql); // 得到对象的方法数组 Method[] methods = c.getMethods(); // 遍历结果集 while (set.next()) { 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; } }
本文为博主原创文章,谢绝转载。谢谢。
本博文参考有http://caizi12.iteye.com/blog/2168801
http://blog.csdn.net/a379039233/article/details/6158816/
http://blog.csdn.net/bigtree_3721/article/details/50886016
感谢上述作者。