// 参考:http://blog.csdn.net/stevenhu_223/article/details/9286121
最近发现好多框架底层的实现与Java的reflect和cglib有关,看过原理后找了几篇经典的文档,以供后来复习使用
前言:我们知道,类和类的成员变量及方法都是要求有权限控制的(public、protected、private);而当类中的信息封装为私有时,外部对该类中私有的信息是没有访问权限的,也就是说当该类里的内容信息均受private权限控制时,外部想要获取和处理该类里的私有信息是几乎不可能的;但是,有时候这种需求是有的,而当我们非得需要去动用别的类里封装的私有信息时,java的反射机制就起到了非常关键的作用了;
java反射机制的实现主要由三个类来主导:它们分别是Class、Field、Method;
java在编译和运行时,会将需要被编译和运行的所有类加载进类加载器,每个类被加载之后,系统就会为该类生成一个对应的Class对象,通过该Class对象就可以访问到java虚拟机中的这个类,进而在运行时对这个被访问的类进行信息的获取和处理(当然,不管被访问的这个类里的信息是否私有的);通俗的讲,Class对象间接代表了它对应的类,通过这个Class对象,我们就可以去执行反射机制的实现;
获取Class对象主要有三种方式:
1). 调用Class类的forName(String name)静态方法,参数name为Class对应的类的全名(包括包名);
比如我们要创建Gesture这个类对应的Class对象:
Class<Gesture> mClass = Class.forName("android.gesture.Gesture");
android.gesture为Gesture的包名,Class<Gesture>中的Gesture表示得到的是Gesture类型对应的Class对象,<>中的Gesture也可为通配符?表示,如:Class<?>mClass = Class.forName("android.gesture.Gesture");
2). 调用类的class属性得到类对应的Class对象。如:Class<?>mClass = Gesture.class; (一般建议用这种方式得到Class对象)
3).调用类的实例化对象的getClass()方法。特别说明的是,getClass()是java类的始祖Object类的方法,所以,所有java对象都可以调用该方法;如mGesture是Gesture类型的对象,Class<?> mClass = mGesture.getClass()得到的是Gesture类对应的Class对象
那么在得到对应类的Class对象对应后,我们就可以通过该Class对象得到它所对应的类的一些信息,比如该类的构造函数、成员(属性)、方法(函数);
Class类提供的相关接口介绍:(详细参看API)
接口 | 返回类型 | 接口功能实现 |
getPackage() |
Package | 得到目标类的包名对应的Package对象 |
getCanonicalName() |
String | 得到目标类的全名(包名+类名) |
getName() | String | 同getCanonicalName() |
getClassLoader() |
ClassLoader |
得到加载目标类的ClassLoader对象 |
getClasses() |
Class<?>[] | 得到目标类中的所有的public内部类以及public内部接口所对应的Class对象 |
getDeclaredClasses() |
Class<?>[] |
同getClasses(),但不局限于public修饰,只要是目标类中声明的内部类和接口均可 |
getConstructors() |
Constructor<?>[] |
得到目标类的所有public构造函数对应的Constructor对象 |
getDeclaredConstructors() |
Constructor<?>[] |
同getConstructors(),但不局限于public修饰,只要是目标类中声明的构造函数均可 |
getField(String arg) |
Field | 得到目标类中指定的某个public属性对应的Field对象 |
getDeclaredField(String arg) |
Field | 同getField,但不局限于public修饰,只要是目标类中声明的属性均可 |
getFields() |
Field[] | 得到目标类中所有的public属性对应的Field对象 |
getDeclaredFields() |
Field[] | 同getFields(),但不局限于public修饰的属性 |
getMethod(String arg0, Class<?>... arg1) |
method | 得到目标类中指定的某个public方法对应的Method对象 |
getDeclaredMethod(String arg0, Class<?>... arg1) |
Method | 同getMethod,但不局限于public修饰的方法 |
getMethods() |
Method[] | 得到目标类中所有的public方法对应的Method对象 |
getDeclaredMethods() |
Method[] | 同getMethods(),但不局限于public修饰的方法 |
getEnclosingClass() |
Class | 得到目标类所在的外围类的Class对象 |
getGenericInterfaces() |
Type[] | 得到目标类实现的接口对应的Type对象 |
getGenericSuperclass() |
Type | 得到目标类继承的父类所对应的Type对象 |
getInterfaces() |
Class<?>[] | 得到目标类实现的接口所对应的Class对象 |
getSuperclass() |
Class | 得到目标类继承的父类所对应的Class对象 |
isMemberClass() |
boolean | 目标类是否为成员类 |
cisAnonymousClass() |
boolean | 目标类是否为匿名类 |
我们知道一般类里包含有属性(成员)和方法(函数),竟然Class是描述类的信息,那么类其它部分应该会对应有描述它们的部分,而Field类型的对象就是描述Class对象对应类的出现包括public、protected、private属性);一个Field对象对应描述一个类的属性;
通过上文对Class的介绍,我们知道Class提供了四种接口函数可以得到对应属性的Field:
1). getField(String name):返回类型为Field,name为类中的属性名,得到的是描述类中的一个public属性对应的Field对象;如 Field mField =mClass.getField("mGestureID") 得到的是Gesture类中的一个public属性mGestureID对应的Field对象;
2). getFields():返回类型为Field类型数组,得到的是描述类中的所有public属性对应的所有Field对象;
3). getDeclaredField(String name):同getField(String name),只不过得到的Field对象描述的不只是public属性,
还包括protected、private属性,也是说只要是在类中声明的属性;
4). getDeclaredFields():getFields(),得到的是描述类中声明的所有属性(public、protected、private)对应的Field对象;
Field类的相关函数接口介绍:
Field类提供的相关接口介绍:(详细参看API)
接口 | 返回类型 | 接口功能实现 |
setAccessible(boolean flag) | void | 参数为true,只要是在类中声明的目标属性均可访问,为false,只有public目标属性可访问 |
set(Object object, Object value) | void | 给目标属性设置值(private、protected属性均不能访问,但可以通过先调用setAccessible(true)实现访问),第一个参数为目标属性所在类的对象,第二个参数为传入的值 |
get(Object object) | Object | 得到目标属性的值(private、protected属性均不能访问,但可以通过调用setAccessible(true)实现访问),参数为目标属性所在类的对象 |
setBoolean(Object object, boolean value) | void | 同set(Object object, Object value),只不过操作的数据类型为boolean |
getBoolean(Object object) | boolean | 同get(Object object),只不过得到的数据类型为boolean |
setByte(Object object, boolean value) | void | 同set(Object object, Object value),只不过操作的数据类型为byte |
getByte(Object object) | byte | 同get(Object object),只不过得到的数据类型为byte |
setShort(Object object, boolean value) | void | 同set(Object object, Object value),只不过操作的数据类型为short |
getShort(Object object) | short | 同get(Object object),只不过得到的数据类型为short |
setInt(Object object, boolean value) | void | 同set(Object object, Object value),只不过操作的数据类型为int |
getInt(Object object) | int | 同get(Object object),只不过得到的数据类型为int |
setLong(Object object, boolean value) | void | 同set(Object object, Object value),只不过操作的数据类型为long |
getLong(Object object) | long | 同get(Object object),只不过得到的数据类型为long |
setFloat(Object object, boolean value) | void | 同set(Object object, Object value),只不过操作的数据类型为float |
getFloat(Object object) | float | 同get(Object object),只不过得到的数据类型为float |
setDouble(Object object, boolean value) | void | 同set(Object object, Object value),只不过操作的数据类型为double |
getDouble(Object object) | double | 同get(Object object),只不过得到的数据类型为double |
setChar(Object object, boolean value) | void | 同set(Object object, Object value),只不过操作的数据类型为char |
getChar(Object object) | char | 同get(Object object),只不过得到的数据类型为char |
getName() | String | 得到目标属性的名字,不局限于private修饰符,只要是类中声明的属性 |
getGenericType() | Type | 得到目标属性的类型,不局限于private修饰符 |
getType() | Class<?> | 得到目标属性的类型对应的Class对象 |
getModifiers() | int | 得到目标属性的修饰符值(private为2、protected为4、public为1、static为8、final为16) |
getDeclaringClass() | Class<?> | 得到目标属性所在类对应的Class对象 |
下面就以一个示例代码来验证Field表中的函数接口的实现,如下:
1). FieldBeReflected.java(被反射的类)
2). ReflectField.java(执行反射调用的类)
同Fiel一样,一个Method对象对应描述一个类的方法;
Class对象也提供了四种接口函数得到对应方法的Method对象,如下:
1). getMethod(String name, Class<?>... parameterTypes):返回类型为Method,第一个参数name为类中的方法名,第二个参数为可变参数,传入的是参数类型对应的Class对象(方法的参数可能为多个的情况);该函数得到的是描述类中的一个public方法对应的Method对象;
2). getMethods():返回类型为Method类型数组,得到的是描述类中的所有public方法对应的Method对象;
3). Method getDeclaredMethod(String name, Class<?>...parameterTypes):
同getMethod(String name, Class<?>... parameterTypes),只不过得到的Method对象描述的不只是public方法, 还包括
protected、private方法,也是说只要是在类中声明的方法;
4). getDeclaredMethods():getMethods(),得到的是描述类中声明的所有方法(public、protected、private)对应的FMethod对象;
Method类的相关函数接口介绍:
Method类提供的相关接口介绍:(详细参看API)
接口 | 返回类型 | 接口功能实现 |
setAccessible(boolean flag) | void | 参数为true,只要是在类中声明的目标方法均可访问,为false,只有public目标属性可访问 |
invoke(Object receiver, Object... args) | Object | 动态执行调用目标方法,第一个参数为Class对象或者类的实例,第二个参数为可变实参的对象(多个实参) |
getDeclaringClass() | Class<?> | 得到目标方法所在类对应的Class对象 |
getExceptionTypes() | Class<?> | 得到目标方法抛出的异常类型对应的Class对象 |
getGenericExceptionTypes() | Type[] | 得到目标方法抛出的异常类型对应的Type对象 |
getReturnType() | Class<?> | 得到目标方法返回类型对应的Class对象 |
getGenericReturnType() | Type | 得到目标方法返回类型对应的Type对象 |
getParameterTypes() | Class<?>[] | 得到目标方法各参数类型对应的Class对象 |
getGenericParameterTypes() | Type[] | 得到目标方法各参数类型对应的Type对象 |
getModifiers() | int | 得到目标方法修饰符的值 |
getName() | String | 得到目标方法的名字 |
下面就以一个示例代码来验证Method表中的函数接口的实现,如下:
1). MethodBeReflected.java(被反射的类)
2)ReflectMethod.java(执行反射的类)
import java.lang.reflect.Field; import java.lang.reflect.Method; import java.text.SimpleDateFormat; import java.util.Date; public class TestReflect { public String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public String sayHello() { return "hello"; } public String sayHello(String name) { return name + ", hello"; } public String sayHello(String name, Date date) { return name + ", hello at " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date); } @Override public String toString() { return "the object: name=" + name + ";age=" + age; } /** * @param args */ public static void main(String[] args) { try { // 测试String的生成 String testStr = "testString"; Class getClassWay1 = testStr.getClass(); Class getClassWay2 = String.class; Class getClassWay3 = Class.forName("java.lang.String"); System.out.println(getClassWay1 == getClassWay2); System.out.println(getClassWay2 == getClassWay3); String str1 = (String) Class.forName("java.lang.String").newInstance(); System.out.println(str1); String str2 = (String) Class.forName("java.lang.String") // 得到字节码即事例对象 .getConstructor(StringBuffer.class) // 获得该对象中的特定构造器 .newInstance(new StringBuffer("abc")); // 根据构造器产生对象实例 System.out.println(str2.charAt(1)); System.out.println("=========="); ///////////////////////////////////////////////////////// Class<?> clazz = Class.forName("com.huabao.test.Demo"); Object obj = clazz.newInstance(); System.out.println(obj); Field fname = clazz.getField("name"); fname.set(obj, "java"); Field fage = clazz.getDeclaredField("age"); fage.setAccessible(true); fage.setInt(obj, 100); System.out.println(obj); System.out.println(fage.getInt(obj)); System.out.println("=========="); Method m0 = clazz.getMethod("getName", null); System.out.println(m0.invoke(obj, null)); Method m00 = clazz.getMethod("setName", new Class[] { String.class }); System.out.println(m00.invoke(obj, new Object[] { "java111" })); System.out.println(obj); Method m1 = clazz.getMethod("sayHello", new Class[0]); System.out.println(m1.invoke(obj, new Object[0]).toString()); Method m2 = clazz.getMethod("sayHello", new Class[] { String.class }); System.out.println(m2.invoke(obj, new Object[] { "java world" }).toString()); Method m3 = clazz.getMethod("sayHello", new Class[] { String.class, Date.class }); System.out.println(m3.invoke(obj, new Object[] { "java world", new Date() }).toString()); } catch (Exception e) { e.printStackTrace(); } } }
import java.util.HashMap; public class TestClass { public static void main(String[] args) { Object a = new HashMap(); System.out.println(a.getClass().isArray()); System.out.println(a.getClass().getGenericSuperclass()); System.out.println(a.getClass().getPackage()); System.out.println(a.getClass().getSimpleName()); System.out.println("+++"); for (Class c : a.getClass().getInterfaces()) { System.out.println(c.getName()); } System.out.println("+++"); System.out.println(a.getClass().getComponentType()); System.out.println(a.getClass().getModifiers()); System.out.println(a.getClass().isLocalClass()); System.out.println(a.getClass().isLocalClass()); } }
Class
该类主要获得编译后的字节码信息,主要包含类的加载/构造方法/普通方法/属性列表/包信息/父类信息的主要信息
Class类(在java.lang包中,Instances of the class Classrepresent classes and interfaces in a running Javaapplication):
在Java中,每个class都有一个相应的Class对象。也就是说,当我们编写一个类,编译完成后,在生成的.class文件中,就会产生一个Class对象,用于表示这个类的类型信息
获取Class实例的三种方式:
(1)利用对象调用getClass()方法获取该对象的Class实例;
(2)使用Class类的静态方法forName(),用类的名字获取一个Class实例(staticClass forName(String className) Returns the Classobject associated with the class or interface with the given stringname. );
(3)运用.class的方式来获取Class实例,对于基本数据类型的封装类,还可以采用.TYPE来获取相对应的基本数据类型的Class实例
在newInstance()调用类中缺省的构造方法 ObjectnewInstance()(可在不知该类的名字的时候,常见这个类的实例) Creates a new instance of the class represented by this Classobject.
在运行期间,如果我们要产生某个类的对象,Java虚拟机(JVM)会检查该类型的Class对象是否已被加载。如果没有被加载,JVM会根据类的名称找到.class文件并加载它。一旦某个类型的Class对象已被加载到内存,就可以用它来产生该类型的所有对象
public class ClassTest {
public static void main(String [] args)throws Exception{
String str1="abc";
Class cls1=str1.getClass();
Class cls2=String.class;
Class cls3=Class.forName("java.lang.String");
System.out.println(cls1==cls2);
System.out.println(cls1==cls3);
}
}
返回结果为:true,true.
解释:虚拟机只会产生一份字节码, 用这份字节码可以产生多个实例对象。
Method Field
Method
提供关于类或接口上单独某个方法(以及如何访问该方法)的信息。所反映的方法可能是类方法或实例方法(包括抽象方法)。
Method
允许在匹配要调用的实参与底层方法的形参时进行扩展转换;但如果要进行收缩转换,则会抛出 IllegalArgumentException
。
Field
提供有关类或接口的单个字段的信息,以及对它的动态访问权限。反射的字段可能是一个类(静态)字段或实例字段。
Array
允许在执行 get 或 set 访问操作期间进行扩展转换,但如果将发生收缩转换,则抛出一个 IllegalArgumentException
。