首页 > 其他分享 >反射概述

反射概述

时间:2023-02-21 21:11:37浏览次数:47  
标签:反射 对象 System public 概述 clazz Class out

反射概述及动态代理

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

标签:反射,对象,System,public,概述,clazz,Class,out
From: https://www.cnblogs.com/msuenb/p/17142432.html

相关文章

  • 16 反射
    16反射16.1通过案例体会反射的好处16.2通过概念再体会反射16.3Class类的理解16.4提供丰富的类16.5获取字节码信息的四种形式16.6可以作为Class类的实例的种类......
  • redis-概述、redis下载&安装
    redis概述redis是一款高性能的NOSQL系列的非关系型数据库 什么是NOSQLNoSQL(NoSQL=NotOnlySQL),意即"不仅仅是SQL",是一项全新的数据库理念,泛指非关系型的数据......
  • Filter_案例2_过滤敏感词汇_实现与Listener_概述
    Filter_案例2_过滤敏感词汇_实现   packagehf.xueqiang.web.filter;importjavax.servlet.*;importjavax.servlet.annotation.Web......
  • 826~827 JQuery 概述、快速入门
    JQuery基础:1、概念:一个JavaScript框架。简化JS开发jQuery是一个快速、简洁的JavaScript框架,是继Prototype之后又一个优秀的JavaScript代码库(框架)于2006年1......
  • K8S概述
    一、什么是Kubernetes?Kubernetes是一个可移植、可扩展的开源平台,用于管理容器化工作负载和服务,有助于声明式配置和自动化。它拥有庞大且快速发展的生态系统。Kubernetes......
  • java基础 -- 反射深入浅出与CLass源码解析
    java反射在运行状态中,对于任意的一个类,都能够知道这个类的所有属性和方法,对任意一个对象都能够通过反射机制调用一个类的任意方法,这种动态获取类信息及动态调用类对象方法的......
  • 自然语言处理概述
    1  自然语言的特点线性:⾃然语⾔呈现为⼀种线性的符号序列。层次性:⾃然语⾔内部存在层次结构。歧义性:同⼀个⾃然语⾔句⼦存在多种不同的理解。演化性:⾃然语⾔随着时......
  • 泛型概述
    泛型概述Author:MsuenbDate:2023-02-20所谓泛型,就是允许在定义类、接口时通过一个标识表示类中某个属性的类型或者是某个方法的返回值及参数类型。这个类型参数将......
  • 项目一众筹网03_5_RBAC(权限管理)模型-概述
    系列文章目录文章目录​​系列文章目录​​​​18-RBAC模型-概述​​​​19-RBAC模型-多对多在数据库的表示​​​​20-RBAC模型-RBAC0~3​​​​21-RBAC模型-RBAC模型的数......
  • 00022.06 IO的概述
    系列文章目录文章目录​​系列文章目录​​​​一、IO是什么?​​​​二、IO的分类​​​​三、IO流有四大抽象的基类/超类/父类​​​​总结​​一、IO是什么?I:input,输入O:o......