• java 反射


    java反射主要从以下几个方面理解

    • 理解 Class 类
    • 理解 Java 的类加载机制
    • 学会使用 ClassLoader 进行类加载
    • 理解反射的机制
    • 掌握 Constructor、Method、Field 类的用法
    • 理解并掌握动态代理

    1、理解Class类

    Java程序在运行时,Java运行时系统一直对所有的对象进行所谓的运行时类型标识,即所谓的RTTI。这项信息纪录了每个对象所属的类。虚拟机通常使用运行时类型信息选准正确方法去执行,用来保存这些类型信息的类是Class类。Class类封装一个对象和接口运行时的状态,当装载类时,Class类型的对象自动创建。
    简单解释上面一段话
    1、Class类也是类的一种,只是名字和class关键字高度相似。Java是大小写敏感的语言。
    2、Class类的对象内容是你创建的类的类型信息,比如你创建一个shapes类,那么,Java会生成一个内容是shapes的Class类的对象。
    3、Class类的对象不能像普通类一样,以 new shapes() 的方式创建,它的对象只能由JVM创建,因为这个类没有public构造函数。
    4、Class类的作用是运行时提供或获得某个对象的类型信息。

    Class原理

    所有java类都是继承了object这个类,在object这个类中有一个方法:getclass().这个方法是用来取得该类已经被实例化了的对象的该类的引用,这个引用指向的是Class类的对象。

    怎么获取Class对象

    1、Class类的forName方法

     Class<?> aClass = Class.forName("com.sl.reflect.Student");
    

    2、对象的getClass()方法

     Student student = new Student();
     Class<? extends Student> aClass1 = student.getClass();
    

    3、使用类名加.class

     Class classes = Student.class.getClass();
    

    4、通过ClassLoader对象的loadClass()方法

    Class<?> aClass2 = ClassLoader.getSystemClassLoader().loadClass("com.sl.reflect.Student");

    敲黑板调用newInstance()方法时,调用的是无参构造器,所以每个类中一定要声明一个无参构造器

    ClassLoader加载

    类装载器是用来把类(class)装载进 JVM 的。JVM 规范定义了两种类型的类装载器:启动类装载器(bootstrap)和用户自定义装载器(user-defined class loader)。 JVM在运行时会产生3个类加载器组成的初始化加载器层次结构 ,如下图所示:

     
     
    image.png
            //1、获取系统类的加载器
            ClassLoader classLoader = ClassLoader.getSystemClassLoader();
            System.out.println(classLoader);
    
            //2. 获取系统类加载器的父类加载器(扩展类加载器,可以获取).
            classLoader = classLoader.getParent();
            System.out.println(classLoader);
    
            //3. 获取扩展类加载器的父类加载器(引导类加载器,不可获取).
            classLoader = classLoader.getParent();
            System.out.println(classLoader);
    

    注意系统类加载器可以加载当前项目src目录下面的所有类,如果文件也放在src下面,也可以用类加载器来加载调用 getResourceAsStream 获取类路径下的文件对应的输入流。

           //文件夹在src下
            InputStream resourceAsStream = ClassLoader.getSystemClassLoader().getResourceAsStream("text1.txt");
            //文件夹在包名下
            InputStream resourceAsStream1 = ClassLoader.getSystemClassLoader().getResourceAsStream("com/sl/reflect/text2.txt");
    

    反射

    Java反射机制主要提供了以下功能

    • 在运行时构造任意一个类的对象
    • 在运行时获取任意一个类所具有的成员变量和方法
    • 在运行时调用任意一个对象的方法(属性)
    • 生成动态代理

    Student测试类

    /**
     * @author shuliangzhao
     * @Title: Student
     * @ProjectName design-parent
     * @Description: TODO
     * @date 2019/6/15 23:08
     */
    public class Student {
    
        private String name;
    
        private Integer age;
    
        public Student() {
    
        }
    
        public Student(String name,Integer age) {
            this.name = name;
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    }
    
    Method类
      public static void testMethod() throws Exception {
            Class<Student> aClass = (Class<Student>) Class.forName("com.sl.reflect.Student");
            //1.获取方法
            // 获取取clazz对应类中的所有方法--方法数组(一)
            // 不能获取private方法,并且获取从父类继承来的所有方法
            Method[] methods = aClass.getMethods();
            for (Method method:methods) {
                System.out.println(method);
            }
            System.out.println("================================");
            //2.获取方法
            // 获取取clazz对应类中的所有方法--方法数组(一)
            // 不能获取private方法,不获取从父类继承来的所有方法
            Method[] declaredMethods = aClass.getDeclaredMethods();
            for (Method method:declaredMethods) {
                System.out.println(method);
            }
    
            System.out.println("=================================");
            //  1.3.获取指定的方法
            //  需要参数名称和参数列表,无参则不需要写
            //  对于方法public void setName(String name) {  }
            Method method = aClass.getDeclaredMethod("setName", String.class);
            System.out.println(method);
            //  而对于方法public void setAge(int age) {  }
            method = aClass.getDeclaredMethod("setAge", Integer.class);
            System.out.println(method);
            //  这样写是获取不到的,如果方法的参数类型是int型
            //  如果方法用于反射,那么要么int类型写成Integer: public void setAge(Integer age) {  }
            //  要么获取方法的参数写成int.class
    
            //2.执行方法
            //  invoke第一个参数表示执行哪个对象的方法,剩下的参数是执行方法时需要传入的参数
            Object obje = aClass.newInstance();
            method.invoke(obje,2);
    
            //如果一个方法是私有方法,第三步是可以获取到的,但是这一步却不能执行
            //私有方法的执行,必须在调用invoke之前加上一句method.setAccessible(true);
        }
    
        /**
          *  把类对象和类方法名作为参数,执行方法
         *
         *  把全类名和方法名作为参数,执行方法
         *  可变参数可以放数组
         * @param obj: 方法执行的那个对象.
         * @param methodName: 类的一个方法的方法名. 该方法也可能是私有方法.
         * @param args: 调用该方法需要传入的参数
         * @return: 调用方法后的返回值
         *
         */
        public Object invoke(Object obj, String methodName, Object ... args) throws Exception{
            //1. 获取 Method 对象
            //   因为getMethod的参数为Class列表类型,所以要把参数args转化为对应的Class类型。
    
            Class [] parameterTypes = new Class[args.length];
            for(int i = 0; i < args.length; i++){
                parameterTypes[i] = args[i].getClass();
                System.out.println(parameterTypes[i]);
            }
    
            Method method = obj.getClass().getDeclaredMethod(methodName, parameterTypes);
            //如果使用getDeclaredMethod,就不能获取父类方法,如果使用getMethod,就不能获取私有方法
    
            //
            //2. 执行 Method 方法
            //3. 返回方法的返回值
            return method.invoke(obj, args);
        }
    
    Field类
    public static void testField() throws Exception {
            Class<Student> aClass = (Class<Student>) Class.forName("com.sl.reflect.Student");
            //1.获取字段
            //  1.1 获取所有字段 -- 字段数组
            //     可以获取公用和私有的所有字段,但不能获取父类字段
            Field[] declaredFields = aClass.getDeclaredFields();
            for (Field field:declaredFields) {
                System.out.println(field);
            }
            System.out.println("=============================");
            //  1.2获取指定字段
            Field field = aClass.getDeclaredField("name");
            System.out.println(field.getName());
    
            System.out.println("==============================");
    
            Student student = new Student();
            //如果字段是私有的,不管是读值还是写值,都必须先调用setAccessible(true)方法
            field.setAccessible(true);
            student.setAge(1);
            student.setName("张三");
            //2.使用字段
            //  2.1获取指定对象的指定字段的值
            Object o = field.get(student);
            System.out.println(o);
            System.out.println("==========================");
            //  2.2设置指定对象的指定对象Field值
            field.set(student, "DEF");
            System.out.println(student.getName());
        }
    
    Constructor类
     public static void testConstructor() throws Exception{
            Class<Student> aClass = (Class<Student>) Class.forName("com.sl.reflect.Student");
            //1. 获取 Constructor 对象
            //   1.1 获取全部
            Constructor<?>[] constructors = aClass.getConstructors();
            for (Constructor constructor:constructors) {
                System.out.println(constructor);
            }
            System.out.println("============================");
            //  1.2获取某一个,需要参数列表
            Constructor<Student> constructor = aClass.getConstructor(String.class, Integer.class);
            System.out.println(constructor);
            System.out.println("============================");
            //2. 调用构造器的 newInstance() 方法创建对象
            Object obj = constructor.newInstance("zhagn", 1);
        }
    
    Annotation
    @Retention(RetentionPolicy.RUNTIME)
    @Target(value={ElementType.METHOD})
    public @interface AgeValidator {
        public int min();
        public int max();
    }
    
    public static void testAnnotation() throws Exception{
            Class<?> aClass = Class.forName("com.sl.reflect.Student");
            Object o = aClass.newInstance();
            Method method = aClass.getDeclaredMethod("setAge", Integer.class);
            int val = 6;
            AgeValidator annotation = method.getAnnotation(AgeValidator.class);
            if (annotation != null) {
                if (annotation instanceof AgeValidator) {
                   AgeValidator ageValidator =  annotation;
                   if (val < ageValidator.min() || val > ageValidator.max()) {
                       throw new RuntimeException("年龄非法");
                   }
                }
            }
            method.invoke(o,20);
            System.out.println(o);
        }
    

    小结

      1. Class: 是一个类; 一个描述类的类.
          封装了描述方法的 Method,
        描述字段的 Filed,
        描述构造器的 Constructor 等属性.
      2. 如何得到 Class 对象:
          2.1 Person.class
          2.2 person.getClass()
          2.3 Class.forName("com.atguigu.javase.Person")
      3. 关于 Method:
          3.1 如何获取 Method:
            1). getDeclaredMethods: 得到 Method 的数组.
            2). getDeclaredMethod(String methondName, Class ... parameterTypes)
          3.2 如何调用 Method
            1). 如果方法时 private 修饰的, 需要先调用 Method 的 setAccessible(true), 使其变为可访问
            2). method.invoke(obj, Object ... args);
      4. 关于 Field:
          4.1 如何获取 Field: getField(String fieldName)
          4.2 如何获取 Field 的值:
            1). setAccessible(true)
            2). field.get(Object obj)
          4.3 如何设置 Field 的值:
            field.set(Obejct obj, Object val)
      5. 了解 Constructor 和 Annotation
      6. 反射和泛型.
          6.1 getGenericSuperClass: 获取带泛型参数的父类
          6.2 Type 的子接口: ParameterizedType
          6.3 可以调用 ParameterizedType 的 Type[] getActualTypeArguments() 获取泛型参数的数组.
     
  • 相关阅读:
    面试官:重写 equals 时为什么一定要重写 hashCode?
    MyBatis 中为什么不建议使用 where 1=1?
    面试官:方法重写时需要注意哪些问题?
    Java中List排序的3种方法
    面试官:this和super有什么区别?this能调用到父类吗?
    面试官:int和Integer有什么区别?为什么要有包装类?
    HashMap 中的一个“坑”!
    Java 中接口和抽象类的 7 大区别!
    List 去重的 6 种方法,这个方法最完美!
    面试官:如何实现 List 集合去重?
  • 原文地址:https://www.cnblogs.com/treeshu/p/11033074.html
Copyright © 2020-2023  润新知