没有合适的资源?快使用搜索试试~ 我知道了~
资源推荐
资源详情
资源评论
JAVA 的反射机制与动态代理
李海峰(QQ:61673110)-Andrew830314@163.com
运行时类型信息(RunTime Type Information,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 对象是一个呢?
Cf1 cf1 = new Cf1();
Class clazz = 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(String name)传入一个类的完整类路径也可以获得 Class 对象,但由于使
用的是字符串,必须强制转换才可以获取泛型的 Class<T>的 Class 对象,并且你必须获取这
个方法可能抛出的 ClassNotFoundException 异常。
2.对于引用数据类的引用(必须初始化),可以通过 Object 类继承的 getClass()方法获取这
个引用的 Class 对象,由于引用已经被初始化,所以这种方式也不会初始化静态域,因为静
态域已经被初始化过。另外,前面两种方式如果说是创建 Class 对象,那么这种方式应该是
取得 Class 对象,因为类的实例已经被创建,那么 Class 对象也一定早就被创建。
Class 的常用方法:
l forName(String name):这是一个静态方法,传入的参数是一个类的完整类路径的
字符串,返回这个类的 Class 对象,前面说过 Class 对象的创建发生在类的加载时,所
以这个方法会导致静态成员被调用;
l forName(String name,boolean initialize,ClassLoader loader):这是上面的方
法的重载方法,initialize指定在创建Class对象时是否初始化这个类(即是否执行静态成
员,由于在一次JVM的执行中,静态成员的初始化只类加载的时候执行一次,所以如果
之前这个类已经被加载,那么即使initialize为true也不会再次执行静态成员的加载),
loader指定使用哪个类加载器的实现类
(Thread.currentThread().getContextClassLoader()可以获取当前线程使用的类
加载器)。forName(***)方法不可以获取基本数据类型的Class对象。
如果要测试initialize是否起作用,请不要在main()方法测试自身类,因为main()是静态方法,
执行这个方法会导致静态域被初始化,所以你的initialize无论是true还是false,效果都是一样
的。
l asSubClass(Class superClass):这个方法是将父类的 class 对象作为参数传入,并
将其强制转换成当前的 Class 对象(子类的 Class 对象)。
例:
Class<List> clazz = List.class;
Class<? extends List> subClazz = ArrayList.class.asSubclass(clazz);
System.out.println(subClazz.getCanonicalName());
注意红色的部分不能写成 Class<ArrayList>形式,因为 asSubclass()只知道是要转换成子
类的 Class 对象,但不知道是哪个子类。
l cast(Object o):这个方法是将传入的对象强制转换成 Class 对象所代表的类型的对
象;
l getClassLoader():返回创建当前 Class 对象的类加载器,默认的,应用程序获得的
是 sun.misc.Launcher$AppClassLoader , Applet 程序获得的是
sun.applet.AppletClassLoader;
l getCanonicalName():返回 JAVA 语 言 中 所定义的底层类的规范化名称,如果没有
规范化名称就返回 null,对于普通的引用数据类型,这个方法和 getName()方法都返回
完整的类路径,对于数组(以字符串数组为例), getName()返回[Ljava.lang.String;,
这个方法返回 java.lang.String[];
l getSimpleName():返回底层规范化名称的简写,也就是去掉包名;
l getConstructors():返回 Class 对象的公有构造器的 java.lang.reflect.Contructor 对象
数组,如果找不到匹配的构造方法,返回 NoSuchMethodExcetion 异常;
l getConstructor(Class<?> …parameterTypes):按照指定的可变参数列表,返回
符合参数条件的公有构造方法的 Constructor,这 里 不 可能是数组,因为构造方法不可能
重复,如果找不到匹配的构造方法,返回 NoSuchMethodExcetion 异常;
l getDeclaredConstructors():返回 Class 对象的所有构造器的 Constructor 对象,如
果找不到匹配的构造方法,返回 NoSuchMethodExcetion 异常;
l getDeclaredConstructor(Class<?> …parameterTypes):按照指定的可变参数
列表,返回符合参数条件的所有构造方法的 Constructor,这 里 不 可能是数组,因为构造
方法不可能重复,如果找不到匹配的构造方法,返回 NoSuchMethodExcetion 异常;
l getFields():获取 Class 对象的所有公有成员属性的 java.lang.reflect.Field 数组;
l getField(String name):按照字段名称获取公有字段的 Field 对象,注意 name 是区
分大小写的;
l getDeclaredFields():获取 Class 对象的所有成员属性的 Field 数组;
l getDeclaredField(String name):按照字段名称获取所有字段的 Field 对象,注意
name 是区分大小写的;
l getMethods():获取 Class 对象的公有方法(以及从父类继承的方法,但不包含构造
方法)的 java.lang.reflect.Method 数组;
l getMethod(String name,Class<?> …parameterTypes):按照 name 指定的方法
名称,parameterTypes 指定的可变数组获取公有方法(以及从父类继承的方法,但不包
含构造方法)的 Method 对象,注意 name 是区分大小写的;
l getDeclaredMethods():获取 Class 对象的所有方法(不包含父类继承的方法,构造
方法)的 Method 数组;
l getDeclaredMethod(String name,Class<?> …parameterTypes):按照 name
指定的方法名称,parameterTypes 指定的可变数组获取所有方法(不包含父类继承的方
法,构造方法)的 Method 对象,注意 name 是区分大小写的;
l getGenericInterface():以 Type 数组的形式返回 Class 对象的类直接实现的包含泛
型参数的接口,返回顺序是 implements 后的接口顺序;
l getInterface():以 Class 数组的形式返回 Class 对象的类直接实现的接口,返回顺序
是 implements 后的接口顺序;
l getModifiers():以 java.lang.reflect.Modifier 的常量值的形式返回 Class 对象的类的修
饰的整型值的和;
l getGenericSuperclass():以 Type 数组的形式返回 Class 对象的类直接实现的包含泛
型参数的超类, 返回顺序是 extends 后的接口顺序;
l getSuperclass():以 Class 数组的形式返回 Class 对象的类直接实现的超类, 返回顺序
是 extends 后的接口顺序;
l getName():以 String 形式返回 Class 对象所表示的实体的完整类名,基本数据类型
返回自身,数组类型(以 String 数组为例)返回[Ljava.lang.String;,这个方法没有
getCanonicalName()返回的完整,但不是所有的类型都有底层的规范化名称;
l getPackage():以 java.lang.reflect.Package 形式返回 Class 对象的类所在的包,基本数
据类型、数组抛出异常;
l getResource(String url):这个方法使用当前 Class 对象的 ClassLoader 实体加载 url
指定的资源,返回 java.net.URL 对象。如果 url 以 \ 开头,那么就从当前的 classPath
开始定位资源,否则就从当前 Class 对象的类所在的包开始定位资源,Hibernate 要求
*.hbm.xml 必须和 PO 类在一个包下,就是利用了没有 \ 开头的 url 传入这个方法实现
的;
l getResourceAsStream(String url):这个方法和上面的方法一样,只不过返回的是
java.io.InputStream 的对象;
l isAssignableFrom(Class<?> class):判断当前 Class 对象的类是否是参数 class 的
类的超类或者接口;
l isInstance(Object o):判断参数 o 是否是 Class 对象的类的实例,相 当 于 o instanceOf
Class。
isInstance()、instanceOf 关键字检查的某个实例是否是这个类型,具有继承关系的检查能力,
即 使是其子类的对象,也可以返回 true 。 但 是 Class 对象是严格的类型,所以
super.class==sub.class 是一定返回 false 的。
l newInstance():这个方法将会使得 Class 对象调用类中的公有无参构造方法实例化
对象,, 返回一个 Object 对象,大多数框架都会使用到这个方法,例如 EJB 容器、Spring
容器都会要求受管组件提供一个默认构造方法。
注意:以上的方法并不是对每种类型都可用,对于返回数组的方法,如果不可用则返回长度
为 0 的数组,对于返回其他类型的方法,如果不可用则返回 null。例如:getConstructors()
方法对于基本数据类型、数组就不可用,那么返回一个长度为 0 的 Constructor 数组。
另外,上 面 的 方法获取类中的元素大都是只能获取 public 的元素,可以获取全部元素的大都
是含有 Declared 字符。
某些方法不具有递归特性,例如只能查找本类的元素,不能查找内部类或者其父类中的元素,
如果你非要这么做,需要自行递归操作,例如:调用 getSuperClass()直到其返回 null 为止。
______________________________________________
2.JAVA
的反射机制:
前面说过 RTTI 获取某个对象的确切类型,要求在这个对象在编译时必须已知,也就是必须
已经在你的代码中存在完整的声明(T t), 但 是 如果是运行时才会知晓的对象(例如网络中
传递过来的字节), RTTI 就没办法工作了。
java.lang.Class 与 java.lang.reflect.*包中的类提供了一种有别于 RTTI(编译器在编译器时打
开和检查*.class 文件)的反射机制(运行时打开和检查*.class 文件)。
JAVA 的反射功能相当强大,例如前面说过的类的复用方式---组合,如果是动态组合的新类
就叫聚合,反射就可以完成聚合功能。
(1.)Field
:这个类用于获取类中的字段信息以及访问这些字段的能力。
l getObject(Object o):返回参数 o 的对象上的 Field 表示的字段的值;
l setObject(Object o,Object value):将参数 o 的对象上的 Field 表示的字段的值设
置为 value;
l getBoolean(Object o):获取参数 o 的对象的 Field 表示的布尔类型的字段的值;
l setBoolean(Object o,boolean value):将参数 o 的对象上的 Field 表示的布尔类型
的字段的值设置为 value;
… …对于基本数据类型,都拥有自己的 getXXX()、setXXX()方法。
l getGenericType():返回 Field 表示的字段声明的泛型类型的 Type 实例;
l getModifiers():以整型数值和的形式返回 Field 表示的字段的修饰符;
l getType():返回 Field 表示的字段的类型的 Class 对象;
l isEnumConstants():如果 Field 表示的字段是枚举类型,返回 true;
l toGenericString():返回描述此字段的字符串(包含泛型信息),能够包含泛型信息
是与 toString()方法的唯一区别。
例:
public class Reflect1 {
private int i;
protected String s;
public List<String> list = new ArrayList<String>();
public Reflect1() {
System.out.println("default");
}
protected Reflect1(String name) {
System.out.println(name);
}
public void f() {
System.out.println("invoke f");
}
String mf(int j, Object... args) {
System.out.println("invoke mf");
return String.valueOf(j + args.length);
}
}
public class Rf {
public static void main(String[] args) throws Exception {
Class<Reflect1> clazz = Reflect1.class;
Reflect1 rf1 = clazz.newInstance();
// 含有Declared字符串的是获取所有的元素,否则就只能获取公有元素
Field[] f = clazz.getDeclaredFields();
for (Field field : f) {
// 设置这里本不具有访问权限的元素为可访问
field.setAccessible(true);
// 使用基本数据类型专有的API
if (field.getType().getCanonicalName().equals("int")
|| field.getType().getCanonicalName().equals(
"java.lang.Integer")) {
field.setInt(rf1, 9);
}
System.out.println("Field is " + field.getName() + "\t"
+ field.toGenericString() + "\t" + field.get(rf1));
}
}
}
控制台输出如下语句:
default
Field is i private int net.ilkj.reflect.Reflect1.i 9
Field is s protected java.lang.String net.ilkj.reflect.Reflect1.s null
Field is list
public java.util.List<java.lang.String> net.ilkj.reflect.Reflect1.list []
______________________________________________
(2.)Method
:这个类用于获取类中的方法的信息以及访问这些方法的能力。
l getDefaultValue():返回此方法表示的注视成员的默认值;
剩余22页未读,继续阅读
资源评论
天机与日月争辉
- 粉丝: 1
- 资源: 53
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功