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

    反射

    • 反射定义
    • java反射API
    • 使用反射获取类信息
    • 使用反射创建对象
    • 使用反射调用方法和操作成员变量
    • 代理模式

    反射概述

    主要指程序可以访问、控制和修改它本身状态或行为的一种能力。

    在CS领域,反射是指一类应用,它们能够自我描述和自控制。也就是说这类应用通过采用某种机制对自己行为的描述self-representation和检测examination,并能根据自身的行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。

    Java的反射机制

    允许动态发现和绑定类、方法、字段、以及其它由与语言产生的元素。

    反射是java被视为动态语言的关键。

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

    • 在运行时判断任意一个对象所属的类
    • 在运行时构造任意一个类的对象
    • 在运行时判断任意一个类所具有的成员变量和方法
    • 在运行时调用任意一个对象的方法。甚至可以调用private方法
    • 生成动态代理

    Java反射API

    java反射需要的类并不多,主要有java.lang.Class和java.lang.reflect包中的Filed、Constructor、Method、Array类。

    • Class类:Class类的实例表示正在运行的java应用程序中的类和接口
    • Field类:提供有关类或接口的属性的信息,以及对它的动态访问权限。反射的字段可能是一个类(静态)属性或实例属性,封装反射类的属性的类。
    • Constructor类:提供关于类的单个构造方法的信息以及对它的访问权限,封装了反射类的构造方法
    • Method类:提供关于类或接口上单独某个方法的信息。封装反射类方法的类
    • Array类:提供动态创建数组和访问数组的静态方法。所有方法都是静态方法。

    Class类是反射的起源,要反射一个类,必须先为它产生一个Class类的对象,才能通过Class对象获取其他想要的新。

    Class类

    java程序运行时,系统会对所有对象进行所谓的运行时类型标识,用于保存这些类型信息的类就是Class类。Class类封装一个对象和接口运行时的状态。

    、每种类型都有一个Class对象。需要创建某个类的实例时,JVM首先检查所要加载的类的Class对象是否已经存在。如果还不存在,JVM根据类名查找对应字节码文件并加载,接着创建对应的Class对象,最后才创建出这个类的实例。

    java基本数据类型(八种)和关键字void也都对应一个Class对象。每个数组属性也被映射为Class对象,相同类型和相同维数的数组都共享该Class对象。

    运行中的类或接口在JVM中都会有一个对应的Class对象存在,它保存了对应类和接口的类型信息。想要获取类和接口的对应信息,必须先获取这个Class对象。

    1. 获得Class对象

      三种方式获得Class对象:

      • 调用Object类的getClass()方法来得到Class对象,也是常见的方法

        MyObject x;
        Class c1 = x.getClass();
        
      • 使用Class类的forName()静态方法获得与字符串对应Class对象。

        Class c2 = Class.forName("java.lang.String");
        

        参数字符串必须是类或接口的全限定名

      • 使用类型名.class获取该类型的Class对象

        Class c11 = Manager.class;
        Class c12 = int.class;
        Class c13 = double[].class;
        
    2. 常用方法

      提供大量方法来获取所代表实体(类、接口、数组、枚举、注解、基本类型或void)的信息。

      常用方法:

    使用java反射

    使用java反射机制来获取类的详细信息、创建类的对象、访问属性值、调用类的方法等。

    获取类型信息

    先获取Class对象,再调用方法来获取信息。

    1. 获取指定类对应的Class对象

      Class clazz = Class.forName("java.util.ArrayList");

    2. 获取类的包名

      getPackage()方法获得一个java.lang.Package类的对象。

      通过Package类提供的方法可以访问相关的信息。

      String packageName = clazz.getPackage().getName(); //获取全名

    3. 获取类的修饰符

      int mod = clazz.getModifiers();//获取整数表示的类修饰符值

      把这个整数值转换到对应的字符串,使用java.lang.reflect.Modifier类提供的toString(int mod)静态方法

      String modifier = Modifier.toString(mod);

    4. 获取类的全限定名

      String className = clazz.getName();

    5. 获取类的父类

      Class superClazz = clazz.getSuperClass();

    6. 获取类实现的接口

      class[] interfaces = clazz.getInterfaces();

    7. 获取类的成员变量

      Class对象的getFields()方法获取到此Class对象所对应的实体的所有public字段(成员变量)。如果要获取所有的字段,可以使用getDeclareFields()方法

      Field[] fields = clazz.getDeclaredFields();

      Filed类用来代表字段的详细信息。通过调用Field类提供的相应方法就可以获取字段的修饰符、数据类型、字段名等信息。

      for (Filed field : fields) {
          String modifier = Modifer.toString(field.getModifiers());//访问修饰符
          Class type = filed.getType();//数据类型
          String name = field.getName(); //字段名
          if (type.isArray()) { //是数组类型需要特别处理
              String arrType = type.getComponentType().getName()+"[]";
             
          }
      }
      
    8. 获取类的构造方法

      Class对象的getConstructors方法获取到所有public的构造方法。getDeclaredConstructors()获取所有构造方法

      Constructor[] constructors = clazz.getDeclaredConstructors();

      Constructor类用来表示类的构造方法的相关信息。可以获得该构造方法的修饰符、构造方法名、参数列表等信息。

      for (Constructor constructor : constructros) {
      	String name = constructor.getName();//得到构造方法名
      	String modifier = Modifier.toString(constructor.getModifiers());
          Class[] paramTypes = constructor.getParameterTypes();//得到方法的参数列表
          for (int i = 0; i < paramTypes.length; i++) {
              if (i > 0) System.out.println(", ");
              if (paramTypes[i].isArray()) {
                  System.out.println(paramTypes[i].getComponentType().getName() + "[]"); 
              } else {
                  System.out.println(paramTypes[i].getName()); 
              }
          }
      }
      
    9. 获取类的成员方法

      Class对象的getMethods方法获取到所有public的构造方法。getDeclaredMethods()获取所有构造方法

      Methods[] methods = clazz.getDeclaredMethonds();

      Methond类用来代表类的成员方法的相关信息。通过调用Method类提供的响应方法也可以获得该成员方法的修饰符、返回值类型、方法名、参数列表等信息。

      for (Method method : methods) {
          String modifier = Modifier.toString(method.getModifiers());
          Class returnType = method.getReturnType();//返回类型
          if (returnTyper.isArray()) {
              System.out.println(returnType.getComponentType().getName()+"[]");
          } else {
              System.out.println(returnType.getName());
          }
          
          Class[] paramTypes = methods.getParameterTypes();
           for (int i = 0; i < paramTypes.length; i++) {
              if (i > 0) System.out.println(", ");
              if (paramTypes[i].isArray()) {
                  System.out.println(paramTypes[i].getComponentType().getName() + "[]"); 
              } else {
                  System.out.println(paramTypes[i].getName()); 
              }
          }
      }
      

    创建对象

    1. 使用无参构造方法

      调用Class对象的newInstance()方法。

      Class c = Class.forName("java.util.ArrayList");
      List list = (List) c.newInstance();
      

      指定的类没有无参构造方法会报NoSuchMethodException异常

      import java.util.Date
      
      public class NoArgsCreateInstanceTest {
      	public static void main(String[] args) {
      		Date currentDate = (Date)newInstance("java.util.Date");
      		System.out.println(currenDate);
      	}
      	
      	public static Object newInstance(String className) {
      		Object obj = null;
      		try {
      			obj = Class.forName(className).newInstance();
      		} catch (InstantiationException e) {
      			e.printStackTrace();
      		}
      		return obj;
      	}
      }
      
    2. 使用带参构造方法

      首先需要获得指定名称的Class对象,然后通过反射获取满足指定参数类型要求的构造方法信息类(java.lang.reflect.Constructor)对象,调用newInstance方法创建对象。

      • 第一步:获取指定类的Class对象
      • 第二步:通过Class对象获取满足指定参数类型要求的构造方法类对象。
      • 调用指定Constructor对象的newInstance方法传入对应的参数值,创建对象。
      package JavaIO;
      
      
      import java.lang.reflect.Constructor;
      import java.lang.reflect.InvocationTargetException;
      
      import java.util.Date;
      public class AgrsCreateInstance {
          @SuppressWarnings("unchecked")
          public static void main(String[] args) {
              try {
                  Class clazz = Class.forName("java.util.Date");
                  Constructor con = clazz.getConstructor(long.class);
                  Date date = (Date)con.newInstance(123456789L);
                  System.out.println(date);
              } catch (ClassNotFoundException e) {
                  e.printStackTrace();
              } catch (NoSuchMethodException e) {
                  e.printStackTrace();
              } catch (Exception e) {
                  e.printStackTrace();
              }
      
      
          }
      }
      
      

    调用方法

    使用反射可以获得指定类的指定方法的对象代表,方法的对象代表就是java.lang.reflect.Method类的实例,通过Method类的invoke方法可以动态调用这个方法。

    invoke()方法的第一个参数是对象类型,表示要再指定的这个对象上调用这个方法,第二个参数是一个可变参数,用来为这个方法传递参数值;invoke方法的返回值即为动态调用指定方法后的实际返回值。

    若要访问一个反射调用类的私有方法,矿可以在这个私有方法对应的Method对象上调用setAccessible(true)来取消java语言对本方法的访问检查,然后再调用invoke方法来真正执行这个私有方法。

    package JavaIO;
    
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    
    public class ReflectInvokeMethodTest {
        public static void main(String[] args) {
            try {
                Class clazz = Class.forName("JavaIO.Product");
                Product prod = (Product) clazz.newInstance();
                // 获取一个Prodcut类的方法名为setName,带有参数类型为String
                Method method1 = clazz.getDeclaredMethod("setName", String.class);
                //调用函数获取返回值
                Object returnValue = method1.invoke(prod, "爪哇");
    
                System.out.println("返回值:"+returnValue);
    
                //获取dispalyInfo的对象代表
                Method method2 = clazz.getDeclaredMethod("displayInfo");
                // 取消访问检查 允许访问私有方法 否则会报错
                method2.setAccessible(true);
                returnValue = method2.invoke(prod);
                System.out.println("返回值:"+returnValue);
    
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
    class Product{
        private static long count = 0;
        private long id;
        private String name = "NoBody";
        public Product() {
            System.out.println("默认构造方法");
            id = ++count;
        }
        public long getId() {return id;}
        public void setId(long id ) {this.id = id;}
        public String getName(){return  name;}
        public void setName(String name) {
            System.out.println("调用setName方法");
            this.name = name;
        }
        private void displayInfo(){ //私有方法
            System.out.println(getClass().getName() + "[ id="+id+",name="+name+" ]");
    
        }
    }
    

    访问成员变量的值

    使用反射可以取得类的成员变量的对象代表,成员变量的对象代表是java.lang.reflect.Field类的实例,可以使用getXXX方法来获取指定对象上的值,也可以调用它的setXXX方法来动态修改指定对象上的值,其中XXX表示成员变量的数据类型。

    package JavaIO;
    import java.lang.reflect.Field;
    public class ReflectFieldTest {
        @SupepressWarnings("unchecked")
        public static void main(String[] args) {
            try {
                Class c = Class.forName("JavaIO.Product");
                Product prod = (Product)c.newInstance();
                //调用私有属性
                Filed idField = c.getDeclaredField("id");
                idFiled.setAccessible(true); //取消对本字段的访问检查
                //设置prod对象的idField成员变量的值
                idFiled.setLong(prod, 100);
                //获取idField成员变量的值
                System.out.println("id="+idFiled.getLong(prod));
                
                Filed nameField = c.getDeclaredField("name");
                nameFiled.setAccessible(true); //取消对本字段的访问检查
                //设置prod对象的idField成员变量的值
                nameFiled.set(prod, "张三");
                //获取idField成员变量的值
                System.out.println("id="+nameFiled.get(prod));
            }
        }
    }
    

    操作数组

    数组也是一个对象,可以通过反射来查看数组的各个属性信息。

    short[] sArr = new short[5];
    String str = sArr.getClass().getComponentType().getName();
    

    数组通过反射动态创建,利用java.lang.reflect.Array类来操作

    import java.lang.reflect.Array
    
    Object obj = Array.newInstance(int.class, 5);
    for (int i = 0; i < 5; i++) {
        Array.setInt(obj, i, i * 10);
    }
    for (int i = 0; i < 5; i++) {
        System.out.println(Array.getInt(obj, i));
    }
    

    反射与动态代理

    静态代理

    public interface ClothingFactory { //服装厂接口
        void productClothing();//生产衣服接口
    }
    
    public class LiNingCompany implements ClothingFactory { //生产公司类
        public void productClothing() {
            System.out.println("生产出一批衣服");
        }
    }
    
    public ProxyCompany implements ClothingFactory { //中介代理类
        private ClothingFactory cf;
        public ProxyCompany(ClothingFactory cf){
            this.cf = cf;
        }
        public void productClothing() {
            System.out.println("收取10000中介费");
            cf.productClothing(); //委托真正的服务公司生产服装
        }
        
    }
    
    public class Customer {
        public static void main(String[] args) { //顾客类
            //找一家中介公司
            ClothingFactory cf = new ProxyCompany(new LiNingCompany());
            cf.productClothing();
        }
    }
    
    

    特征代理类和目标对象的类都已经在编译期间就已经确定下俩,不利于程序的扩展。

    需要新增的时候需要一一添加

    动态代理

    在程序运行时根据需要动态创建目标类的代理对象。

    java.lang.reflect包中提供了对动态代理的支持的类和接口。

    1. InvocationHandler接口:代理类的处理类都要实现这个接口,接口中只要一个方法

      public Object invoke(Object proxy, Method method, Object[] args) thorws Throwable;

      proxy指代理类,method是被代理的方法的CLass对象,第三个参数为args传给该方法的参数值数组。

    2. Proxy类:提供创建动态代理类和实例的静态方法。

      import java.lang.reflect.*;
      
      public class DynaProxyHandler implements InvocationHandler {
          private Object target; //目标对象
          public Object newProxyInstance(Object target) {
              this.target = target;
              return Proxy.newProxyInstance(this.target.getClass().getClassLoader(),
                                            this.target.getClass().getInterface(), 
                                            this
                                           )
          }
          public Object invoke(Object proxy, Method method, Object[] args) thorws Throwable {
              Object result = null;
              try {
                  result = method.invoke(this.target, args);
              } catch(Exception e){
                  throw e;
              }
              return result ;
          }
      }
      
      //客户端的代码修改
      public class Customer {
          public static void main(String[] args) { //顾客类
              //找一家中介公司
              DynaProxyHandler handler = new DynaProxyHandler();
              ClothingFactory cf2 = (ClothingFactory)handler.newProxyInstance(new LiNingCompany());
              cf2.productClothing();
          }
      }
      

      在运行时动态创建目标对象的代理对象。

  • 相关阅读:
    selenium+python+API分类总结
    Web自动化测试之六、Selenium Web控件交互
    五、Selenium元素定位的八种方法
    五、X-PATH定位元素
    五、css_selector定位总结
    pytest测试框架实战一
    python实战2
    pycharm 远程开发
    python 虚拟环境
    爬虫案例_网易云歌单
  • 原文地址:https://www.cnblogs.com/DengSchoo/p/12847214.html
Copyright © 2011-2022 走看看