zoukankan      html  css  js  c++  java
  • java反射

    JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取类信息以及动态调用对象的方法的功能称为java语言的反射机制。

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

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

    Class 是一个类; 一个描述类的类.

    封装了描述方法的 Method,

             描述字段的 Filed,
              描述构造器的 Constructor 等属性.
    eg:定义Person类、Student类、Student继承Person
    package reflect;
    
    public class Person {
        String name;
        private int age;
        private Integer sal;
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
            System.out.println("执行了setName()方法");
        }
        public int getAge() {
            return age;
        }
        public void setAge(int age) {
            this.age = age;
            System.out.println("执行了setAge()方法");
        }
        public Integer getSal() {
            return sal;
        }
        public void setSal(Integer sal) {
            this.sal = sal;
            System.out.println("执行了setAge()方法");
        }
        public Person(String name, int age, Integer sal) {
            super();
            this.name = name;
            this.age = age;
            this.sal = sal;
        }
        public Person() {
            super();
            
        }
        private void Say(){
            System.out.println("我好痛苦,我觉得生不如死!");
        }
        public void test(String name,Integer sal){
            System.out.println("调用成功");
            
        }
        
        
        
        
    }
    Person类
    package reflect;
    
    public class Student extends Person{
      private Double score;
    
    public Double getScore() {
        return score;
    }
    
    public void setScore(Double score) {
        this.score = score;
    }
      
    private void Study(){
        System.out.println("我太累了,不想学习");
    }
    }
    Student类

    1、如何描述方法-Method(getDeclaredMethod()不能获取父类方法   getMethod() 一般只能获取私有方法)

    @Test
       public void testMethod() throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{
            Class clazz=Class.forName("reflect.Person");
           //1、获取clazz对应类中的所有方法--方法数组1
            Method[] methods=clazz.getMethods();
            for(Method method:methods){
                System.out.println(method.getName());
            }
            
            System.out.println("-----------------------------------------------------");
            //2、获取所有方法包括私有方法---方法数组2
              methods=clazz.getDeclaredMethods();
              for(Method method:methods){
                    System.out.println(method.getName());
                }
             //3.1、获取指定的方法
              //需要参数名称和参数列表,无参则不需要
              //对于方法 public void setName(String name){}
              Method method=clazz.getDeclaredMethod("setName",String.class);
              System.out.println(method);
              //而对于方法public void setAge(int age){}
              method=clazz.getDeclaredMethod("setAge",int.class);
              System.out.println(method);
              
              method=clazz.getDeclaredMethod("setSal",Integer.class );
              System.out.println(method);
              //4、执行方法 invoke() 第一个参数表示执行哪个对象的方法,剩下的参数是执行方法时需要转入的参数
              Object obj=clazz.newInstance();
              method.invoke(obj, 2000);
              //如果一个方法是私有方法,3.1是可以获取到的,但是这一步却不能执行
              //私有方法的执行,必须在调用invoke之前加上一句method.setAccessible(true)
              method=clazz.getDeclaredMethod("Say",null);
              method.setAccessible(true);
              method.invoke(obj, null);
              
       }

    主要用到的两个方法

    /**
             * @param name the name of the method
             * @param parameterTypes the list of parameters
             * @return the {@code Method} object that matches the specified
             */
            public Method getMethod(String name, Class<?>... parameterTypes){
                
            }
            
            /**
             * @param obj  the object the underlying method is invoked from
             * @param args the arguments used for the method call
             * @return  the result of dispatching the method represented by
             */
            public Object invoke(Object obj, Object... args){
                
            }

    自定义工具方法

      自定义一个方法

                 把类对象和类方法名作为参数,执行方法

                 把全类名和方法名作为参数,执行方法

    假设:Person类里面有一个方法

    public void test(String name,Integer sal){
            System.out.println("调用成功");
        }

    1. 把类对象和类方法名作为参数,执行方法(下面的方法没有考虑到无参的方法)

        /**
         * @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方法
          //返回方法返回值
        return  method.invoke(obj, args);
      }
    @Test
      public void testInvoke() throws Exception{
          Object obj=new Person();
          invoke(obj,"test","李刚",20000);
      }

     2.把全类名和方法名作为参数,执行方法(没有考考虑无参的方法)

    public Object invoke(String className,String methodName,Object...args){
        Object obj=null;
          try{
              obj=Class.forName(className).newInstance();
              return invoke(obj,methodName,args);
          }catch(Exception e){
              e.printStackTrace();
          }
          return null;
      }

      这种反射实现的主要功能是可配置和低耦合。只需要类名和方法名,而不需要一个类对象就可以执行一个方法。如果我们把全类名和方法名放在一个配置文件中,就可以根据调用配置文件来执行方法

    如何获取父类定义的(私有)方法

    前面说一般使用getDeclaredMethod获取方法(因为此方法可以获取类的私有方法,但是不能获取父类方法)

    如何获取父类方法呢,getMethod

    首先我们要知道,如何获取类的父亲:

      比如有一个类,继承自Person

    @Test
      public void testGetSuperClass() throws Exception{
        String className="reflect.Student";
        Class clazz=Class.forName(className);
        Class superClazz=clazz.getSuperclass();
        System.out.println(superClazz);//class reflect.Person
      }

    此时如果Student中有一个方法是私有方法method1(int age); Person中有一个私有方法method2();
      怎么调用 定义一个方法,不但能访问当前类的私有方法,还要能父类的私有方法

    /**
       * 
       * @param obj: 某个类的一个对象
       * @param methodName: 类的一个方法的方法名. 
       * 该方法也可能是私有方法, 还可能是该方法在父类中定义的(私有)方法
       * @param args: 调用该方法需要传入的参数
       * @return: 调用方法后的返回值*/
      public Object invoke2(Object obj,String methodName,Object... args){
          Class[] parameterTypes=new Class[args.length];
          for(int i = 0; i < args.length; i++){
              parameterTypes[i] = args[i].getClass();
          }
          try{
              Method method=getMethod(obj.getClass(),methodName,parameterTypes);
              method.setAccessible(true);
              return method.invoke(obj, args);
          }catch(Exception e){
              e.printStackTrace();
          }
          return null;
      }
      /**
       * 获取 clazz 的 methodName 方法. 该方法可能是私有方法, 还可能在父类中(私有方法)
       * 如果在该类中找不到此方法,就向他的父类找,一直到Object类为止
     * 这个方法的另一个作用是根据一个类名,一个方法名,追踪到并获得此方法*/
      public Method getMethod(Class clazz,String methodName,Class ...parameterTypes){
          for(;clazz!=Object.class;clazz=clazz.getSuperclass()){
              try{
                  Method method=clazz.getDeclaredMethod(methodName, parameterTypes);
                  return method;
              }catch(Exception e){
                  
              }
          }
          return null;
      }

    3.2 如何描述字段-Field 

    @Test
         public void testField() throws ClassNotFoundException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException{
             String className="reflect.Person";
             Class clazz=Class.forName(className);
             //1、获取字段
             //获取所有字段,字段数组
             //可以获取公用和私有的所有字段,但不能获取父类字段
             Field[] fields=clazz.getDeclaredFields();
             for(Field field:fields){
                 System.out.println(field);
             }
             Person person=new Person();
             person.setName("郭维平");
             person.setSal(6000);
            //1.2获取指定字段
                Field field=clazz.getDeclaredField("name");
                System.out.println("-----字段名称-------"+field.getName());
             
             //2、使用字段
                //2.1获取指定对象的指定字段的值
             Object val=field.get(person);
             System.out.println(val);
             field.set(person, "周益涛");
             System.out.println(person.getName());
            
             field=clazz.getDeclaredField("age");
             field.setAccessible(true);//如果字段是私有的,不管是读值还是写值,都必须先调用setAccessible(true);比如Person类中,字段name字段是公用的,age是私有的
             System.out.println("--------age---------"+field.get(person));
             
             
         }

     输出结果:

    java.lang.String com.idea.test.Person.name
    private int com.idea.test.Person.age
    private java.lang.Integer com.idea.test.Person.sal
    执行了setName()方法
    执行了setAge()方法
    -----字段名称-------name
    郭维平
    周益涛
    --------age---------0

    但是如果需要访问父类中的(私有)字段:(好像有点问题需注意)

    @Test
         public void testField() throws ClassNotFoundException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, InstantiationException{
            //创建className对应类的对象,并为其fieldName赋值val
            //Student继承自Person,age是Person类的私有字段
            String className="reflect.Student";
            String fieldName="age";
            Object val=26;
            Object obj=null;
            //1、创建className对应类的对象
            Class clazz=Class.forName(className);
            //2、创建fieldName对象字段的对象
            Field field=getField(clazz,fieldName);
            //3、为此对象赋值
            obj=clazz.newInstance();
            setFieldValue(obj,field,val);
            //4、获取此对象的值
            Object value=getFieldValue(obj,field);
            
         }
        
        public Object getFieldValue(Object obj,Field field) throws IllegalArgumentException, IllegalAccessException{
            field.setAccessible(true);
            return field.get(obj);
            
        }
        
        public void setFieldValue(Object obj,Field field,Object val) throws IllegalArgumentException, IllegalAccessException{
            field.setAccessible(true);
            field.set(obj, val);
            
        }
        
        public Field getField(Class clazz,String fieldName) throws NoSuchFieldException, SecurityException{
            Field field= null;
            for(Class clazz2=clazz;clazz2!=Object.class;clazz2=clazz2.getSuperclass()){
                field=clazz2.getDeclaredField(fieldName);
            }
            return field;
        }

     3.3如何描述构造器-Constructor

    @Test
        public void testConstructor() throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{
            String className="reflect.Person";
            Class<Person>  clazz=(Class<Person>)Class.forName(className);
            //1、获取Constructor对象
            //1.1获取全部
            Constructor<Person>[] constructors=(Constructor<Person>[])clazz.getConstructors();
            for(Constructor constructor:constructors){
                System.out.println(constructor);
            }
            //  //  1.2获取某一个,需要参数列表
            Constructor<Person> constructor=clazz.getConstructor(String.class,int.class,Integer.class);
            Object obj=constructor.newInstance("周益涛",18,10000);
            System.out.println("-----"+obj.toString());
            
        }

    3.4 如何描述注解 -- Annotation

    package reflect;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    @Retention(RetentionPolicy.RUNTIME)
    @Target(value={ElementType.METHOD})
    public @interface AgeValidator {
        public int min();
        public int max();
    
    }
    定义一个Annotation

    此注解只能用在方法上

    @AgeValidator(min=12,max=30)
        public void setAge(int age) {
            this.age = age;
            System.out.println("执行了setAge()方法");
        }

    那么我们在给Person类对象的age赋值时,是感觉不到注解的存在的

    @Test
    public void testAnnotation(){
          Person p=new Person();
          p.setAge(15);
      }
      

    必须通过反射的方式为属性赋值,才能获取到注解

     @Test
      public void testAnnotation1() throws InstantiationException, IllegalAccessException, ClassNotFoundException, NoSuchMethodException, SecurityException{
          String className="reflect.Person";
          Class clazz=Class.forName(className);
          Object obj=clazz.newInstance();
          Method method=clazz.getDeclaredMethod("setAge",int.class);
         int val=6;
         //获取指定名称的注解
         Annotation annotation=method.getAnnotation(AgeValidator.class);
         if(annotation!=null){
             if(annotation instanceof AgeValidator){
                 AgeValidator ageValidator=(AgeValidator)annotation;
                 if(val<ageValidator.min() ||val>ageValidator.max()){
                     throw new RuntimeException("年龄非法");
                 }
             }
         }
      }

    反射小结

     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: 获取带泛型参数的父类, 返回值为: BaseDao<Employee, String>
        6.2 Type 的子接口: ParameterizedType
        6.3 可以调用 ParameterizedType 的 Type[] getActualTypeArguments() 获取泛型参数的数组

     

  • 相关阅读:
    编程模式
    第六章类(十九)readonly
    Javascript----实现鼠标背景效果(同时不影响其操作)
    Javascript----input事件实现动态监听textarea内容变化
    javascript----mouseover和mouseenter的区别
    Javascript----实现火箭按钮网页置顶
    Javascript----scroll事件进度条监听
    Javascript----生成省-市下拉表单
    Javascript----增删改查
    javascript-----轮播图插件
  • 原文地址:https://www.cnblogs.com/zouhong/p/12104254.html
Copyright © 2011-2022 走看看