Java编程的动态性(author Dennis M. Sosnoski )学习——学习笔记(1)
来源:互联网 发布:中国出口武器 知乎 编辑:程序博客网 时间:2024/05/20 08:24
类加载器
装入到JVM的类由类装入器控制。JVM中构建了引导程序类装入器。由引导程序对类进行验证。
同时应用程序可以自定义类装载器(派生自java.lang.ClassLoader)。每个构造好的类某种意义上由类装载器所“拥有”。类装入器通常保留它们所装入类的映射,从而当再次请求某个类时,能通过名称找到该类。
类装载器为树状结构。树根是引导程序装载器。在类装载器处理实例请求时,会递归检查父类。这意味着类装载器中的类对其后代均可见,并且当多个类装载器可以装在某个类时,最上端的类装载器是实际装入器。
在J2EE框架中,每个j2ee程序都有自己独立的类装入器,
Tomcat类装入器
其中, Common 类装入器从 Tomcat 安装的某个特定目录的 JAR 文件进行装入,旨在用于在服务器和所有 Web 应用程序之间共享代码。Catalina 装入器用于装入 Tomcat 自己的类,而 Shared 装入器用于装入 Web 应用程序之间共享的类。最后,每个 Web 应用程序有自己的装入器用于其私有类。
引入反射
1.基于类的反射
Class[] types = new Class[]{};//传入构造器的参数Constructor cons = Person.class.getConstructor(types);//按照参数寻找构造器Object[] a = new Object[]{"a","b",1};//实际传入参数的值Person p = cons.newInstance(a);//构造
缺省调用:
Person.class.getConstructor().newInstance();
2.通过反射增加字段
Field用来获取字段存储的位置
getField()和getDeclaredField()不同在于第二个可以访问私有变量。
//获取值Field field = obj.getClass().getDeclaredField(name);Object i = field.get(obj);//设定值field.set(obj,value);
3.反射获取方法
getMethod/getDeclaredMethod
命名方法的方式是驼峰法
Method method = obj.getClass().getMethod(methodName,types);//types可以缺省,为方法参数类型Object result = method.invoke(obj,object[]);//object[]为方法参数,可以缺省
4.反射数组
通过反射来扩展数组
public Object growArray(Object array, int size) { Class type = array.getClass().getComponentType();//获取数组中数据的类型 Object grown = Array.newInstance(type, size);//创建新的数组 System.arraycopy(array, 0, grown, 0, Math.min(Array.getLength(array), size));//调整数组的大小 return grown;}
5.反射的安全性
Constructor,Field,Method均扩展了java.lang.reflect.AccessibleObject实例。调用setAccessible()方法,可以启动或关闭对一个实例的接入检测。
file = class.getDeclaredField("a");//a是私有变量file.get(obj);//抛出异常//如果关闭检测filed.setAccessible(true);file.get(obj);//正常
特别的,在关闭检测的情况下,如果运行时添加JVM参数 -Djava.security.manager 以实现安全性管理器,它将再次失败,除非您定义了 ReflectSecurity 类的许可权限。
6.反射对性能的影响
反射获取防方法和字段均在直接获取时间的700倍以上,获取对象相对较短,在12倍。
使用Javassist进行类型转换
1.概念:javassist是使用javassist.ClassPool类来跟踪和控制所操作的类。与JVM类装载器不同的是,它不是装载,而是通过Javassist API作为数据使用。
2.常用的方法:
ClassPool:获取指定路径中的类
CtClass ct = ClassPool.getDefault().get(Object)//获取默认路径中的类ct.getDeclaredMethod();//方法
字段、方法和构造函数分别由 javassist.CtField、javassist.CtMethod 和 javassist.CtConstructor 的实例表示。这些类定义了修改由它们所表示的对象的所有方法的方法,包括方法或者构造函数中的实际字节码内容。
3.解决思路
·获取方法
·复制一个新的方法,把旧的方法名改为:name+”
·由ctclass写入。
需要计时的方法
public class StringBuilder{//该方法是对一个string进行反复构造来延长执行时间,需要添加计时功能,注释内是希望达到的效果 private String buildString(int length) { // long start = System.currentTimeMillis(); String result = ""; for (int i = 0; i < length; i++) { result += (char)(i%26 + 'a'); } //System.out.println("Call to buildString took " + (System.currentTimeMillis()-start) + " ms."); return result; } public static void main(String[] argv) { StringBuilder inst = new StringBuilder(); for (int i = 0; i < argv.length; i++) { String result = inst.buildString(Integer.parseInt(argv[i])); System.out.println("Constructed string of length " + result.length()); } }}
添加计时:
public class JassistTiming { public static void main(String[] argv) { if (argv.length == 2) {//argv[0]表示类,argv[1]表示方法名 try { CtClass clas = ClassPool.getDefault().get(argv[0]); if (clas == null) { System.err.println("Class " + argv[0] + " not found"); } else { addTiming(clas, argv[1]); clas.writeFile();//更新.class文件 System.out.println("Added timing to method " + argv[0] + "." + argv[1]); } } catch (CannotCompileException ex) { ex.printStackTrace(); } catch (NotFoundException ex) { ex.printStackTrace(); } catch (IOException ex) { ex.printStackTrace(); } } else { System.out.println("Usage: JassistTiming class method-name"); } } private static void addTiming(CtClass clas, String mname) throws NotFoundException, CannotCompileException { CtMethod mold = clas.getDeclaredMethod(mname); //把原本的方法重命名为func+$impl,新建一个方法,把它命名为原本的func String nname = mname+"$impl"; mold.setName(nname); CtMethod mnew = CtNewMethod.copy(mold, mname, clas, null); //type是原函数的返回值的类型 String type = mold.getReturnType().getName(); StringBuffer body = new StringBuffer();//待添加的函数主体 body.append("{\nlong start = System.currentTimeMillis();\n");//开始计时 if (!"void".equals(type)) {//如果无返回值 body.append(type + " result = "); } body.append(nname + "($$);\n");//调用$impl方法 //结束计时 body.append("System.out.println(\"Call to method " + mname + " took \" +\n (System.currentTimeMillis()-start) + " + "\" ms.\");\n"); if (!"void".equals(type)) { body.append("return result;\n");//如果返回值为空,则不处理 } body.append("}"); mnew.setBody(body.toString()); clas.addMethod(mnew); System.out.println("Interceptor method body:"); System.out.println(body.toString()); }}
在经过jassistTiming修改后,原本stringBuilder中的方法实际变成了
private String buildString$impl(int length) { String result = ""; for (int i = 0; i < length; i++) { result += (char)(i%26 + 'a'); } return result; } private String buildString(int length) { long start = System.currentTimeMillis(); String result = buildString$impl(length); System.out.println("Call to buildString took " + (System.currentTimeMillis()-start) + " ms."); return result; }
4.使用Jassist带来的风险
由于jassist采用比较宽松的编译时代码检查,所以可能带来问题。比如将long赋值给int,又或者把string存储到int中。
但同时,javassist带来的aop这种新的设计模式
- Java编程的动态性(author Dennis M. Sosnoski )学习——学习笔记(1)
- 我的 C 语言学习生涯记——纪念 Dennis M. Ritchie
- java学习笔记(五)——网络编程
- Java学习笔记——动态代理
- 学习笔记——JAVA动态编译
- 学习笔记——JAVA动态编译
- JAVA学习笔记Day25——动态接口的实现
- 《Java编程思想》学习笔记6——Java动态代理
- 《Java编程思想》学习笔记6——Java动态代理
- 《Java编程思想》学习笔记6——Java动态代理
- 《Java编程思想》学习笔记6——Java动态代理
- 《Java编程思想》学习笔记6——Java动态代理
- 《Java编程思想》学习笔记5——Java动态代理
- 《Java多线程编程核心技术》学习笔记(一)——Java的多线程
- java编程思想学习笔记(一)——java类与c++类的区别
- Java学习笔记(一):Java编程环境的搭建
- JDBC编程学习笔记(一)——与MySQL的连接 ConnMySql.java
- Java学习笔记——多线程编程
- 彻底解决Android 拍照 内存溢出 Out of Memory的问题
- Oracle大数据常见优化查询
- Universal-Image-Loader源码阅读(29)-BitmapDisplayer
- poj1159
- Spring Cloud构建微服务
- Java编程的动态性(author Dennis M. Sosnoski )学习——学习笔记(1)
- 一个简单的volley网络请求
- 如何在linux下查看目录的剩余空间大小
- 删除链表中的重复元素
- java基于spring框架上传文件
- 写时拷贝
- 都在做百度SEO,你是否真的了解百度?
- [转]【坐在马桶上看算法】算法2:邻居好说话:冒泡排序--作者:ahalei
- iOS-RAC学习笔记(二)——RACSignal订阅