• 基础笔记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	estin
    自定义类的加载器:sun.misc.Launcher$AppClassLoader@addbf1
    
    sun.misc.Launcher$AppClassLoader@addbf1
    sun.misc.Launcher$ExtClassLoader@42e816
    null

    线程上下文类加载器

    osgi

    equinox

  • 相关阅读:
    自己实现的一个简单的C# IOC 容器
    C# 内存缓存工具类 MemoryCacheUtil
    使用触发器和C#程序实现数据同步
    Maven 命令安装指定 jar 包到本地仓库
    C# RSA 非对称加密
    JS可选链操作符?.和双问号??
    Learn D3 入门文档: Introduction
    Lerna 基本概念
    图片 src 为二进制的处理
    ASCII 和 Base64
  • 原文地址:https://www.cnblogs.com/straybirds/p/6254835.html
Copyright © 2020-2023  润新知