2.JAVA反射机制API及功能
获取类的Fields
获取类的Method
获取类的Constructor
新建类的实例
Class<T>的函数newInstance
通过Constructor对象的方法newInstance
3调用类的函数
调用private函数
4设置/获取类的属性值
private属性
Java反射是Java被视为动态(或准动态)语言的一个关键性质。
这个机制同意程序在执行时透过Reflection APIs取得不论什么一个已知名称的class的内部信息,包含其modifiers(诸如public, static 等)、superclass(比如Object)、实现之interfaces(比如Cloneable)。也包含fields和methods的全部信息,并可于执行时改变fields内容或唤起methods。
Java反射机制容许程序在执行时载入、探知、使用编译期间全然未知的classes。换言之,Java能够载入一个执行时才得知名称的class,获得其完整结构。
这样的“看透”class的能力(the ability of the program to examine itself)被称为introspection(内省、内观、反省)。Reflection和introspection是常被并提的两个术语。
Class类:代表一个类,位于java.lang包下。
Field类:代表类的成员变量(成员变量也称为类的属性)。
Method类:代表类的方法。
Constructor类:代表类的构造方法。
Array类:提供了动态创建数组,以及訪问数组的元素的静态方法。
获取类的Class对象:
要想使用反射,首先须要获得待操作的类所相应的Class对象。
Java中,不管生成某个类的多少个对象。这些对象都会相应于同一个Class对象。
这个Class对象是由JVM生成的,通过它可以获悉整个类的结构。
经常使用的获取Class对象的3种方式:
1).使用Class类的静态方法。比如: Class.forName("java.lang.String");
2).使用类的.class语法。如: String.class;
3).使用对象的getClass()方法。如:String str = "aa"; Class<?
> classType1 = str.getClass();
获取类的Fields
能够通过反射机制得到某个类的某个属性,然后改变相应于这个类的某个实例的该属性值。JAVA 的Class<T>类提供了几个方法获取类的属性。
public FieldgetField(String name) | 返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段 |
public Field[] getFields() | 返回一个包括某些 Field 对象的数组,这些对象反映此 Class 对象所表示的类或接口的全部可訪问公共字段 |
public FieldgetDeclaredField(Stringname) | 返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段 |
public Field[] getDeclaredFields() | 返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的全部字段 |
执行结果:public static void main(String[] args) throws ClassNotFoundException { // TODO Auto-generated method stub Class<?> pc3 = Class.forName("reflect.Person"); Field[] fields=pc3.getFields(); for (Field f : fields) { System.out.println("field:"+f); } Field[] fields2= pc3.getDeclaredFields(); for (Field f : fields2) { System.out.println("d field:"+f); } }
field:public java.lang.String reflect.Person.name d field:private java.lang.String reflect.Person.password d field:public java.lang.String reflect.Person.name 可见getFields和getDeclaredFields差别:getFields返回的是申明为public的属性,包含父类中定义。
getDeclaredFields返回的是指定类定义的全部定义的属性,不包含父类的。
获取类的Method
通过反射机制得到某个类的某个方法,然后调用相应于这个类的某个实例的该方法
Class<T>类提供了几个方法获取类的方法。
public MethodgetMethod(String name,Class<?>... parameterTypes) | 返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法 |
public Method[] getMethods() | 返回一个包括某些 Method 对象的数组。这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法 |
public MethodgetDeclaredMethod(Stringname,Class<?>... parameterTypes) | 返回一个 Method 对象。该对象反映此 Class 对象所表示的类或接口的指定已声明方法 |
public Method[] getDeclaredMethods() | 返回 Method 对象的一个数组。这些对象反映此 Class 对象表示的类或接口声明的全部方法,包含公共、保护、默认(包)訪问和私有方法,但不包含继承的方法 |
获取类的Constructor
通过反射机制得到某个类的构造器,然后调用该构造器创建该类的一个实例
Class<T>类提供了几个方法获取类的构造器。
public Constructor<T>
getConstructor(Class<? >... parameterTypes) |
返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法 |
public Constructor<?>[] getConstructors() |
返回一个包括某些 Constructor 对象的数组。这些对象反映此 Class 对象所表示的类的全部公共构造方法 |
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) |
返回一个 Constructor 对象。该对象反映此 Class 对象所表示的类或接口的指定构造方法 |
public Constructor<?>[] getDeclaredConstructors() |
返回 Constructor 对象的一个数组。这些对象反映此 Class 对象表示的类声明的全部构造方法。它们是公共、保护、默认(包)訪问和私有构造方法 |
输出:Constructor<?>[] cons=pc3.getConstructors(); for (int i = 0; i < cons.length; i++) { System.out.println("cons: "+cons[i]); }
通过反射获取类的Field对象,调用Field方法设置或获取值
此外还须要设置訪问权限。
例如以下:
AccessibleObject是Method,Field,Constructor的父类,override属性默觉得false,可调用setAccessible方法改变,假设设置为true,则表示能够忽略訪问权限的限制,直接调用。
2.2.假设不是ture,则要进行訪问权限检測。用Reflection的quickCheckMemberAccess方法先检查是不是public的,假设不是再用Reflection.getCallerClass(1)方法获得到调用这种方法的Class,然后做是否有权限訪问的校验,校验之后缓存一次。以便下次假设还是这个类来调用就不用去做校验了。直接用上次的结果,(非常奇怪用这样的方式缓存,由于这样的方式假设下次换个类来调用的话,就不用会缓存了,而再验证一遍,把这次的结果做为缓存。但上一次的缓存结果就被冲掉了。这是一个非常easy的缓冲机制,仅仅适用于一个类的反复调用)。
2.3.调用MethodAccessor的invoke方法。每一个Method对象包括一个root对象,root对象里持有一个MethodAccessor对象。我们获得的Method独享相当于一个root对象的镜像,全部这类Method共享root里的MethodAccessor对象,(这个对象由ReflectionFactory方法生成,ReflectionFactory对象在Method类中是static final的由native方法实例化)。
每一个实际的Java方法仅仅有一个相应的Method对象作为root,这个root是不会暴露给用户的,而是每次在通过反射获取Method对象时新创建Method对象把root包装起来再给用户。
在第一次调用一个实际Java方法相应得Method对象的invoke()方法之前,实现调用逻辑的MethodAccessor对象还没创建;等第一次调用时才新创建MethodAccessor并更新给root,然后调用MethodAccessor.invoke()真正完毕反射调用。
为了权衡两个版本号的性能,Sun的JDK使用了“inflation”的技巧:让Java方法在被反射调用时。开头若干次使用native版。等反射调用次数超过阈值时则生成一个专用的MethodAccessor实现类。生成当中的invoke()方法的字节码,以后对该Java方法的反射调用就会使用Java版。
以下相应源代码:
每次NativeMethodAccessorImpl.invoke()方法被调用时,都会添加一个调用次数计数器,看超过阈值没有;一旦超过,则调用MethodAccessorGenerator.generateMethod()来生成Java版的MethodAccessor的实现类,而且改变DelegatingMethodAccessorImpl所引用的MethodAccessor为Java版。兴许经由DelegatingMethodAccessorImpl.invoke()调用到的就是Java版的实现了。注意到关键的invoke0()方法是个native方法。它在HotSpot VM里是由JVM_InvokeMethod()函数所支持的,扯个额外话:想想大多数java有关的东西,假设你愿意去看源代码分析的话。总会找到jvm这层。当然还有大神继续研究Hotspot的里面的C源代码,我认为主要知识点到这一层就算是比較深入了 。
再看DelegatingMethodAccessorImpl:
这是一个间接层,方便在native与Java版的MethodAccessor之间实现切换。
native结束,再看java版本号:MethodAccessorGenerator。
它的基本工作就是在内存里生成新的专用Java类。并将其载入。
private static synchronized String generateName(boolean isConstructor, boolean forSerialization) { if (isConstructor) { if (forSerialization) { int num = ++serializationConstructorSymnum; return "sun/reflect/GeneratedSerializationConstructorAccessor" + num; } else { int num = ++constructorSymnum; return "sun/reflect/GeneratedConstructorAccessor" + num; } } else { int num = ++methodSymnum; return "sun/reflect/GeneratedMethodAccessor" + num; } }
上面贴出来的方法就是产生名字的。至于大神RednaxelaFX贴出来解释GeneratedMethodAccessor1 这段看不懂。如有大神理解,还请多多留言指正。