反射

2019-04-07  本文已影响0人  Finlay_Li

什么是反射?

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;
    }
上一篇下一篇

猜你喜欢

热点阅读