基础笔记13(动态性:反射,动态编译,执行,操作)

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\test\bin
自定义类的加载器:sun.misc.Launcher$AppClassLoader@addbf1

sun.misc.Launcher$AppClassLoader@addbf1
sun.misc.Launcher$ExtClassLoader@42e816
null

 

线程上下文类加载器

osgi

equinox

 

posted @ 2017-01-06 03:57  假程序猿  阅读(552)  评论(0编辑  收藏  举报