JAVA的反射机制与动态代理李海峰(QQ:61673110)-Andrew830314@163.com运行时类型信息(RunTimeTypeInformation,RTTI)使得你在程序运行时发现和使用类型信息。RTTI主要用来运行时获取向上转型之后的对象到底是什么具体的类型。1.Class对象:JAVA使用Class对象来执行RTTI。每个类都有一个Class对象,它用来创建这个类的所有对象,反过来说,每个类的所有对象都会关联同一个Class对象(对于数组来说,维数、类型一致的数组的Class对象才是相同的),每个对象的创建都依赖于Class对象的是否创建,Class对象的创建发生在类加载(java.lang.ClassLoader)的时候。java.lang.Class类实现了Serializable、GenericDeclaration、Type、AnnotatedElement四个接口,分别实现了可序列化、泛型定义、类型、元数据(注解)的功能。你可以把Class对象理解为一个类在内存中的接口代理(它代理了这个类的类型信息、方法签名、属性),JVM加载一个类的时候首先创建Class对象,然后创建这个类的每个实例的时候都使用这个Class对象。Class只有一个私有的无参构造方法,也就是说Class的对象创建只有JVM可以完成。如何验证同一个类的多个对象的Class对象是一个呢?Cf1cf1=newCf1();Classclazz=Cf1.class;System.out.println(cf1.getClass()==clazz);我们知道==用来比较引用是否相等(也就是同一个引用),上面的输出语句结果是true。那么Class对象是否相等是JAVA对象中唯一可以使用==判断的。如何获取Class对象:1.所有的引用数据类型(类-类型)的类名、基本数据类型都可以通过.class方式获取其Class对象(对于基本数据类型的封装类还可以通过.TYPE的方式获取其Class对象,但要注意.TYPE实际上获取的封装类对应的基本类型的Class对象的引用,那么你可以判断出int.class==Integer.TYPE返回true,int.class==Integer.class返回false!),通过这种方式不会初始化静态域,使用.class、.TYPE的方式获取Class对象叫做类的字面常量;2.Class的forName(Stringname)传入一个类的完整类路径也可以获得Class对象,但由于使用的是字符串,必须强制转换才可以获取泛型的ClassT的Class对象,并且你必须获取这个方法可能抛出的ClassNotFoundException异常。2.对于引用数据类的引用(必须初始化),可以通过Object类继承的getClass()方法获取这个引用的Class对象,由于引用已经被初始化,所以这种方式也不会初始化静态域,因为静态域已经被初始化过。另外,前面两种方式如果说是创建Class对象,那么这种方式应该是取得Class对象,因为类的实例已经被创建,那么Class对象也一定早就被创建。Class的常用方法:lforName(Stringname):这是一个静态方法,传入的参数是一个类的完整类路径的字符串,返回这个类的Class对象,前面说过Class对象的创建发生在类的加载时,所以这个方法会导致静态成员被调用;lforName(Stringname,booleaninitialize,ClassLoaderloader):这是上面的方法的重载方法,initialize指定在创建Class对象时是否初始化这个类(即是否执行静态成员,由于在一次JVM的执行中,静态成员的初始化只类加载的时候执行一次,所以如果之前这个类已经被加载,那么即使initialize为true也不会再次执行静态成员的加载),loader指定使用哪个类加载器的实现类(Thread.currentThread().getContextClassLoader()可以获取当前线程使用的类加载器)。forName(***)方法不可以获取基本数据类型的Class对象。如果要测试initialize是否起作用,请不要在main()方法测试自身类,因为main()是静态方法,执行这个方法会导致静态域被初始化,所以你的initialize无论是true还是false,效果都是一样的。lasSubClass(ClasssuperClass):这个方法是将父类的class对象作为参数传入,并将其强制转换成当前的Class对象(子类的Class对象)。例:ClassListclazz=List.class;Class?extendsListsubClazz=ArrayList.class.asSubclass(clazz);System.out.println(subClazz.getCanonicalName());注意红色的部分不能写成ClassArrayList形式,因为asSubclass()只知道是要转换成子类的Class对象,但不知道是哪个子类。lcast(Objecto):这个方法是将传入的对象强制转换成Class对象所代表的类型的对象;lgetClassLoader():返回创建当前Class对象的类加载器,默认的,应用程序获得的是sun.misc.Launcher$AppClassLoader,Applet程序获得的是sun.applet.AppletClassLoader;lgetCanonicalName():返回JAVA语言中所定义的底层类的规范化名称,如果没有规范化名称就返回null,对于普通的引用数据类型,这个方法和getName()方法都返回完整的类路径,对于数组(以字符串数组为例),getName()返回[Ljava.lang.String;,这个方法返回java.lang.String[];lgetSimpleName():返回底层规范化名称的简写,也就是去掉包名;lgetConstructors():返回Class对象的公有构造器的java.lang.reflect.Contructor对象数组,如果找不到匹配的构造方法,返回NoSuchMethodExcetion异常;lgetConstructor(Class?…parameterTypes):按照指定的可变参数列表,返回符合参数条件的公有构造方法的Constructor,这里不可能是数组,因为构造方法不可能重复,如果找不到匹配的构造方法,返回NoSuchMethodExcetion异常;lgetDeclaredConstructors():返回Class对象的所有构造器的Constructor对象,如果找不到匹配的构造方法,返回NoSuchMethodExcetion异常;lgetDeclaredConstructor(Class?…parameterTypes):按照指定的可变参数列表,返回符合参数条件的所有构造方法的Constructor,这里不可能是数组,因为构造方法不可能重复,如果找不到匹配的构造方法,返回NoSuchMethodExcetion异常;lgetFields():获取Class对象的所有公有成员属性的java.lang.reflect.Field数组;lgetField(Stringname):按照字段名称获取公有字段的Field对象,注意name是区分大小写的;lgetDeclaredFields():获取Class对象的所有成员属性的Field数组;lgetDeclaredField(Stringname):按照字段名称获取所有字段的Field对象,注意name是区分大小写的;lgetMethods():获取Class对象的公有方法(以及从父类继承的方法,但不包含构造方法)的java.lang.reflect.Method数组;lgetMethod(Stringname,Class?…parameterTypes):按照name指定的方法名称,parameterTypes指定的可变数组获取公有方法(以及从父类继承的方法,但不包含构造方法)的Method对象,注意name是区分大小写的;lgetDeclaredMethods():获取Class对象的所有方法(不包含父类继承的方法,构造方法)的Method数组;lgetDeclaredMethod(Stringname,Class?…parameterTypes):按照name指定的方法名称,parameterTypes指定的可变数组获取所有方法(不包含父类继承的方法,构造方法)的Method对象,注意name是区分大小写的;lgetGenericInterface():以Type数组的形式返回Class对象的类直接实现的包含泛型参数的接口,返回顺序是implements后的接口顺序;lgetInterface():以Class数组的形式返回Class对象的类直接实现的接口,返回顺序是implements后的接口顺序;lgetModifiers():以java.lang.reflect.Modifier的常量值的形式返回Class对象的类的修饰的整型值的和;lgetGenericSuperclass():以Type数组的形式返回Class对象的类直接实现的包含泛型参数的超类,返回顺序是extends后的接口顺序;lgetSuperclass():以Class数组的形式返回Class对象的类直接实现的超类,返回顺序是extends后的接口顺序;lgetName():以String形式返回Class对象所表示的实体的完整类名,基本数据类型返回自身,数组类型(以String数组为例)返回[Ljava.lang.String;,这个方法没有getCanonicalName()返回的完整,但不是所有的类型都有底层的规范化名称;lgetPackage():以java.lang.reflect.Package形式返回Class对象的类所在的包,基本数据类型、数组抛出异常;lgetResource(Stringurl):这个方法使用当前Class对象的ClassLoader实体加载url指定的资源,返回java.net.URL对象。如果url以\开头,那么就从当前的classPath开始定位资源,否则就从当前Class对象的类所在的包开始定位资源,Hibernate要求*.hbm.xml必须和PO类在一个包下,就是利用了没有\开头的url传入这个方法实现的;lgetResourceAsStream(Stringurl):这个方法和上面的方法一样,只不过返回的是java.io.InputStream的对象;lisAssignableFrom(Class?class):判断当前Class对象的类是否是参数class的类的超类或者接口;lisInstance(Objecto):判断参数o是否是Class对象的类的实例,相当于oinstanceOfClass。isInstance()、instanceOf关键字检查的某个实例是否是这个类型,具有继承关系的检查能力,即使是其子类的对象,也可以返回true。但是Class对象是严格的类型,所以super.class==sub.class是一定返回false的。lnewInstance():这个方法将会使得Class对象调用类中的公有无参构造方法实例化对象,,返回一个Object对象,大多数框架都会使用到这个方法,例如EJB容器、Spring容器都会要求受管组件提供一个默认构造方法。注意:以上的方法并不是对每种类型都可用,对于返回数组的方法,如果不可用则返回长度为0的数组,对于返回其他类型的方法,如果不可用则返回null。例如:getConstructors()方法对于基本数据类型、数组就不可用,那么返回一个长度为0的Constructor数