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();
        }
    }
    

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

原文地址:https://www.cnblogs.com/DengSchoo/p/12847214.html