1:动态语言:
在运行时,能够改变程序结构和类型。(java不行,如:python,js,ruby)
c,c++,java却可以通过反射,操作字节码获得类似动态的特性。
2.反射机制:
可以在运行时,加载,探索,使用编译期间未知的类型。
在运行时,加载一个只知道类名的类,便能知道这个类的属性和方法,可以用来生成对象,对于对象可以调用其方法和属性。
(加载类,其实就是在堆内存中生成一个Class类型的对象(jvm通过加载器),并且每个类型都只会有一个Class对象。所以Class对象是反射的根源)
3.获取Class:
Class string = Class.forName("com.me.test.reflect.User");//一般用类全名(即包括包名)(之所可以不用,是import了 Class string2 = "stirng".getClass();//通过对象获取 Class string3 = String.class;//通过类型获取
a.数组是不同的维度对应不同的Class
b.像class interface enu void type private等这些关键词代表的意义也是Class类型
4.功能作用:
特别注意对于一些方法对于可变参数(即数组)而言,数组参数要转成Object,如果参数本身就是数组,不转换会该参数的元素被误认为,是多个参数的封装到数组了。
Object object = f.invoke(null, (Object) new String[] { "a", "b" },(Object) new String[] { "aa", "ab" });
获取属性,方法 ,构造器的时候,带declared的方法名才能获取所有范围的,否则只能获取public修饰的。比如:declaredField()和getField()。
Class userClass =Class.forName("com.me.test.reflect.User"); 1.// 获取属性,只能是public修饰的 Field field = userClass.getField("id");// 否则找不到,异常 // 可以是任意范围 Field declaredField = userClass.getDeclaredField("pId"); // public int com.me.test.reflect.User.id----private int // com.me.test.reflect.User.pId System.out.println(field + "----" + declaredField); // 只能获取public属性 Field[] fields = userClass.getFields(); // 所有属性 Field[] declaredF = userClass.getDeclaredFields(); // [public int com.me.test.reflect.User.id] System.out.println(Arrays.toString(fields)); // [private int com.me.test.reflect.User.pId, // public int com.me.test.reflect.User.id, // protected java.lang.String com.me.test.reflect.User.proName] System.out.println(Arrays.toString(declaredF)); 2.//获取无参方法可以不写参数类型 Method getName = userClass.getMethod("getName"); Method setName = userClass.getMethod("setName", String.class); Method declaredsetName = userClass.getDeclaredMethod("setName", String.class); Method[] methods = userClass.getMethods(); Method[] declaredMethods = userClass.getDeclaredMethods(); 3.//获得构造器 Constructor[] constructors = userClass.getConstructors(); Constructor[] declaredconstructors2 = userClass.getDeclaredConstructors(); Constructor<User> constructor = userClass.getConstructor(int.class,String.class); Constructor<User> declaredconstructor2 = userClass.getDeclaredConstructor(int.class,String.class); 4.//创建有参数的对象需要先获得构造器 User newInstance = userClass.newInstance();//无参的字节码对象直接创建 User newInstance2 = constructor.newInstance(1,"大王"); 5.//设置用属性,首先获得属性对象,以及属性所属的对象 field.setAccessible(true);//true表示不进行安全检查, 对于私有属性和方法才能操作 field.set(newInstance,11 ); 6.//调用方法,首先获得方法对象,以及调用方法的对象 declaredsetName.invoke(newInstance, "三王");
5.反射执行效率低于正常执行,比如执行10亿次getName()方法
普通大约:2258ms 1倍
反射大约:62687ms 30倍
不安全检查的反射:14305ms 6倍
6.反射获取泛型:java的泛型只存在编译期,所以为了得到泛型,java提供了一些不属于Class的类型来获取泛型。下面类型和Class类型同属Type的子类型
public void test1(Map<String, User> map, List<User> l) {} public Map<String, User> test2() {return new HashMap<String, User>();} Method test1 = ReflectTest.class.getMethod("test1", Map.class, List.class); // 获得方法的参数类型数组 Type[] genericParameterTypes = test1.getGenericParameterTypes(); // [java.util.Map<java.lang.String, com.me.test.reflect.User>, // java.util.List<com.me.test.reflect.User>] System.out.println(Arrays.toString(genericParameterTypes)); for (Type gType : genericParameterTypes) { // 判断是否是泛型参数 if (gType instanceof ParameterizedType) { // 获得泛型参数中的泛型类型 Type[] actualTypeArguments = ((ParameterizedType) gType).getActualTypeArguments(); // 第一次循环: [class java.lang.String, class com.me.test.reflect.User] // 第二次循环: [class com.me.test.reflect.User] System.out.println(Arrays.toString(actualTypeArguments)); } } Method test2 = ReflectTest.class.getMethod("test2"); // 获得返回类型 Type genericReturnType = test2.getGenericReturnType(); // [java.util.Map<java.lang.String, com.me.test.reflect.User>, // java.util.List<com.me.test.reflect.User>] System.out.println(genericReturnType); // 返回类型是否是泛型 if (genericReturnType instanceof ParameterizedType) { // 获得泛型参数中的泛型类型 Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments(); // 输出: [class java.lang.String, class com.me.test.reflect.User] System.out.println(Arrays.toString(actualTypeArguments)); }
7.反射获取注解:
8.动态编译:动态的加载一些类文件进行编译。
java1.6引入了动态编译机制之前的类似效果的方式:
Runtime runtime = Runtime.getRuntime(); runtime.exec("javac -cp d:/mytest/ Hello.java");//编译 Process process = runtime.exec("java -cp d:/mytest/ Hello");//运行 //可以从输入流中读取到打印的信息 InputStream inputStream = process.getInputStream();
java6引入了javaCompile类(如果想编译一个字符串的程序,可以考虑先弄成.java文件。)
JavaCompiler systemJavaCompiler = ToolProvider.getSystemJavaCompiler(); // 四个参数in, out, err, arguments,前三个默认是标准输入,输出,错误输出 // 会在同位生成字节码文件,返回0表示成功 int run = systemJavaCompiler.run(null, null, null, "d:/mytest/ Hello.java");
可以通过反射运行字节码:
URL[] urls = new URL[] { new URL("file:/" + "d:/mytest/") };//url不仅可以封装链接,还可以是文件目录(感觉用的少) URLClassLoader classLoader = URLClassLoader.newInstance(urls);// 获得加载器 Class<?> hello = classLoader.loadClass("Hello");// 加载字节码生成Class对象 Method f = hello.getDeclaredMethod("f", String[].class);// void f(String[] s){..} // 对于静态方法可以所属对象可以是null // 特别注意为什么要转成Object,对于可变参数(即数组)而言, // 如果参数本身就是数组,不转换会该参数的元素被误认为,是多个参数的封装到数组了。 Object object = f.invoke(null, (Object) new String[] { "a", "b" });
9.java脚步引擎(1.6引入),通过一套固定的接口,实现和脚步引擎交互,从而可以将一些复杂业务逻辑交给脚本语言处理。比如用js运行字符串“12+3-3*/5=”的计算。
java6中将Rhino(由java语言编写可以实现js)引擎集成了(来源v215暂略)
10.字节码操作:java动态性一般由反射和字节码实现的。
1.操作字节码可以在运行时,动态生成新的类,改变类的结构(增删改属性,方法)。
2.效率比反射高,
10.1一些字节码框架:(一些应用级别的框架都用到他们,如spring hibernate ...)(比上面8自带的强大)
BCEL:byte code engineering Library,apache项目的一部分,可以深入jvm汇编语言操作类细节,拥有指令集级别的操作。 ASM:轻量级别的字节码操作,直接涉及jvm底层的操作和指令。 CGLIB:code generation library,基于ASM的代码生成库框架 Javassist:一个开源的分析,编辑,创建字节码的类库,性能和CGLIB差不多,但使用更加灵活。
10.2 Javassist可以用来面向切面编程,也可以实现反射效果。(使用前需要首先倒入此包)(略)
11.类加载过程:jvm把class文件加载到内存,并对数据进行校验,解析,初始化,最后能够使用该类型的过程。目的:了解jvm运行过程,java动态性:热部署,动态加载,增强程序灵活性。
11.1 加载:将.class字节码内容加载到内存,并将静态数据转换成方法区中运行时数据结构,在堆中生成一个代表这个类的class对象。作为方法区类数据访问的入口(需要类加载器的参与)
链接:
初始化:
11.2不初始化类的情况:
a.调用类的常量时不会初始化类(final static)
b.子类调用父类的静态属性不会初始化子类。
12.类加载器
类加载器的作用:
类加载器的机制:
获取加载器的对象:
System.out.println(System.getProperty("java.class.path")); System.out.println("自定义类的加载器:"+User.class.getClassLoader()); System.out.println(ClassLoader.getSystemClassLoader()); System.out.println(ClassLoader.getSystemClassLoader().getParent()); System.out.println(ClassLoader.getSystemClassLoader().getParent().getParent());// 对于引导加载器不能获取到
输出:
E:workspace2 estin 自定义类的加载器:sun.misc.Launcher$AppClassLoader@addbf1 sun.misc.Launcher$AppClassLoader@addbf1 sun.misc.Launcher$ExtClassLoader@42e816 null
线程上下文类加载器
osgi
equinox