码迷,mamicode.com
首页 > 编程语言 > 详细

探索Java反射机制

时间:2015-08-30 23:10:43      阅读:177      评论:0      收藏:0      [点我收藏+]

标签:java   jvm   反射技术   

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("初始化快");
    }


省略get,set,构造器等基础方法;下面来测试一下;

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的主要几个类

Field 类:代表类的成员变量(成员变量也称为类的属性)。
Method类:代表类的方法。
Constructor 类:代表类的构造方法。
Array类:提供了动态创建数组,以及访问数组的元素的静态方法。

下面写几个小例子来灵活的运用一下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和反射之间的联系。



版权声明:本文为博主原创文章,未经博主允许不得转载。

探索Java反射机制

标签:java   jvm   反射技术   

原文地址:http://blog.csdn.net/liaodehong/article/details/48049461

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!