1.1. 反射的概念

反射最大的作用之一就在于我们可以不在编译时知道某个对象的类型,而在运行时通过提供完整的”包名+类名.class”得到。注意:不是在编译时,而是在运行时。

反射之所以被称为框架的灵魂,主要是因为它赋予了我们在运行时分析类以及执行类中方法的能力。

通过反射你可以获取任意一个类的所有属性和方法,你还可以调用这些方法和属性。

1.2. 反射机制的功能

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

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

1.3. 实现反射机制的类

Java中主要由以下的类来实现Java反射机制(这些类都位于java.lang.reflect包中):

  • Class类:代表一个类。 Field类:代表类的成员变量(成员变量也称为类的属性)。
  • Method类:代表类的方法。
  • Constructor类:代表类的构造方法
  • Array类:提供了动态创建数组,以及访问数组的元素的静态方法。

1.4. 动态代理

首先,它是一个代理机制,如果熟悉设计模式中的代理模式,我们会知道,代理可以看作是对调用目标的一个包装,这样我们对目标代码的调用不是直接发生的,而是通过代理完成。其实很多动态代理场景,我认为也可以看作是装饰器(Decorator)模式的应用。

通过代理可以让调用者与实现者之间解耦。比如进行 RPC 调用,框架内部的寻址、序列化、反序列化等,对于调用者往往是没有太大意义的,通过代理,可以提供更加友善的界面。

下面是JDK动态代理的一个简单实例,在生产系统中,我们可以轻松扩展类似逻辑进行诊断、限流等。

定义一个接口

public interface Hello {

    void sayHello();
}

接口实现

public class HelloImpl implements Hello{
    @Override
    public void sayHello() {
        System.out.println("hello world");
    }
}

定义一个代理处理器

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class MyInvocationHandler implements InvocationHandler {

    private Object target;

    public MyInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println("这里可以做限流、打日志等操作");

        Object invoke = method.invoke(target, args);
        return invoke;
    }
}

调用, 这里我们不是直接调用 Hello 的方法,而是通过代理解耦的去调用,在代理中可以做一些其他操作,比如日志打点、限流、诊断等操作。

import java.lang.reflect.Proxy;

public class MyDynamicProxy {

    public static void main(String[] args) {

        HelloImpl hello = new HelloImpl();

        MyInvocationHandler handler = new MyInvocationHandler(hello);

        Hello proxyHello = (Hello) Proxy.newProxyInstance(HelloImpl.class.getClassLoader(), HelloImpl.class.getInterfaces(), handler);

        proxyHello.sayHello();
    }
}

1.5. 反射常用API

下面这个类用来演示Reflection API的基本使用方法。这里自定义的copy方法是用来创建一个和参数objcet同样类型的对象,然后把object对象中的所有属性拷贝到新建的对象中,并将其返回。


public class ReflectTester {

    public Object copy(Object object) throws Exception {
        Class classType = object.getClass();
        //构造一个新的对象
        Object objectCopy=classType.getConstructor(new Class[]{}).newInstance(new Object[]{});

        //获取对象的所有属性
        Field[] declaredFields = classType.getDeclaredFields();

        for (Field field: declaredFields) {
            String name = field.getName();

            String firstLetter = name.substring(0,1).toUpperCase();

            //获得和属性对应的getXXX()方法的名字
            String getMethodName = "get"+firstLetter+name.substring(1);

            //获得和属性对应的setXXX()方法的名字
            String setMethodName="set"+firstLetter+name.substring(1);

            //获得和属性对应的getXXX()方法
            Method getMethod = classType.getMethod(getMethodName, new Class[]{});

            //获得和属性对应的setXXX()方法
            Method setMethod = classType.getMethod(setMethodName,new Class[]{field.getType()});

            //调用原对象的getXXX()方法
            Object value = getMethod.invoke(object, new Object[]{});

            System.out.println(name+":"+value);

            //调用拷贝对象的setXXX()方法
            setMethod.invoke(objectCopy,new Object[]{value});
        }
        return objectCopy;
    }

    public static void main(String[] args) throws Exception {
        Student student = new Student();
        student.setName("张三");

        Object copy = new ReflectTester().copy(req);
        System.out.println(copy);

    }
}

获取反射中的Class对象

  • 第一种,使用 Class.forName 静态方法。当你知道该类的全路径名时,你可以使用该方法获取 Class 类对象。
Class clz = Class.forName("java.lang.String");
  • 第二种,使用 .class 方法。
Class clz = String.class;
  • 第三种,使用类对象的 getClass() 方法。
String str = new String("Hello");
Class clz = str.getClass();

通过反射创建类对象

第一种:通过 Class 对象的 newInstance() 方法。

Class clz = Student.class;
Student student = (Student)clz.newInstance();

第二种:通过 Constructor 对象的 newInstance() 方法

Class clz = Student.class;
Constructor constructor = clz.getConstructor();
Student apple = (Student)constructor.newInstance();

通过 Constructor 对象创建类对象可以选择特定构造方法,而通过 Class 对象则只能使用默认的无参数构造方法。下面的代码就调用了一个有参数的构造方法进行了类对象的初始化。

Class clz = Student.class;
Constructor constructor = clz.getConstructor(String.class, int.class);
Student student = (Student)constructor.newInstance("张三", 15);

通过反射获取类属性、方法、构造器

我们通过 Class 对象的 getFields() 方法可以获取 Class 类的属性,但无法获取私有属性。

Class clz = Student.class;
Field[] fields = clz.getFields();
for (Field field : fields) {
    System.out.println(field.getName());
}
© gaohueric all right reserved,powered by Gitbook文件修订时间: 2021-12-08 23:22:22

results matching ""

    No results matching ""