反射概述

反射概述及动态代理

Author: Msuenb

Date: 2023-02-21


Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。

加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象包含着完整的类的结构信息,可以通过这个对象获取类的结构。

java.lang.Class类

要想获取一个类的结构,需要先获取到该类的Class对象,Class对象是反射的根源。

Java中所有类型都可以获取Class对象:

// 1.基本数据类型和void
Class<Integer> integerClass = int.class;
Class<Void> voidClass = void.class;
// 2.类和接口
Class<String> stringClass = String.class;
Class<Comparable> comparableClass = Comparable.class;
// 3.枚举和注解
Class<ElementType> elementTypeClass = ElementType.class;
Class<Override> overrideClass = Override.class;
// 4.数组
Class<String[]> aClass = String[].class;

获取Class对象的四种方式:

  • 类型名.class:要求编译期间已知类型
  • 对象.getClass():获取对象的运行时类型
  • Class.forName(全类名):可以获取编译期间未知的类型
  • ClassLoader.getSystemClassLoader().loadClass(全类名):可以用系统类加载对象加载指定路径下的类型
package demo;
public class GetClassObjTest {
    public static void main(String[] args) throws ClassNotFoundException {
        Class<GetClassObjTest> c1 = GetClassObjTest.class;
        GetClassObjTest obj = new GetClassObjTest();
        Class<? extends GetClassObjTest> c2 = obj.getClass();
        Class<?> c3 = Class.forName("demo.GetClassObjTest");
        Class<?> c4 = ClassLoader.getSystemClassLoader().loadClass("demo.GetClassObjTest");
    }
}

反射的基本应用

Java反射机制提供的功能:

  • 在运行时判断任意一个对象所属的类
  • 在运行时构造任意一个类的对象
  • 在运行时调用任意一个对象的成员变量和方法
  • 在运行时获取泛型,注解,修饰符等信息
  • 生成动态代理

反射相关的主要类:

  • java.lang.Class:代表一个类
  • java.lang.reflect.Constructor:代表类的构造器
  • java.lang.reflect.Field:代表类的成员变量
  • java.lang.reflect.Method:代表类的方法
  • java.lang.reflect.Array:Array对象可以代表所有的数组

可以通过这些反射相关类提供的API获取:包、修饰符、类型名、父类、父接口、成员(属性、构造器、方法、内部类),注解等信息

示例代码:获取类的常规信息

package demo;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

public class GetClassInfoTest {
    public static void main(String[] args) {
        // 先得到某个类型的Class对象
        Class<String> clazz = String.class;

        // 1.获取包对象
        Package pkg = clazz.getPackage();
        System.out.println("包名:" + pkg);

        // 2.获取修饰符
        int mod = clazz.getModifiers();
        System.out.println("类的修饰符有:" + Modifier.toString(mod));

        // 3.获取类型名
        String name = clazz.getName();
        System.out.println("类名:" + name);

        // 4.获取父类对应的Class对象
        Class<? super String> superclass = clazz.getSuperclass();
        System.out.println("父类:" + superclass);

        // 5.获取父接口们
        Class<?>[] interfaces = clazz.getInterfaces();
        System.out.print("父接口:");
        for (Class<?> anInterface : interfaces) {
            System.out.print(anInterface + ", ");
        }

        // 6.获取类的属性
        System.out.println("\n----------属性----------");
        Field[] fields = clazz.getFields();
        for (Field field : fields) {
            // 修饰符 数据类型 属性名
            int modifiers = field.getModifiers();
            System.out.println("属性的修饰符:" + Modifier.toString(modifiers));

            String name1 = field.getName();
            System.out.println("属性名:" + name1);

            Class<?> type = field.getType();
            System.out.println("数据类型:" + type);
        }

        System.out.println("----------构造器----------");
        Constructor<?>[] constructors = clazz.getDeclaredConstructors();
        for (int i = 0; i < constructors.length; i++) {
            Constructor<?> constructor = constructors[i];
            System.out.println("第" + (i+1) + "个构造器:");
            // 修饰符 构造器名称 构造器形参列表 抛出异常列表
            int modifiers = constructor.getModifiers();
            System.out.print("构造器修饰符:" + Modifier.toString(modifiers) + "\n");

            String name1 = constructor.getName();
            System.out.print("构造器名:" + name1 + "\n");

            Class<?>[] parameterTypes = constructor.getParameterTypes();
            System.out.print("形参列表:");
            for (Class<?> parameterType : parameterTypes) {
                System.out.print(parameterType + ", ");
            }

            System.out.print("\n异常列表:");
            Class<?>[] exceptionTypes = constructor.getExceptionTypes();
            for (Class<?> exceptionType : exceptionTypes) {
                System.out.print(exceptionType + ", ");
            }
            System.out.println();

            Class<?>[] inners = clazz.getDeclaredClasses();
            for (Class<?> inner : inners) {
                System.out.println("内部类:" + inner);
            }
        }
    }
}

