反射

作者: Finlay_Li | 来源:发表于2019-04-07 16:53 被阅读0次

    什么是反射?

    java的内存模型,我们关注的点,一个方法区,一个栈,一个堆


    image.png

    首先我们了解一下JVM,什么是JVM,Java的虚拟机,java之所以能跨平台就是因为这个东西,你可以理解成一个进程,程序,只不过他的作用是用来跑你的代码的。

    假如你写了一段代码:Object o=new Object(); 运行了起来!

    1. JVM会启动,你的代码会编译成一个.class文件,然后被类加载器加载进jvm的内存中
    2. 你的类Object加载到方法区中,方法区中就存在了这个类的所有描述/资料。
    3. 然后创建了Object类的Class对象到堆中,注意这个不是new出来的对象,而是类的类型对象,每个类只有一个class对象,作为方法区类的数据结构的接口。
    4. jvm检查类是否加载,寻找类对应的class对象,若加载好,则为你的对象分配内存,初始化 = new Object()。

    为什么要讲这个呢?因为要理解反射必须知道它在什么场景下使用

    题主想想上面的程序对象是自己new的,程序相当于写死了给jvm去跑。假如一个服务器上突然遇到某个请求哦要用到某个类,哎呀但没加载进jvm,是不是要停下来自己写段代码,new一下,再启动一下服务器?

    反射是什么呢?当我们的程序在运行时,需要动态的加载一些类这些类可能之前用不到所以之前没有加载到jvm,而是在运行时根据需要才加载,这样的好处对于服务器来说不言而喻

    举例子

    1. 项目底层有时是用mysql,有时用oracle,需要动态地根据实际情况加载驱动类,这个时候反射就有用了,假设 com.java.dbtest.myqlConnection,com.java.dbtest.oracleConnection这两个类我们要用,这时候我们的程序就写得比较动态化
      通过Class tc =Class.forName("com.java.dbtest.TestConnection"); 让jvm在服务器中找到并加载这个类,而如果是oracle则传入的参数就变成另一个了。这时候就可以看到反射的好处了,这个动态性就体现出java的特性了!

    2. 如果接触过spring,会发现当你配置各种各样的bean时,是以配置文件的形式配置的,你需要用到哪些bean就配哪些,spring容器就会根据你的需求去动态加载,你的程序就能健壮地运行。

    Java程序运行分为两种状态:

    1、编译时:类编译为 .class 字节码文件时。

    2、运行时:将生成的 .class 字节码文件加载到内存中。

    泛型和反射的区别

    1、不知道在程序的运行中哪些类型要用——>泛型

    2、不知道哪个类在程序运行中会被实例化——反射

    Class 是开启反射的源头

    加载进JVM的类,JVM会先创建对应类的Class实例
    当获取了jvm 创建的Class 实例,则可调用这个类的所有描述/资料。

    如何获取 Class 实例?

    1. 使用运行时类的 class 属性

      Class clazz1 = Person.class;
      
      System.out.println(clazz1);
      
    2. 使用运行时类对象的 getClass() 方法

      Person p = new Person();
      
      Class clazz2 = p.getClass();
      
      System.out.println(clazz2);
      
    3. 使用 Class 类的静态方法 forName(String className)

       String className = "com.atguigu.java.Person";
      
       Class clazz3 = Class.forName(className)
      
       System.out.println(clazz3);
      
    4. (了解)使用类加载器

      String className = "com.atguigu.java.Person";
      
      ClassLoader cl = this.getClass().getClassLoader();
      
      Class clazz4 = cl.loadClass(className);
      
      System.out.println(clazz4);
      

    反射的使用

    在程序运行时获取类信息

    1. Fields

    Field[] getFields() :获取所有 public 修饰的属性,包括父类的
    Field[] getDeclaredFields():获取当前类中所有的属性,包括私有的,不包括父类的

    
    
           Class clazz = Person.class;
    
            Field[] fields = clazz.getDeclaredFields();
    
            for (Field field : fields) {
    
                //1 修饰符
    
                int mod = field.getModifiers();
    
                String strMod = Modifier.toString(mod);
    
                System.out.print(strMod + "\t");
    
                //2 数据类型
    
                Class type = field.getType();
    
                System.out.print(type.getName() + "\t");
    
                //3 属性名
    
                System.out.println(field.getName());
    
    
    1. Methods:

    Method[] getMethods() : 获取所有 public 修饰的方法,包括父类的
    Method[] getDeclaredMethods() : 获取当前类中所有的方法,包括私有的,不包括父

            Class clazz = Person.class;
    
            Method[] methods = clazz.getDeclaredMethods();
    
            for (Method method : methods) {
    
                //0 获取方法注解
    
                Annotation[] annotations = method.getAnnotations();
    
                for (Annotation annotation : annotations) {
    
                    System.out.println(annotation);
    
                }
    
                //1 获取修饰符
    
                String mod = Modifier.toString(method.getModifiers());
    
                System.out.print(mod + "\t");
    
                //2 获取返回值类型
    
                Class returnType = method.getReturnType();
    
                System.out.print(returnType.getName() + "\t");
    
                //3 获取方法名
    
                System.out.print(method.getName() + "(");
    
                //4 获取参数列表
    
                Class[] paramTypes = method.getParameterTypes();
    
                for (Class class1 : paramTypes) {
    
                    System.out.print(class1.getName() + ",");
    
                }
    
                System.out.println(")");
    
                //5 获取方法异常
    
                Class[] exceptionTypes = method.getExceptionTypes();
    
                for (Class class1 : exceptionTypes) {
    
                    System.out.println(class1.getName());
    
                }
    
    1. 获取类的一些信息
                    1) 获取 运行时类 的父类
    
                               方法:getSuperclass()
    
                    2)获取带泛型父类的类型
    
                               方法:getGenericSuperclass()
    
                    4 )运行时获取运行时类的接口
    
                                 方法:getInterfaces()
    
                    5 )在运行时获取运行时类的内部类
    
                                 方法:getClasses()
    
                    6 )在运行时获取运行时类的注解
    
                                 方法:getAnnotations()
    
                    7) 获取自定义注解value
    
                                    MyAnnotation mya = (MyAnnotation) annotation;
    
                                    System.out.println(mya.value());
    
                     8)在运行时获取运行时类的包
    
                                 方法:getPackage()
    
                     9) 获取类的名字
    
                           String name = clazz.getName();
    
                           String simpleName = clazz.getSimpleName();
    
    1. 在运行时获取运行时类带泛型父类的 泛型类型
            public void test3(){
    
                Class clazz = Person.class;
    
                //1 获取带泛型父类类型,返回值为类型
    
               //com.atguigu.java.Creature<java.lang.String>
    
                Type type = clazz.getGenericSuperclass();
    
                //2参数化类型。在java中,长的像参数列表<java.lang.String>
    
                ParameterizedType pt = (ParameterizedType) type;
    
                //3 参数化获取的是Class中的泛型属性,因Calss实现了Type接口, 要获取真实类型数组
    
                Type[] types = pt.getActualTypeArguments();
    
                //4 类型类强转
    
                Class cl = (Class) types[0];
    
                System.out.println(cl.getName());
    
            }
    

    改变类信息或者调用运行时类方法

    1. 创建 运行时类对象
      方法:newInstance()
      ① 默认调用无参构造器
      ②结合泛型使用:可不用强转

    2. 操作属性

            public void test3() throws Exception{
    
                //1 使用forname()获取Person的大Class实例
    
                Class clazz=Class.forName("ReflectionGte/Person");
    
                //2 创建Person 实例
    
                Person person=(Person) clazz.newInstance();
    
                //3获取属性
    
                Field age=clazz.getField("age");
    
                //4得到这个属性实例进行操作
    
                age.set(person, 18);
    
                //5反射获取运行时类的属性值
    
                Object obj=age.get(person);
    
                System.out.println(obj);
    
                //6 私有属性的访问
    
                Field name = clazz.getDeclaredField("name");
    
                name.setAccessible(true);//忽略访问权限
    
                // 私有属性的操作
    
                name.set(person, "张三");
    
                //查看
    
                Object obj2 = name.get(person);
    
                System.out.println(obj2);
    
            }
    
    1. 操作方法:
            public void test4() throws Exception{
    
                        //获取类的Class
    
                Class clazz=Class.forName("ReflectionGte.Person");
    
                // 创建Person 实例
    
                Person person=(Person) clazz.newInstance();
    
                //无参无返回的
    
                Method m1=clazz.getMethod("eat");
    
                Object obj2 = m1.invoke(person);
    
                System.out.println(obj2);
    
                //有参有返回的
    
                Method m2 = clazz.getMethod("setName", String.class, double.class, int.class);
    
                Object obj = m2.invoke(person, "张三", 99.99, 18);
    
                System.out.println("--" + obj);
    
                //私有的!
    
                //Method m3 = clazz.getMethod("sleep");
    
                Method m3 = clazz.getDeclaredMethod("sleep");
    
                m3.setAccessible(true);//忽略访问权限
    
                m3.invoke(person);
    
            }
    

    反射+泛型结合

    目的

    不确定要用什么类,返回哪个对象

    解释

      public <T> T   get(Class<T> clazz){
    
            //查询表数据,封装对象,返回
    
           // return clazz.newInstance();
    
            return clazz.cast(obj);
    
        }
    
    1. public <T> T

    未知数据类型,类中泛型也不适用,使用方法泛型

    1. Class<T> clazz

    未知返回创建什么对象,使用反射

    1. return clazz.newInstance();

    使用时确定传入什么泛型类型,就返回什么对象的实例

    示例

        @GetMapping("/infoByCart/{productId}")
        @ApiOperation("PC端:获取购物车商品详情")
        @ApiImplicitParam(name = "productId", value = "商品id", paramType = "query")
        public Result<CartProductInfoRes> infoByCart(@PathVariable @Min(1L) Long productId) {
            Long languageId = getLanguageId();
            RedisProductInfoRes redisProductInfoRes = getRedisProductInfoRes(productId, languageId);
            //数据过滤
            Class<CartProductInfoRes> cartProductInfoResClass = CartProductInfoRes.class;
            CartProductInfoRes cartProductInfo = productInfoFilter(redisProductInfoRes, cartProductInfoResClass, languageId);
            return succeed(cartProductInfo);
        }
    
        /**
         * 组装pc端的商品详情数据
         *
         * @param redisProductInfoRes redis的商品详情数据
         * @param languageId          语言id
         * @return com.olight.omall.product.dto.res.CartProductInfoRes
         */
        private <E> E productInfoFilter(RedisProductInfoRes redisProductInfoRes, Class<E> clazz, Long languageId) {
            E instance = null;
            try {
                instance = clazz.newInstance();
            } catch (Exception e) {
                throw new OcloudBusinessException(ProductErrorCodeEnum.ERROR_CODE_10006);
            }
            if (redisProductInfoRes == null) {
                return instance;
            }
            //基本数据多语言过滤
            List<ProductLangRes> productLangRes = redisProductInfoRes.getProductLangRes();
            for (ProductLangRes productLang : productLangRes) {
                if (languageId.equals(productLang.getLanguageId())) {
                    OcloudBeanUtils.copy(productLang, instance);
                    break;
                }
            }
            OcloudBeanUtils.copy(redisProductInfoRes, instance);
            return instance;
        }
    

    相关文章

      网友评论

          本文标题:反射

          本文链接:https://www.haomeiwen.com/subject/dmpsiqtx.html