Java反射学习总结
来源:互联网 发布:什么是java面向对象 编辑:程序博客网 时间:2024/04/28 15:32
Java反射机制提供了什么功能:
1、获取Class对象,在使用Java的反射功能时,基本首先都要获取类的Class对象,再通过Class对象获取其他的对象。可通过如下方式获取:
1.1 调用getClass:
Boolean var1 = true;Class<?> classType2 = var1.getClass();
1.2 运用.class 语法:
Class<?> classType4 = Boolean.class;
1.3 运用static method Class.forName():
Class<?> classType5 = Class.forName("java.lang.Boolean"); //需要完整的类名,包名+类名
2、获取方法
getDeclaredMethods():获取所有的方法
getReturnType():获得方法的放回类型
getParameterTypes():获得方法的传入参数类型
getDeclaredMethod("方法名",参数类型.class,……):获得特定的方法
3、执行方法
method.invoke(cvt, param) // cvt为动态创建的实例对象,param为执行方法method需要带的参数
4、获取构造方法
getDeclaredConstructors():获取所有的构造方法
getDeclaredConstructor(参数类型.class,……):获取特定的构造方法
5、获取实例对象的方法
// 调用类的Class对象的newInstance方法Class<?> classType = ExtendType.class;Object inst = classType.newInstance();// 调用默认Constructor对象的newInstance方法Class<?> classType = ExtendType.class;Constructor<?> constructor1 = classType.getConstructor();Object inst = constructor1.newInstance();// 调用带参数Constructor对象的newInstance方法Constructor<?> constructor2 = classType.getDeclaredConstructor(int.class, String.class);Object inst = constructor2.newInstance(1, "123");
6、获取父类信息
getSuperclass():获取某类的父类
getInterfaces():获取某类实现的接口
7、获取注解信息
8、获取属性信息
private BaseConvert createConvert(String string) {BaseConvert cvt = null;String className = getClassName(string); // 完整类名try {Class<?> cls = Class.forName(className); // 首先必须根据全类名获取cls对象Method method = cls.getMethod("setmSource", String.class); // 根据cls对象获取指定的方法,参数(方法名称,方法参数类型)String param = string.toLowerCase();Constructor<?> constructor = cls.getDeclaredConstructor(); // 根据cls对象获取构造函数cvt = (BaseConvert) constructor.newInstance(); // 根据构造函数新建类的实例cvtmethod.invoke(cvt, param); // 调用实例对象cvt的方法method,参数(当前是实例对象,传入的参数)} catch (Exception e) {e.printStackTrace();}return cvt;}
在线的应用不会频繁使用反射,因为反射的性能开销较大。其实还有一种和反射一样强大的特性,但是开销却很低,它就是Javassit。
Javassit其实就是一个二方包,提供了运行时操作Java字节码的方法。大家都知道,Java代码编译完会生成.class文件,就是一堆字节码。JVM(准确说是JIT)会解释执行这些字节码(转换为机器码并执行),由于字节码的解释执行是在运行时进行的,那我们能否手工编写字节码,再由JVM执行呢?答案是肯定的,而Javassist就提供了一些方便的方法,让我们通过这些方法生成字节码。
类似字节码操作方法还有ASM。几种动态编程方法相比较,在性能上Javassist高于反射,但低于ASM,因为Javassist增加了一层抽象。在实现成本上Javassist和反射都很低,而ASM由于直接操作字节码,相比Javassist源码级别的api实现成本高很多。几个方法有自己的应用场景,比如Kryo使用的是ASM,追求性能的最大化。而NBeanCopyUtil采用的是Javassist,在对象拷贝的性能上也已经明显高于其他的库,并保持高易用性。实际项目中推荐先用Javassist实现原型,若在性能测试中发现Javassist成为了性能瓶颈,再考虑使用其他字节码操作方法做优化。
Javassist的使用很简单,首先获取到class定义的容器ClassPool,通过它获取已经编译好的类(Compile time class),并给这个类设置一个父类,而writeFile讲这个类的定义从新写到磁盘,以便后面使用。
ClassPool pool = ClassPool.getDefault();CtClass cc = pool.get("test.Rectangle");cc.setSuperclass(pool.get("test.Point"));cc.writeFile();
由CtClass可以方便的获取字节码和加载字节码:
byte[] b = cc.toBytecode();Class clazz = cc.toClass();
如果需要定义一个新类,只需要
ClassPool pool = ClassPool.getDefault();CtClass cc = pool.makeClass("Point");
同样的还可以通过CtMethod和CtField构造方法和成员甚至Annotation。
ClassPool pool = ClassPool.getDefault();CtClass cc = pool.makeClass("foo");CtMethod mthd = CtNewMethod.make("public Integer getInteger() { return null; }", cc);cc.addMethod(mthd);
CtField f = new CtField(CtClass.intType, "i", cc);point.addField(f);
clazz = cc.toClass(); Object instance = class.newInstance();
Javassist不仅可以生成类、变量和方法,还可以操作现有的方法,这在AOP上非常有用,比如做方法调用的埋点
// Point.javaclass Point { int x, y; void move(int dx, int dy) { x += dx; y += dy; }}// 对已有代码每次move执行时做埋点ClassPool pool = ClassPool.getDefault();CtClass cc = pool.get("Point");CtMethod m = cc.getDeclaredMethod("move");m.insertBefore("{ System.out.println($1); System.out.println($2); }");cc.writeFile();
其中$1和$2表示调用栈中的第一和第二个参数,写到磁盘后的class定义类似:
class Point { int x, y; void move(int dx, int dy) { { System.out.println(dx); System.out.println(dy); } x += dx; y += dy; }}
在使用Javassist时遇到过一些问题。
1 因为tomcat和jboss使用的是独立的classloader,而Javassist是通过默认的classloader加载类,因此直接对tomcat context中定义的类做toClass会抛出ClassCastException异常,可以用tomcat的classloader加载字节码。
CtClass cc = ...;Class c = cc.toClass(bean.getClass().getClassLoader());
2 发现在简单的测试中可以load的类,在tomcat中无法load。这是因为,ClassPool.getDefault()查找的路径和底层的JVM路径。而tomcat中定义了多个classloader,因此额外的class路径需要注册到ClassPool中。
pool.insertClassPath(new ClassClassPath(this.getClass()));
3 我想在运行时修改类的一个方法,但是JVM是不允许动态的reload类定义的。一旦classloader加载了一个class,在运行时就不能重新加载这个class的另一个版本,调用toClass()会抛LinkageError。因此需要绕过这种方式定义全新的class。而toClass()其实是当前thread所在的classloader加载class。
4 Javassist生成的字节码由于没有class声明,字节码创建变量及方法调用都需要通过反射。这点在在线的应用上的性能损失是不能接受的,受到NBeanCopyUtil实现的启发,可以定义一个Interface,Javassist的字节码实现这个Interface,而调用方通过这个接口调用字节码,而不是反射,这样避免了反射调用的开销。还有一点字节码new一个变量也是通过反射,因此通过代理的方法,将每个pv都需要new的字节码对象改为每次new一个代理对象,代理到常驻内存的字节码对象中,这样避免了每次反射的开销。
- JAVA 反射学习总结
- Java反射学习总结
- Java反射学习总结
- java反射学习总结
- Java学习总结---反射
- Java反射学习总结
- Java反射学习总结
- Java反射学习总结
- java反射学习总结
- Java学习总结-反射
- java反射学习总结
- java反射学习 总结
- java反射机制学习总结
- java反射机制学习总结
- Java学习总结之反射
- java反射机制学习总结
- JAVA反射机制学习总结
- Java学习总结:反射机制
- 查找算法及二叉平衡树
- Tomcat服务器启动闪退,非jdk原因
- iOS消息推送
- unbantu下php 中smarty模板配置过程
- 想涨工资吗?那就学习Scala,Golang或Python吧
- Java反射学习总结
- Redis 哈希(Hash)
- Android权限
- Unix & Linux笔记(6)
- 百团大战
- 一步一个脚印的走法-走了N年
- Groovy基础总结
- Android 调用系统相机返回data为null
- 广播——broadcast