创建运行时类的对象

  • 方式1:直接通过 Class 对象来实例化(被实例化类必须要公共的无参构造)

  • 方式2:通过获取构造器对象进行实例化

    如果构造器的权限修饰符的范围不可见,可以调用 setAccessible(true)

示例代码:

package demo;

import org.junit.jupiter.api.Test;

import java.lang.reflect.Constructor;

public class CreateObjTest {
    @Test
    public void test01() throws Exception {
        // 1.获取该类的 Class对象
        Class<?> clazz = Class.forName("demo.Person");
        // 2.创建对象
        Object o = clazz.newInstance();
        System.out.println(o);
    }

    @Test
    public void test02() throws Exception{
        // 1.获取该类的Class对象
        Class<?> clazz = Class.forName("demo.Person");
        // 2.获取构造器对象 有参
        Constructor<?> constructor = clazz.getDeclaredConstructor(String.class, int.class);
        // 3.创建实例对象
        Object o = constructor.newInstance("Tom", 19);
        System.out.println(o);
    }
}

class Person {
    private String name;
    private int age;

    public Person() {}

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

还可以通过反射的方式创建任意类型的数组对象:

import java.lang.reflect.Array;

public class ArrayObjTest {
    public static void main(String[] args) {
        Class<String> clazz = String.class;

        Object arr = Array.newInstance(clazz, 5);

        System.out.println("数组类型:" + arr.getClass());
        Array.set(arr, 2, "tom");
        System.out.println(Array.get(arr, 2));
    }
}

访问运行时类的属性

  1. 获取该类型的Class对象:Class clazz = Class.forName("包.类名");

  2. 获取属性对象:Field field = clazz.getDeclaredField("属性名");

    如果属性的权限修饰符不是public,那么需要设置属性可访问field.setAccessible(true);

  3. 创建实例对象:如果操作的是非静态属性,需要创建实例对象

  4. 设置属性值:field.set(obj,"属性值"); 如果操作静态变量,那么实例对象可以省略,用null表示

  5. 获取属性值:Object value = field.get(obj); 如果操作静态变量,那么实例对象可以省略,用null表示

package demo;

import java.lang.reflect.Field;

public class FieldTest {
    public static void main(String[] args) throws Exception{
        // 1. 获取Student的Class对象
        Class<?> clazz = Class.forName("demo.Student");

        // 2. 获取属性对象
        Field idField = clazz.getDeclaredField("id");
        Field jobField = clazz.getDeclaredField("job");

        // 3. 设置可访问
        idField.setAccessible(true);

        // 4. 创建Student实例对象
        Object stu = clazz.newInstance();

        // 5. 获取属性值
        Object id = idField.get(stu);
        Object job = jobField.get(null);    // 获取静态属性值
        System.out.println("id = " + id + ", job = " + job);
        
        // 6. 设置属性值
        idField.set(stu, 181040211);
        jobField.set(null, "student");  // 设置静态属性值
        System.out.println("id = " + idField.get(stu) + ", job = " + jobField.get(null));

    }
}
class Student {
    private int id;
    private String name;
    public static String job = "学生"; 

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public static String getJob() {
        return job;
    }

    public static void setJob(String job) {
        Student.job = job;
    }
}

调用运行时类的方法

  1. 获取该类型的Class对象:Class clazz = Class.forName("包.类名");

