Java反射机制,可以说我们平常开发中用的很多,尽管作为开发人员,我们并没有什么感觉,那是因为框架的设计者,已经为我们封装好了,我们只需要用去调用一下就可以了。以前也用到过反射,包括自己也写过,也学到过,但是我感觉都比较浅,今天有时间,我觉得有必要去慢慢的深入了解一下。
我们把能够分析类能力的程序称为反射(reflective).反射机制功能及其强大,简单说几个反射的用途:
1.在运行中分析类的能力;
2.在运行中查看对象,例如编写一个通用的tostring方法;
3.实现通用的数组操作代码;
4.利用Method对象;
其实反射运用起来很简单,我们只要记住,运用反射的第一步就是得到Class,那么这个Class是什么啦,我们要注意一点,这个Class和class不同,class代表的是一种类型,一个关键字,他是Java里面的基本元素,我们都知道Java里面的普通类都是以class修饰的,而枚举是以enum修饰的等等。而我们的Class则表示:
在程序运行期间,Java系统为会所有的对象维护一个被称为运行时的类型标识。这个信息跟踪着每个对象所属的类。我们把有关这个类的所有信息都保存在Class里面。只要我们获得了Class,就可以根据其特定的方法,获取我们想要的结果;
这里不深入了解Class,我们只需要记住,Class就像开启宝库的钥匙,里面有我们所需要的类,对象,方法,构造器,但是如果没有这个钥匙,一切都是白搭。所以我们要先构造出Class ;
来看看构造Class的三种方法;
1.Class.forName("类名字符串") (注意:类名字符串必须是全称,包名+类名)
2.类名.class
3.实例对象.getClass()
先创建一个简单的Person类;
<pre name="code" class="java">public class Person implements Serializable{ private int id; public String name; protected String address; int age; static{ System.out.println("静态初始化块"); } { System.out.println("初始化快"); }
public static void main(String[] args) { try { Class c1=Class.forName("com.cn.vo.Person"); //通过类名得到Class,包名也属于类名的一部分 Class c2=Person.class; //通过类对象得到Class Person p=new Person(); Class c3=p.getClass(); //通过对象引用得到Class System.out.println(c1); System.out.println(c2); System.out.println(c3); System.out.println(c1==c3); } catch (ClassNotFoundException e) { e.printStackTrace(); } }
这里通过类对象得到Class是最简单的一种方法,但是这里要注意一点,就是一个Class对象实际上表示的是一个类型,而这个类型未必是一种类,例如int不是类,但int.class是一个Class类型的对象。
但是不论使用哪一种方法,最后都只能得到一个Class,因为我们的静态初始化块只执行了一次,证明我们的类只加载了一次。
所以,生成Class对象的过程其实是如此的:
当我们编写一个新的java类时,JVM就会帮我们编译成class对象,存放在同名的.class文件中。在运行时,当需要生成这个类的对象,JVM就会检查此类是否已经装载内存中。若是没有装载,则把.class文件装入到内存中。若是装载,则根据class文件生成实例对象。由此我们可以得出一个结论,一个java类只有一个Class。
这里主要是因为类加载器,ClassLoader采用了双亲委托机制,一个自定义ClassLoader加载一个类之前,它都会先委托它的父亲ClassLoader进行加载,只有当父亲ClassLoader无法加载成功后,才会由自己加载;来看一看源代码:
protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException { // 首先检查该name指定的class是否有被加载 Class c = findLoadedClass(name); if (c == null) { try { if (parent != null) { //如果parent不为null,则调用parent的loadClass进行加载 = parent.loadClass(name, false); } else { //parent为null,则调用BootstrapClassLoader进行加载 c = findBootstrapClass0(name); } } catch (ClassNotFoundException e) { //如果仍然无法加载成功,则调用自身的findClass进行加载 c = findClass(name); } } if (resolve) { resolveClass(c); } return c; }好了,最终结论是不管我们通过什么方法,我们只需要得到一个Class就够了,也就意味着开启宝库的钥匙只有一个把,无论通过什么方法得到这都不是最重要的,关键是要能开启宝库的大门;
既然我们得到了Class,就可以用它来得到很多东西;
这里我们用反射类,reflect提供的API方法来打印一个类的所有信息;
简单说一下API的主要几个类
下面写几个小例子来灵活的运用一下Java的反射机制。
打印出一个类的完整信息;下面这个方法会打印出类的包名,类名,方法名和所有域名;
try { String s="com.cn.vo.Person"; Class c1=Class.forName(s); //通过类名得到Class StringBuffer sb=new StringBuffer("package "); //构造包名关键字 sb.append(c1.getPackage().getName()+";"+"\n"); //得到包名 sb.append(Modifier.toString(c1.getModifiers())+" class "+c1.getSimpleName()+"{"+"\n"); //得到类的修饰符及类名 Field[] file=c1.getDeclaredFields(); //得到所有的属性 for(Field f:file){ sb.append(Modifier.toString(f.getModifiers())+" "); //得到修饰符 sb.append(f.getType().getSimpleName()+" "); //得到变量类型 sb.append(f.getName()+";"); //得到变量名称 sb.append("\n"); } Method[] methods=c1.getDeclaredMethods(); //得到所有的方法 for(Method m:methods){ Class retType=m.getReturnType(); //得到方法的类型 sb.append(Modifier.toString(m.getModifiers())+" "); //得到修饰符 sb.append(retType+" "); sb.append(m.getName()+";"); //得到方法名称 sb.append("\n"); } sb.append("}"); } catch (Exception e) { e.printStackTrace(); }
访问设置没有get,set方法的类;
我们平常在开发中一个javabean都有其get和set方法,可是如果没有get ,set方法,那么我们怎么样去得到它的值和改变它的值啦,我们可以利用Java反射技术;
try { Person p=new Person("张三", "湖北襄阳", 23); Class c2=p.getClass(); Field f=c2.getDeclaredField("address"); //得到域名称 f.setAccessible(true); //设置为true也可以访问私有类; Object st=f.get(p); //得到域名值 f.set(p, "武汉"); //重新设置值 System.out.println(p); } catch (Exception e) { e.printStackTrace(); }这里有一个方法叫做setAccessible方法,它是Field,Method和Constructor类的公共超类,这个特性是调试,持久存储和相似机制提供的。我们要访问private修饰的类的时候必须要设置为true;
设计一个通用的toString方法;
public static String toString(Object obj){ if(obj==null) return null; Class c1=obj.getClass(); if(c1==String.class) return (String)obj; //如果传递进来的是String对象,就直接打印出来结果 StringBuffer st=new StringBuffer(); try { if(c1.isArray()){ //数组对象 st.append(c1.getComponentType()+"[]{"); //类型 for(int i=0;i<Array.getLength(obj);i++){ //遍历数组 if(i>0) st.append(","); Object val=Array.get(obj, i); //循环取值 st.append(val); } return st.append("}").toString(); } //每个类的父类都是Object,这里不打印; while(c1.getSuperclass()!=null&&!c1.getSimpleName().equals("Object")){ st.append(c1.getName()+"["); //类名 Field[] fields=c1.getDeclaredFields(); //得到所有的属性 AccessibleObject.setAccessible(fields, true); //设置私有属性访文权限 for(Field f:fields){ if(!st.toString().endsWith("[")) st.append(","); st.append(f.getName()+"="); //打印、、得到属性名称 Object val=f.get(obj); //得到属性值 st.append(val); } st.append("]"); c1=c1.getSuperclass(); //得到父类向上递归; } } catch (Exception e) { e.printStackTrace(); } return st.toString(); }尽管反射机制很强大,可以让我们编写更具有通用性的程序,但我们日常做开发模块的时候还是要少用反射,因为反射比脆弱,不容易排错。今天就到这把,下次再探索一下,spring,struts和反射之间的联系。
版权声明:本文为博主原创文章,未经博主允许不得转载。
原文地址:http://blog.csdn.net/liaodehong/article/details/48049461