  2. 获取方法对象:Method method = clazz.getDeclaredMethod("方法名",形参类型列表);

  3. 创建实例对象:Object obj = clazz.newInstance();

  4. 调用方法:Object result = method.invoke(obj, 实参列表);

如果方法的权限修饰符修饰的范围不可见,也可以调用setAccessible(true)
如果方法是静态方法,实例对象也可以省略,用null代替

package demo;

import java.lang.reflect.Method;

public class MethodTest {
    public static void main(String[] args) throws Exception{
        // 1. 获取Student类的对象
        Class<?> clazz = Class.forName("demo.Student");

        // 2. 获取方法对象
        Method setNameMethod = clazz.getDeclaredMethod("setName", String.class);

        // 3. 创建对象实例
        Object stu = clazz.newInstance();

        // 4. 调用方法
        setNameMethod.invoke(stu, "jerry"); // 返回值为null  因为setName没有返回值

        Method getNameMethod = clazz.getDeclaredMethod("getName");
        Object stuName = getNameMethod.invoke(stu);
        System.out.println(stuName);

        // 调用静态方法
        Method getJob = clazz.getDeclaredMethod("getJob");
        Object job = getJob.invoke(null);
        System.out.println(job);
    }
}

动态代理

代理设计模式的原理: 使用一个代理将对象包装起来, 然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。

代理模式分静态代理和动态代理。

  • 静态代理:特征是代理类和目标对象的类都是在编译期间确定下来,不利于程序的扩展。同时,每一个代 理类只能为一个接口服务,这样一来程序开发中必然产生过多的代理。最好可以通过一个代理类完成全部的代理功能。

  • 动态代理:动态代理是指客户通过代理类来调用其它对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象。

动态代理使用场合: 1. 调试 2. 远程方法调用

动态代理相比于静态代理的优点: 抽象角色中(接口)声明的所有方法都被转移到调用处理器一个集中的方法中处理,这样就可以更加灵活和统一的处理众多的方法。

Java动态代理相关API:

Proxy:专门完成代理的操作类,是所有动态代理类的父类。通过此类为一 个或多个接口动态地生成实现类。

提供用于创建动态代理类和动态代理对象的静态方法:

  • static Class getProxyClass(ClassLoader loader, Class... interfaces) :创建 一个动态代理类所对应的Class对象
  • static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) :创建一个动态代理对象

动态代理步骤:

  1. 创建一个实现 InvocationHandler 接口的类,重写 invoke 方法,以完成代理的具体操作

    package demo.proxy_;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    public class MyInvocationHandler implements InvocationHandler {
        private Object target = null;
    
        // 目标对象是活动的 不是固定的
        public MyInvocationHandler(Object target) {
            this.target = target;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("before...");
            Object retVal = method.invoke(target, args);
            System.out.println("after...");
            return retVal;
        }
    }
    
  2. 创建被代理的接及该接口实现类

    package demo.proxy_;
    
    public interface Calculate {
        int add(int x, int y);
    }
    
    package demo.proxy_;
    
    public class Calculator implements Calculate{
        @Override
        public int add(int x, int y) {
            System.out.println(x + " + " + y + " = " + (x + y));
            return x + y;
        }
    }
    
  3. 通过 Proxy 的 newProxyInstance 静态方法,创建一个接口代理

  4. 通过创建的接口代理调用接口实现类的方法

    package demo.proxy_;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Proxy;
    
    public class ProxyTest {
        public static void main(String[] args) {
            // 1. 创建目标对象
            Calculator calculator = new Calculator();
            // 2. 创建InvocationHandler对象
            InvocationHandler invocationHandler = new MyInvocationHandler(calculator);
    
            // 3. 创建代理对象
            Calculate proxy = (Calculate) Proxy.newProxyInstance(calculator.getClass().getClassLoader(), calculator.getClass().getInterfaces(), invocationHandler);
            // 4. 通过代理对象执行Calculate的方法
            proxy.add(2, 7);
        }
    }
    
posted @ 2023-02-21 21:02  msuenb  阅读(24)  评论(0编辑  收藏  举报